You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
14332 lines
660 KiB
14332 lines
660 KiB
/*! |
|
FullCalendar v5.5.1 |
|
Docs & License: https://fullcalendar.io/ |
|
(c) 2020 Adam Shaw |
|
*/ |
|
var FullCalendar = (function (exports) { |
|
'use strict'; |
|
|
|
/*! ***************************************************************************** |
|
Copyright (c) Microsoft Corporation. |
|
|
|
Permission to use, copy, modify, and/or distribute this software for any |
|
purpose with or without fee is hereby granted. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH |
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, |
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM |
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR |
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
|
PERFORMANCE OF THIS SOFTWARE. |
|
***************************************************************************** */ |
|
/* global Reflect, Promise */ |
|
|
|
var extendStatics = function(d, b) { |
|
extendStatics = Object.setPrototypeOf || |
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || |
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; |
|
return extendStatics(d, b); |
|
}; |
|
|
|
function __extends(d, b) { |
|
extendStatics(d, b); |
|
function __() { this.constructor = d; } |
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); |
|
} |
|
|
|
var __assign = function() { |
|
__assign = Object.assign || function __assign(t) { |
|
for (var s, i = 1, n = arguments.length; i < n; i++) { |
|
s = arguments[i]; |
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; |
|
} |
|
return t; |
|
}; |
|
return __assign.apply(this, arguments); |
|
}; |
|
|
|
function __spreadArrays() { |
|
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; |
|
for (var r = Array(s), k = 0, i = 0; i < il; i++) |
|
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) |
|
r[k] = a[j]; |
|
return r; |
|
} |
|
|
|
var n,u,i,t,o,r,f={},e=[],c=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function s(n,l){for(var u in l)n[u]=l[u];return n}function a(n){var l=n.parentNode;l&&l.removeChild(n);}function v(n,l,u){var i,t,o,r=arguments,f={};for(o in l)"key"==o?i=l[o]:"ref"==o?t=l[o]:f[o]=l[o];if(arguments.length>3)for(u=[u],o=3;o<arguments.length;o++)u.push(r[o]);if(null!=u&&(f.children=u),"function"==typeof n&&null!=n.defaultProps)for(o in n.defaultProps)void 0===f[o]&&(f[o]=n.defaultProps[o]);return h(n,f,i,t,null)}function h(l,u,i,t,o){var r={type:l,props:u,key:i,ref:t,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==o?++n.__v:o};return null!=n.vnode&&n.vnode(r),r}function y(){return {current:null}}function p(n){return n.children}function d(n,l){this.props=n,this.context=l;}function _(n,l){if(null==l)return n.__?_(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?_(n):null}function w(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 w(n)}}function k(l){(!l.__d&&(l.__d=!0)&&u.push(l)&&!g.__r++||t!==n.debounceRendering)&&((t=n.debounceRendering)||i)(g);}function g(){for(var n;g.__r=u.length;)n=u.sort(function(n,l){return n.__v.__b-l.__v.__b}),u=[],n.some(function(n){var l,u,i,t,o,r,f;n.__d&&(r=(o=(l=n).__v).__e,(f=l.__P)&&(u=[],(i=s({},o)).__v=o.__v+1,t=$(f,o,i,l.__n,void 0!==f.ownerSVGElement,null!=o.__h?[r]:null,u,null==r?_(o):r,o.__h),j(u,o),t!=r&&w(o)));});}function m(n,l,u,i,t,o,r,c,s,v){var y,d,w,k,g,m,b,A=i&&i.__k||e,P=A.length;for(s==f&&(s=null!=r?r[0]:P?_(i,0):null),u.__k=[],y=0;y<l.length;y++)if(null!=(k=u.__k[y]=null==(k=l[y])||"boolean"==typeof k?null:"string"==typeof k||"number"==typeof k?h(null,k,null,null,k):Array.isArray(k)?h(p,{children:k},null,null,null):null!=k.__e||null!=k.__c?h(k.type,k.props,k.key,null,k.__v):k)){if(k.__=u,k.__b=u.__b+1,null===(w=A[y])||w&&k.key==w.key&&k.type===w.type)A[y]=void 0;else for(d=0;d<P;d++){if((w=A[d])&&k.key==w.key&&k.type===w.type){A[d]=void 0;break}w=null;}g=$(n,k,w=w||f,t,o,r,c,s,v),(d=k.ref)&&w.ref!=d&&(b||(b=[]),w.ref&&b.push(w.ref,null,k),b.push(d,k.__c||g,k)),null!=g?(null==m&&(m=g),s=x(n,k,w,A,r,g,s),v||"option"!=u.type?"function"==typeof u.type&&(u.__d=s):n.value=""):s&&w.__e==s&&s.parentNode!=n&&(s=_(w));}if(u.__e=m,null!=r&&"function"!=typeof u.type)for(y=r.length;y--;)null!=r[y]&&a(r[y]);for(y=P;y--;)null!=A[y]&&L(A[y],A[y]);if(b)for(y=0;y<b.length;y++)I(b[y],b[++y],b[++y]);}function x(n,l,u,i,t,o,r){var f,e,c;if(void 0!==l.__d)f=l.__d,l.__d=void 0;else if(t==u||o!=r||null==o.parentNode)n:if(null==r||r.parentNode!==n)n.appendChild(o),f=null;else {for(e=r,c=0;(e=e.nextSibling)&&c<i.length;c+=2)if(e==o)break n;n.insertBefore(o,r),f=r;}return void 0!==f?f:o.nextSibling}function A(n,l,u,i,t){var o;for(o in u)"children"===o||"key"===o||o in l||C(n,o,null,u[o],i);for(o in l)t&&"function"!=typeof l[o]||"children"===o||"key"===o||"value"===o||"checked"===o||u[o]===l[o]||C(n,o,l[o],u[o],i);}function P(n,l,u){"-"===l[0]?n.setProperty(l,u):n[l]=null==u?"":"number"!=typeof u||c.test(l)?u:u+"px";}function C(n,l,u,i,t){var o,r,f;if(t&&"className"==l&&(l="class"),"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||P(n.style,l,"");if(u)for(l in u)i&&u[l]===i[l]||P(n.style,l,u[l]);}else "o"===l[0]&&"n"===l[1]?(o=l!==(l=l.replace(/Capture$/,"")),(r=l.toLowerCase())in n&&(l=r),l=l.slice(2),n.l||(n.l={}),n.l[l+o]=u,f=o?N:z,u?i||n.addEventListener(l,f,o):n.removeEventListener(l,f,o)):"list"!==l&&"tagName"!==l&&"form"!==l&&"type"!==l&&"size"!==l&&"download"!==l&&"href"!==l&&!t&&l in n?n[l]=null==u?"":u:"function"!=typeof u&&"dangerouslySetInnerHTML"!==l&&(l!==(l=l.replace(/xlink:?/,""))?null==u||!1===u?n.removeAttributeNS("http://www.w3.org/1999/xlink",l.toLowerCase()):n.setAttributeNS("http://www.w3.org/1999/xlink",l.toLowerCase(),u):null==u||!1===u&&!/^ar/.test(l)?n.removeAttribute(l):n.setAttribute(l,u));}function z(l){this.l[l.type+!1](n.event?n.event(l):l);}function N(l){this.l[l.type+!0](n.event?n.event(l):l);}function T(n,l,u){var i,t;for(i=0;i<n.__k.length;i++)(t=n.__k[i])&&(t.__=n,t.__e&&("function"==typeof t.type&&t.__k.length>1&&T(t,l,u),l=x(u,t,t,n.__k,null,t.__e,l),"function"==typeof n.type&&(n.__d=l)));}function $(l,u,i,t,o,r,f,e,c){var a,v,h,y,_,w,k,g,b,x,A,P=u.type;if(void 0!==u.constructor)return null;null!=i.__h&&(c=i.__h,e=u.__e=i.__e,u.__h=null,r=[e]),(a=n.__b)&&a(u);try{n:if("function"==typeof P){if(g=u.props,b=(a=P.contextType)&&t[a.__c],x=a?b?b.props.value:a.__:t,i.__c?k=(v=u.__c=i.__c).__=v.__E:("prototype"in P&&P.prototype.render?u.__c=v=new P(g,x):(u.__c=v=new d(g,x),v.constructor=P,v.render=M),b&&b.sub(v),v.props=g,v.state||(v.state={}),v.context=x,v.__n=t,h=v.__d=!0,v.__h=[]),null==v.__s&&(v.__s=v.state),null!=P.getDerivedStateFromProps&&(v.__s==v.state&&(v.__s=s({},v.__s)),s(v.__s,P.getDerivedStateFromProps(g,v.__s))),y=v.props,_=v.state,h)null==P.getDerivedStateFromProps&&null!=v.componentWillMount&&v.componentWillMount(),null!=v.componentDidMount&&v.__h.push(v.componentDidMount);else {if(null==P.getDerivedStateFromProps&&g!==y&&null!=v.componentWillReceiveProps&&v.componentWillReceiveProps(g,x),!v.__e&&null!=v.shouldComponentUpdate&&!1===v.shouldComponentUpdate(g,v.__s,x)||u.__v===i.__v){v.props=g,v.state=v.__s,u.__v!==i.__v&&(v.__d=!1),v.__v=u,u.__e=i.__e,u.__k=i.__k,v.__h.length&&f.push(v),T(u,e,l);break n}null!=v.componentWillUpdate&&v.componentWillUpdate(g,v.__s,x),null!=v.componentDidUpdate&&v.__h.push(function(){v.componentDidUpdate(y,_,w);});}v.context=x,v.props=g,v.state=v.__s,(a=n.__r)&&a(u),v.__d=!1,v.__v=u,v.__P=l,a=v.render(v.props,v.state,v.context),v.state=v.__s,null!=v.getChildContext&&(t=s(s({},t),v.getChildContext())),h||null==v.getSnapshotBeforeUpdate||(w=v.getSnapshotBeforeUpdate(y,_)),A=null!=a&&a.type==p&&null==a.key?a.props.children:a,m(l,Array.isArray(A)?A:[A],u,i,t,o,r,f,e,c),v.base=u.__e,u.__h=null,v.__h.length&&f.push(v),k&&(v.__E=v.__=null),v.__e=!1;}else null==r&&u.__v===i.__v?(u.__k=i.__k,u.__e=i.__e):u.__e=H(i.__e,u,i,t,o,r,f,c);(a=n.diffed)&&a(u);}catch(l){u.__v=null,(c||null!=r)&&(u.__e=e,u.__h=!!c,r[r.indexOf(e)]=null),n.__e(l,u,i);}return u.__e}function j(l,u){n.__c&&n.__c(u,l),l.some(function(u){try{l=u.__h,u.__h=[],l.some(function(n){n.call(u);});}catch(l){n.__e(l,u.__v);}});}function H(n,l,u,i,t,o,r,c){var s,a,v,h,y,p=u.props,d=l.props;if(t="svg"===l.type||t,null!=o)for(s=0;s<o.length;s++)if(null!=(a=o[s])&&((null===l.type?3===a.nodeType:a.localName===l.type)||n==a)){n=a,o[s]=null;break}if(null==n){if(null===l.type)return document.createTextNode(d);n=t?document.createElementNS("http://www.w3.org/2000/svg",l.type):document.createElement(l.type,d.is&&{is:d.is}),o=null,c=!1;}if(null===l.type)p===d||c&&n.data===d||(n.data=d);else {if(null!=o&&(o=e.slice.call(n.childNodes)),v=(p=u.props||f).dangerouslySetInnerHTML,h=d.dangerouslySetInnerHTML,!c){if(null!=o)for(p={},y=0;y<n.attributes.length;y++)p[n.attributes[y].name]=n.attributes[y].value;(h||v)&&(h&&(v&&h.__html==v.__html||h.__html===n.innerHTML)||(n.innerHTML=h&&h.__html||""));}A(n,d,p,t,c),h?l.__k=[]:(s=l.props.children,m(n,Array.isArray(s)?s:[s],l,u,i,"foreignObject"!==l.type&&t,o,r,f,c)),c||("value"in d&&void 0!==(s=d.value)&&(s!==n.value||"progress"===l.type&&!s)&&C(n,"value",s,p.value,!1),"checked"in d&&void 0!==(s=d.checked)&&s!==n.checked&&C(n,"checked",s,p.checked,!1));}return n}function I(l,u,i){try{"function"==typeof l?l(u):l.current=u;}catch(l){n.__e(l,i);}}function L(l,u,i){var t,o,r;if(n.unmount&&n.unmount(l),(t=l.ref)&&(t.current&&t.current!==l.__e||I(t,null,u)),i||"function"==typeof l.type||(i=null!=(o=l.__e)),l.__e=l.__d=void 0,null!=(t=l.__c)){if(t.componentWillUnmount)try{t.componentWillUnmount();}catch(l){n.__e(l,u);}t.base=t.__P=null;}if(t=l.__k)for(r=0;r<t.length;r++)t[r]&&L(t[r],u,i);null!=o&&a(o);}function M(n,l,u){return this.constructor(n,u)}function O(l,u,i){var t,r,c;n.__&&n.__(l,u),r=(t=i===o)?null:i&&i.__k||u.__k,l=v(p,null,[l]),c=[],$(u,(t?u:i||u).__k=l,r||f,f,void 0!==u.ownerSVGElement,i&&!t?[i]:r?null:u.childNodes.length?e.slice.call(u.childNodes):null,c,i||f,t),j(c,l);}function B(n,l){var u={__c:l="__cC"+r++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n,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(k);},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={__e:function(n,l){for(var u,i,t,o=l.__h;l=l.__;)if((u=l.__c)&&!u.__)try{if((i=u.constructor)&&null!=i.getDerivedStateFromError&&(u.setState(i.getDerivedStateFromError(n)),t=u.__d),null!=u.componentDidCatch&&(u.componentDidCatch(n),t=u.__d),t)return l.__h=o,u.__E=u}catch(l){n=l;}throw n},__v:0},d.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=s({},this.state),"function"==typeof n&&(n=n(s({},u),this.props)),n&&s(u,n),null!=n&&this.__v&&(l&&this.__h.push(l),k(this));},d.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),k(this));},d.prototype.render=p,u=[],i="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,g.__r=0,o=f,r=0; |
|
|
|
var globalObj = typeof globalThis !== 'undefined' ? globalThis : window; // // TODO: streamline when killing IE11 support |
|
if (globalObj.FullCalendarVDom) { |
|
console.warn('FullCalendar VDOM already loaded'); |
|
} |
|
else { |
|
globalObj.FullCalendarVDom = { |
|
Component: d, |
|
createElement: v, |
|
render: O, |
|
createRef: y, |
|
Fragment: p, |
|
createContext: createContext, |
|
flushToDom: flushToDom, |
|
unmountComponentAtNode: unmountComponentAtNode, |
|
}; |
|
} |
|
// HACKS... |
|
// TODO: lock version |
|
// TODO: link gh issues |
|
function flushToDom() { |
|
var oldDebounceRendering = n.debounceRendering; // orig |
|
var callbackQ = []; |
|
function execCallbackSync(callback) { |
|
callbackQ.push(callback); |
|
} |
|
n.debounceRendering = execCallbackSync; |
|
O(v(FakeComponent, {}), document.createElement('div')); |
|
while (callbackQ.length) { |
|
callbackQ.shift()(); |
|
} |
|
n.debounceRendering = oldDebounceRendering; |
|
} |
|
var FakeComponent = /** @class */ (function (_super) { |
|
__extends(FakeComponent, _super); |
|
function FakeComponent() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
FakeComponent.prototype.render = function () { return v('div', {}); }; |
|
FakeComponent.prototype.componentDidMount = function () { this.setState({}); }; |
|
return FakeComponent; |
|
}(d)); |
|
function createContext(defaultValue) { |
|
var ContextType = B(defaultValue); |
|
var origProvider = ContextType.Provider; |
|
ContextType.Provider = function () { |
|
var _this = this; |
|
var isNew = !this.getChildContext; |
|
var children = origProvider.apply(this, arguments); // eslint-disable-line prefer-rest-params |
|
if (isNew) { |
|
var subs_1 = []; |
|
this.shouldComponentUpdate = function (_props) { |
|
if (_this.props.value !== _props.value) { |
|
subs_1.forEach(function (c) { |
|
c.context = _props.value; |
|
c.forceUpdate(); |
|
}); |
|
} |
|
}; |
|
this.sub = function (c) { |
|
subs_1.push(c); |
|
var old = c.componentWillUnmount; |
|
c.componentWillUnmount = function () { |
|
subs_1.splice(subs_1.indexOf(c), 1); |
|
old && old.call(c); |
|
}; |
|
}; |
|
} |
|
return children; |
|
}; |
|
return ContextType; |
|
} |
|
function unmountComponentAtNode(node) { |
|
O(null, node); |
|
} |
|
|
|
// no public types yet. when there are, export from: |
|
// import {} from './api-type-deps' |
|
var EventSourceApi = /** @class */ (function () { |
|
function EventSourceApi(context, internalEventSource) { |
|
this.context = context; |
|
this.internalEventSource = internalEventSource; |
|
} |
|
EventSourceApi.prototype.remove = function () { |
|
this.context.dispatch({ |
|
type: 'REMOVE_EVENT_SOURCE', |
|
sourceId: this.internalEventSource.sourceId, |
|
}); |
|
}; |
|
EventSourceApi.prototype.refetch = function () { |
|
this.context.dispatch({ |
|
type: 'FETCH_EVENT_SOURCES', |
|
sourceIds: [this.internalEventSource.sourceId], |
|
}); |
|
}; |
|
Object.defineProperty(EventSourceApi.prototype, "id", { |
|
get: function () { |
|
return this.internalEventSource.publicId; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventSourceApi.prototype, "url", { |
|
get: function () { |
|
return this.internalEventSource.meta.url; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventSourceApi.prototype, "format", { |
|
get: function () { |
|
return this.internalEventSource.meta.format; // TODO: bad. not guaranteed |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
return EventSourceApi; |
|
}()); |
|
|
|
function removeElement(el) { |
|
if (el.parentNode) { |
|
el.parentNode.removeChild(el); |
|
} |
|
} |
|
// Querying |
|
// ---------------------------------------------------------------------------------------------------------------- |
|
function elementClosest(el, selector) { |
|
if (el.closest) { |
|
return el.closest(selector); |
|
// really bad fallback for IE |
|
// from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest |
|
} |
|
if (!document.documentElement.contains(el)) { |
|
return null; |
|
} |
|
do { |
|
if (elementMatches(el, selector)) { |
|
return el; |
|
} |
|
el = (el.parentElement || el.parentNode); |
|
} while (el !== null && el.nodeType === 1); |
|
return null; |
|
} |
|
function elementMatches(el, selector) { |
|
var method = el.matches || el.matchesSelector || el.msMatchesSelector; |
|
return method.call(el, selector); |
|
} |
|
// accepts multiple subject els |
|
// returns a real array. good for methods like forEach |
|
// TODO: accept the document |
|
function findElements(container, selector) { |
|
var containers = container instanceof HTMLElement ? [container] : container; |
|
var allMatches = []; |
|
for (var i = 0; i < containers.length; i += 1) { |
|
var matches = containers[i].querySelectorAll(selector); |
|
for (var j = 0; j < matches.length; j += 1) { |
|
allMatches.push(matches[j]); |
|
} |
|
} |
|
return allMatches; |
|
} |
|
// accepts multiple subject els |
|
// only queries direct child elements // TODO: rename to findDirectChildren! |
|
function findDirectChildren(parent, selector) { |
|
var parents = parent instanceof HTMLElement ? [parent] : parent; |
|
var allMatches = []; |
|
for (var i = 0; i < parents.length; i += 1) { |
|
var childNodes = parents[i].children; // only ever elements |
|
for (var j = 0; j < childNodes.length; j += 1) { |
|
var childNode = childNodes[j]; |
|
if (!selector || elementMatches(childNode, selector)) { |
|
allMatches.push(childNode); |
|
} |
|
} |
|
} |
|
return allMatches; |
|
} |
|
// Style |
|
// ---------------------------------------------------------------------------------------------------------------- |
|
var PIXEL_PROP_RE = /(top|left|right|bottom|width|height)$/i; |
|
function applyStyle(el, props) { |
|
for (var propName in props) { |
|
applyStyleProp(el, propName, props[propName]); |
|
} |
|
} |
|
function applyStyleProp(el, name, val) { |
|
if (val == null) { |
|
el.style[name] = ''; |
|
} |
|
else if (typeof val === 'number' && PIXEL_PROP_RE.test(name)) { |
|
el.style[name] = val + "px"; |
|
} |
|
else { |
|
el.style[name] = val; |
|
} |
|
} |
|
|
|
// Stops a mouse/touch event from doing it's native browser action |
|
function preventDefault(ev) { |
|
ev.preventDefault(); |
|
} |
|
// Event Delegation |
|
// ---------------------------------------------------------------------------------------------------------------- |
|
function buildDelegationHandler(selector, handler) { |
|
return function (ev) { |
|
var matchedChild = elementClosest(ev.target, selector); |
|
if (matchedChild) { |
|
handler.call(matchedChild, ev, matchedChild); |
|
} |
|
}; |
|
} |
|
function listenBySelector(container, eventType, selector, handler) { |
|
var attachedHandler = buildDelegationHandler(selector, handler); |
|
container.addEventListener(eventType, attachedHandler); |
|
return function () { |
|
container.removeEventListener(eventType, attachedHandler); |
|
}; |
|
} |
|
function listenToHoverBySelector(container, selector, onMouseEnter, onMouseLeave) { |
|
var currentMatchedChild; |
|
return listenBySelector(container, 'mouseover', selector, function (mouseOverEv, matchedChild) { |
|
if (matchedChild !== currentMatchedChild) { |
|
currentMatchedChild = matchedChild; |
|
onMouseEnter(mouseOverEv, matchedChild); |
|
var realOnMouseLeave_1 = function (mouseLeaveEv) { |
|
currentMatchedChild = null; |
|
onMouseLeave(mouseLeaveEv, matchedChild); |
|
matchedChild.removeEventListener('mouseleave', realOnMouseLeave_1); |
|
}; |
|
// listen to the next mouseleave, and then unattach |
|
matchedChild.addEventListener('mouseleave', realOnMouseLeave_1); |
|
} |
|
}); |
|
} |
|
// Animation |
|
// ---------------------------------------------------------------------------------------------------------------- |
|
var transitionEventNames = [ |
|
'webkitTransitionEnd', |
|
'otransitionend', |
|
'oTransitionEnd', |
|
'msTransitionEnd', |
|
'transitionend', |
|
]; |
|
// triggered only when the next single subsequent transition finishes |
|
function whenTransitionDone(el, callback) { |
|
var realCallback = function (ev) { |
|
callback(ev); |
|
transitionEventNames.forEach(function (eventName) { |
|
el.removeEventListener(eventName, realCallback); |
|
}); |
|
}; |
|
transitionEventNames.forEach(function (eventName) { |
|
el.addEventListener(eventName, realCallback); // cross-browser way to determine when the transition finishes |
|
}); |
|
} |
|
|
|
var guidNumber = 0; |
|
function guid() { |
|
guidNumber += 1; |
|
return String(guidNumber); |
|
} |
|
/* FullCalendar-specific DOM Utilities |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
// Make the mouse cursor express that an event is not allowed in the current area |
|
function disableCursor() { |
|
document.body.classList.add('fc-not-allowed'); |
|
} |
|
// Returns the mouse cursor to its original look |
|
function enableCursor() { |
|
document.body.classList.remove('fc-not-allowed'); |
|
} |
|
/* Selection |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
function preventSelection(el) { |
|
el.classList.add('fc-unselectable'); |
|
el.addEventListener('selectstart', preventDefault); |
|
} |
|
function allowSelection(el) { |
|
el.classList.remove('fc-unselectable'); |
|
el.removeEventListener('selectstart', preventDefault); |
|
} |
|
/* Context Menu |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
function preventContextMenu(el) { |
|
el.addEventListener('contextmenu', preventDefault); |
|
} |
|
function allowContextMenu(el) { |
|
el.removeEventListener('contextmenu', preventDefault); |
|
} |
|
function parseFieldSpecs(input) { |
|
var specs = []; |
|
var tokens = []; |
|
var i; |
|
var token; |
|
if (typeof input === 'string') { |
|
tokens = input.split(/\s*,\s*/); |
|
} |
|
else if (typeof input === 'function') { |
|
tokens = [input]; |
|
} |
|
else if (Array.isArray(input)) { |
|
tokens = input; |
|
} |
|
for (i = 0; i < tokens.length; i += 1) { |
|
token = tokens[i]; |
|
if (typeof token === 'string') { |
|
specs.push(token.charAt(0) === '-' ? |
|
{ field: token.substring(1), order: -1 } : |
|
{ field: token, order: 1 }); |
|
} |
|
else if (typeof token === 'function') { |
|
specs.push({ func: token }); |
|
} |
|
} |
|
return specs; |
|
} |
|
function compareByFieldSpecs(obj0, obj1, fieldSpecs) { |
|
var i; |
|
var cmp; |
|
for (i = 0; i < fieldSpecs.length; i += 1) { |
|
cmp = compareByFieldSpec(obj0, obj1, fieldSpecs[i]); |
|
if (cmp) { |
|
return cmp; |
|
} |
|
} |
|
return 0; |
|
} |
|
function compareByFieldSpec(obj0, obj1, fieldSpec) { |
|
if (fieldSpec.func) { |
|
return fieldSpec.func(obj0, obj1); |
|
} |
|
return flexibleCompare(obj0[fieldSpec.field], obj1[fieldSpec.field]) |
|
* (fieldSpec.order || 1); |
|
} |
|
function flexibleCompare(a, b) { |
|
if (!a && !b) { |
|
return 0; |
|
} |
|
if (b == null) { |
|
return -1; |
|
} |
|
if (a == null) { |
|
return 1; |
|
} |
|
if (typeof a === 'string' || typeof b === 'string') { |
|
return String(a).localeCompare(String(b)); |
|
} |
|
return a - b; |
|
} |
|
/* String Utilities |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
function padStart(val, len) { |
|
var s = String(val); |
|
return '000'.substr(0, len - s.length) + s; |
|
} |
|
/* Number Utilities |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
function compareNumbers(a, b) { |
|
return a - b; |
|
} |
|
function isInt(n) { |
|
return n % 1 === 0; |
|
} |
|
/* FC-specific DOM dimension stuff |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
function computeSmallestCellWidth(cellEl) { |
|
var allWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-frame'); |
|
var contentWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-cushion'); |
|
if (!allWidthEl) { |
|
throw new Error('needs fc-scrollgrid-shrink-frame className'); // TODO: use const |
|
} |
|
if (!contentWidthEl) { |
|
throw new Error('needs fc-scrollgrid-shrink-cushion className'); |
|
} |
|
return cellEl.getBoundingClientRect().width - allWidthEl.getBoundingClientRect().width + // the cell padding+border |
|
contentWidthEl.getBoundingClientRect().width; |
|
} |
|
|
|
var DAY_IDS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; |
|
// Adding |
|
function addWeeks(m, n) { |
|
var a = dateToUtcArray(m); |
|
a[2] += n * 7; |
|
return arrayToUtcDate(a); |
|
} |
|
function addDays(m, n) { |
|
var a = dateToUtcArray(m); |
|
a[2] += n; |
|
return arrayToUtcDate(a); |
|
} |
|
function addMs(m, n) { |
|
var a = dateToUtcArray(m); |
|
a[6] += n; |
|
return arrayToUtcDate(a); |
|
} |
|
// Diffing (all return floats) |
|
// TODO: why not use ranges? |
|
function diffWeeks(m0, m1) { |
|
return diffDays(m0, m1) / 7; |
|
} |
|
function diffDays(m0, m1) { |
|
return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60 * 24); |
|
} |
|
function diffHours(m0, m1) { |
|
return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60); |
|
} |
|
function diffMinutes(m0, m1) { |
|
return (m1.valueOf() - m0.valueOf()) / (1000 * 60); |
|
} |
|
function diffSeconds(m0, m1) { |
|
return (m1.valueOf() - m0.valueOf()) / 1000; |
|
} |
|
function diffDayAndTime(m0, m1) { |
|
var m0day = startOfDay(m0); |
|
var m1day = startOfDay(m1); |
|
return { |
|
years: 0, |
|
months: 0, |
|
days: Math.round(diffDays(m0day, m1day)), |
|
milliseconds: (m1.valueOf() - m1day.valueOf()) - (m0.valueOf() - m0day.valueOf()), |
|
}; |
|
} |
|
// Diffing Whole Units |
|
function diffWholeWeeks(m0, m1) { |
|
var d = diffWholeDays(m0, m1); |
|
if (d !== null && d % 7 === 0) { |
|
return d / 7; |
|
} |
|
return null; |
|
} |
|
function diffWholeDays(m0, m1) { |
|
if (timeAsMs(m0) === timeAsMs(m1)) { |
|
return Math.round(diffDays(m0, m1)); |
|
} |
|
return null; |
|
} |
|
// Start-Of |
|
function startOfDay(m) { |
|
return arrayToUtcDate([ |
|
m.getUTCFullYear(), |
|
m.getUTCMonth(), |
|
m.getUTCDate(), |
|
]); |
|
} |
|
function startOfHour(m) { |
|
return arrayToUtcDate([ |
|
m.getUTCFullYear(), |
|
m.getUTCMonth(), |
|
m.getUTCDate(), |
|
m.getUTCHours(), |
|
]); |
|
} |
|
function startOfMinute(m) { |
|
return arrayToUtcDate([ |
|
m.getUTCFullYear(), |
|
m.getUTCMonth(), |
|
m.getUTCDate(), |
|
m.getUTCHours(), |
|
m.getUTCMinutes(), |
|
]); |
|
} |
|
function startOfSecond(m) { |
|
return arrayToUtcDate([ |
|
m.getUTCFullYear(), |
|
m.getUTCMonth(), |
|
m.getUTCDate(), |
|
m.getUTCHours(), |
|
m.getUTCMinutes(), |
|
m.getUTCSeconds(), |
|
]); |
|
} |
|
// Week Computation |
|
function weekOfYear(marker, dow, doy) { |
|
var y = marker.getUTCFullYear(); |
|
var w = weekOfGivenYear(marker, y, dow, doy); |
|
if (w < 1) { |
|
return weekOfGivenYear(marker, y - 1, dow, doy); |
|
} |
|
var nextW = weekOfGivenYear(marker, y + 1, dow, doy); |
|
if (nextW >= 1) { |
|
return Math.min(w, nextW); |
|
} |
|
return w; |
|
} |
|
function weekOfGivenYear(marker, year, dow, doy) { |
|
var firstWeekStart = arrayToUtcDate([year, 0, 1 + firstWeekOffset(year, dow, doy)]); |
|
var dayStart = startOfDay(marker); |
|
var days = Math.round(diffDays(firstWeekStart, dayStart)); |
|
return Math.floor(days / 7) + 1; // zero-indexed |
|
} |
|
// start-of-first-week - start-of-year |
|
function firstWeekOffset(year, dow, doy) { |
|
// first-week day -- which january is always in the first week (4 for iso, 1 for other) |
|
var fwd = 7 + dow - doy; |
|
// first-week day local weekday -- which local weekday is fwd |
|
var fwdlw = (7 + arrayToUtcDate([year, 0, fwd]).getUTCDay() - dow) % 7; |
|
return -fwdlw + fwd - 1; |
|
} |
|
// Array Conversion |
|
function dateToLocalArray(date) { |
|
return [ |
|
date.getFullYear(), |
|
date.getMonth(), |
|
date.getDate(), |
|
date.getHours(), |
|
date.getMinutes(), |
|
date.getSeconds(), |
|
date.getMilliseconds(), |
|
]; |
|
} |
|
function arrayToLocalDate(a) { |
|
return new Date(a[0], a[1] || 0, a[2] == null ? 1 : a[2], // day of month |
|
a[3] || 0, a[4] || 0, a[5] || 0); |
|
} |
|
function dateToUtcArray(date) { |
|
return [ |
|
date.getUTCFullYear(), |
|
date.getUTCMonth(), |
|
date.getUTCDate(), |
|
date.getUTCHours(), |
|
date.getUTCMinutes(), |
|
date.getUTCSeconds(), |
|
date.getUTCMilliseconds(), |
|
]; |
|
} |
|
function arrayToUtcDate(a) { |
|
// according to web standards (and Safari), a month index is required. |
|
// massage if only given a year. |
|
if (a.length === 1) { |
|
a = a.concat([0]); |
|
} |
|
return new Date(Date.UTC.apply(Date, a)); |
|
} |
|
// Other Utils |
|
function isValidDate(m) { |
|
return !isNaN(m.valueOf()); |
|
} |
|
function timeAsMs(m) { |
|
return m.getUTCHours() * 1000 * 60 * 60 + |
|
m.getUTCMinutes() * 1000 * 60 + |
|
m.getUTCSeconds() * 1000 + |
|
m.getUTCMilliseconds(); |
|
} |
|
|
|
function createEventInstance(defId, range, forcedStartTzo, forcedEndTzo) { |
|
return { |
|
instanceId: guid(), |
|
defId: defId, |
|
range: range, |
|
forcedStartTzo: forcedStartTzo == null ? null : forcedStartTzo, |
|
forcedEndTzo: forcedEndTzo == null ? null : forcedEndTzo, |
|
}; |
|
} |
|
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty; |
|
// Merges an array of objects into a single object. |
|
// The second argument allows for an array of property names who's object values will be merged together. |
|
function mergeProps(propObjs, complexPropsMap) { |
|
var dest = {}; |
|
if (complexPropsMap) { |
|
for (var name_1 in complexPropsMap) { |
|
var complexObjs = []; |
|
// collect the trailing object values, stopping when a non-object is discovered |
|
for (var i = propObjs.length - 1; i >= 0; i -= 1) { |
|
var val = propObjs[i][name_1]; |
|
if (typeof val === 'object' && val) { // non-null object |
|
complexObjs.unshift(val); |
|
} |
|
else if (val !== undefined) { |
|
dest[name_1] = val; // if there were no objects, this value will be used |
|
break; |
|
} |
|
} |
|
// if the trailing values were objects, use the merged value |
|
if (complexObjs.length) { |
|
dest[name_1] = mergeProps(complexObjs); |
|
} |
|
} |
|
} |
|
// copy values into the destination, going from last to first |
|
for (var i = propObjs.length - 1; i >= 0; i -= 1) { |
|
var props = propObjs[i]; |
|
for (var name_2 in props) { |
|
if (!(name_2 in dest)) { // if already assigned by previous props or complex props, don't reassign |
|
dest[name_2] = props[name_2]; |
|
} |
|
} |
|
} |
|
return dest; |
|
} |
|
function filterHash(hash, func) { |
|
var filtered = {}; |
|
for (var key in hash) { |
|
if (func(hash[key], key)) { |
|
filtered[key] = hash[key]; |
|
} |
|
} |
|
return filtered; |
|
} |
|
function mapHash(hash, func) { |
|
var newHash = {}; |
|
for (var key in hash) { |
|
newHash[key] = func(hash[key], key); |
|
} |
|
return newHash; |
|
} |
|
function arrayToHash(a) { |
|
var hash = {}; |
|
for (var _i = 0, a_1 = a; _i < a_1.length; _i++) { |
|
var item = a_1[_i]; |
|
hash[item] = true; |
|
} |
|
return hash; |
|
} |
|
function buildHashFromArray(a, func) { |
|
var hash = {}; |
|
for (var i = 0; i < a.length; i += 1) { |
|
var tuple = func(a[i], i); |
|
hash[tuple[0]] = tuple[1]; |
|
} |
|
return hash; |
|
} |
|
function hashValuesToArray(obj) { |
|
var a = []; |
|
for (var key in obj) { |
|
a.push(obj[key]); |
|
} |
|
return a; |
|
} |
|
function isPropsEqual(obj0, obj1) { |
|
if (obj0 === obj1) { |
|
return true; |
|
} |
|
for (var key in obj0) { |
|
if (hasOwnProperty.call(obj0, key)) { |
|
if (!(key in obj1)) { |
|
return false; |
|
} |
|
} |
|
} |
|
for (var key in obj1) { |
|
if (hasOwnProperty.call(obj1, key)) { |
|
if (obj0[key] !== obj1[key]) { |
|
return false; |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
function getUnequalProps(obj0, obj1) { |
|
var keys = []; |
|
for (var key in obj0) { |
|
if (hasOwnProperty.call(obj0, key)) { |
|
if (!(key in obj1)) { |
|
keys.push(key); |
|
} |
|
} |
|
} |
|
for (var key in obj1) { |
|
if (hasOwnProperty.call(obj1, key)) { |
|
if (obj0[key] !== obj1[key]) { |
|
keys.push(key); |
|
} |
|
} |
|
} |
|
return keys; |
|
} |
|
function compareObjs(oldProps, newProps, equalityFuncs) { |
|
if (equalityFuncs === void 0) { equalityFuncs = {}; } |
|
if (oldProps === newProps) { |
|
return true; |
|
} |
|
for (var key in newProps) { |
|
if (key in oldProps && isObjValsEqual(oldProps[key], newProps[key], equalityFuncs[key])) ; |
|
else { |
|
return false; |
|
} |
|
} |
|
// check for props that were omitted in the new |
|
for (var key in oldProps) { |
|
if (!(key in newProps)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
/* |
|
assumed "true" equality for handler names like "onReceiveSomething" |
|
*/ |
|
function isObjValsEqual(val0, val1, comparator) { |
|
if (val0 === val1 || comparator === true) { |
|
return true; |
|
} |
|
if (comparator) { |
|
return comparator(val0, val1); |
|
} |
|
return false; |
|
} |
|
function collectFromHash(hash, startIndex, endIndex, step) { |
|
if (startIndex === void 0) { startIndex = 0; } |
|
if (step === void 0) { step = 1; } |
|
var res = []; |
|
if (endIndex == null) { |
|
endIndex = Object.keys(hash).length; |
|
} |
|
for (var i = startIndex; i < endIndex; i += step) { |
|
var val = hash[i]; |
|
if (val !== undefined) { // will disregard undefined for sparse arrays |
|
res.push(val); |
|
} |
|
} |
|
return res; |
|
} |
|
|
|
function parseRecurring(refined, defaultAllDay, dateEnv, recurringTypes) { |
|
for (var i = 0; i < recurringTypes.length; i += 1) { |
|
var parsed = recurringTypes[i].parse(refined, dateEnv); |
|
if (parsed) { |
|
var allDay = refined.allDay; |
|
if (allDay == null) { |
|
allDay = defaultAllDay; |
|
if (allDay == null) { |
|
allDay = parsed.allDayGuess; |
|
if (allDay == null) { |
|
allDay = false; |
|
} |
|
} |
|
} |
|
return { |
|
allDay: allDay, |
|
duration: parsed.duration, |
|
typeData: parsed.typeData, |
|
typeId: i, |
|
}; |
|
} |
|
} |
|
return null; |
|
} |
|
function expandRecurring(eventStore, framingRange, context) { |
|
var dateEnv = context.dateEnv, pluginHooks = context.pluginHooks, options = context.options; |
|
var defs = eventStore.defs, instances = eventStore.instances; |
|
// remove existing recurring instances |
|
// TODO: bad. always expand events as a second step |
|
instances = filterHash(instances, function (instance) { return !defs[instance.defId].recurringDef; }); |
|
for (var defId in defs) { |
|
var def = defs[defId]; |
|
if (def.recurringDef) { |
|
var duration = def.recurringDef.duration; |
|
if (!duration) { |
|
duration = def.allDay ? |
|
options.defaultAllDayEventDuration : |
|
options.defaultTimedEventDuration; |
|
} |
|
var starts = expandRecurringRanges(def, duration, framingRange, dateEnv, pluginHooks.recurringTypes); |
|
for (var _i = 0, starts_1 = starts; _i < starts_1.length; _i++) { |
|
var start = starts_1[_i]; |
|
var instance = createEventInstance(defId, { |
|
start: start, |
|
end: dateEnv.add(start, duration), |
|
}); |
|
instances[instance.instanceId] = instance; |
|
} |
|
} |
|
} |
|
return { defs: defs, instances: instances }; |
|
} |
|
/* |
|
Event MUST have a recurringDef |
|
*/ |
|
function expandRecurringRanges(eventDef, duration, framingRange, dateEnv, recurringTypes) { |
|
var typeDef = recurringTypes[eventDef.recurringDef.typeId]; |
|
var markers = typeDef.expand(eventDef.recurringDef.typeData, { |
|
start: dateEnv.subtract(framingRange.start, duration), |
|
end: framingRange.end, |
|
}, dateEnv); |
|
// the recurrence plugins don't guarantee that all-day events are start-of-day, so we have to |
|
if (eventDef.allDay) { |
|
markers = markers.map(startOfDay); |
|
} |
|
return markers; |
|
} |
|
|
|
var INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds']; |
|
var PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/; |
|
// Parsing and Creation |
|
function createDuration(input, unit) { |
|
var _a; |
|
if (typeof input === 'string') { |
|
return parseString(input); |
|
} |
|
if (typeof input === 'object' && input) { // non-null object |
|
return parseObject(input); |
|
} |
|
if (typeof input === 'number') { |
|
return parseObject((_a = {}, _a[unit || 'milliseconds'] = input, _a)); |
|
} |
|
return null; |
|
} |
|
function parseString(s) { |
|
var m = PARSE_RE.exec(s); |
|
if (m) { |
|
var sign = m[1] ? -1 : 1; |
|
return { |
|
years: 0, |
|
months: 0, |
|
days: sign * (m[2] ? parseInt(m[2], 10) : 0), |
|
milliseconds: sign * ((m[3] ? parseInt(m[3], 10) : 0) * 60 * 60 * 1000 + // hours |
|
(m[4] ? parseInt(m[4], 10) : 0) * 60 * 1000 + // minutes |
|
(m[5] ? parseInt(m[5], 10) : 0) * 1000 + // seconds |
|
(m[6] ? parseInt(m[6], 10) : 0) // ms |
|
), |
|
}; |
|
} |
|
return null; |
|
} |
|
function parseObject(obj) { |
|
var duration = { |
|
years: obj.years || obj.year || 0, |
|
months: obj.months || obj.month || 0, |
|
days: obj.days || obj.day || 0, |
|
milliseconds: (obj.hours || obj.hour || 0) * 60 * 60 * 1000 + // hours |
|
(obj.minutes || obj.minute || 0) * 60 * 1000 + // minutes |
|
(obj.seconds || obj.second || 0) * 1000 + // seconds |
|
(obj.milliseconds || obj.millisecond || obj.ms || 0), |
|
}; |
|
var weeks = obj.weeks || obj.week; |
|
if (weeks) { |
|
duration.days += weeks * 7; |
|
duration.specifiedWeeks = true; |
|
} |
|
return duration; |
|
} |
|
// Equality |
|
function durationsEqual(d0, d1) { |
|
return d0.years === d1.years && |
|
d0.months === d1.months && |
|
d0.days === d1.days && |
|
d0.milliseconds === d1.milliseconds; |
|
} |
|
function asCleanDays(dur) { |
|
if (!dur.years && !dur.months && !dur.milliseconds) { |
|
return dur.days; |
|
} |
|
return 0; |
|
} |
|
// Simple Math |
|
function addDurations(d0, d1) { |
|
return { |
|
years: d0.years + d1.years, |
|
months: d0.months + d1.months, |
|
days: d0.days + d1.days, |
|
milliseconds: d0.milliseconds + d1.milliseconds, |
|
}; |
|
} |
|
function subtractDurations(d1, d0) { |
|
return { |
|
years: d1.years - d0.years, |
|
months: d1.months - d0.months, |
|
days: d1.days - d0.days, |
|
milliseconds: d1.milliseconds - d0.milliseconds, |
|
}; |
|
} |
|
function multiplyDuration(d, n) { |
|
return { |
|
years: d.years * n, |
|
months: d.months * n, |
|
days: d.days * n, |
|
milliseconds: d.milliseconds * n, |
|
}; |
|
} |
|
// Conversions |
|
// "Rough" because they are based on average-case Gregorian months/years |
|
function asRoughYears(dur) { |
|
return asRoughDays(dur) / 365; |
|
} |
|
function asRoughMonths(dur) { |
|
return asRoughDays(dur) / 30; |
|
} |
|
function asRoughDays(dur) { |
|
return asRoughMs(dur) / 864e5; |
|
} |
|
function asRoughMinutes(dur) { |
|
return asRoughMs(dur) / (1000 * 60); |
|
} |
|
function asRoughSeconds(dur) { |
|
return asRoughMs(dur) / 1000; |
|
} |
|
function asRoughMs(dur) { |
|
return dur.years * (365 * 864e5) + |
|
dur.months * (30 * 864e5) + |
|
dur.days * 864e5 + |
|
dur.milliseconds; |
|
} |
|
// Advanced Math |
|
function wholeDivideDurations(numerator, denominator) { |
|
var res = null; |
|
for (var i = 0; i < INTERNAL_UNITS.length; i += 1) { |
|
var unit = INTERNAL_UNITS[i]; |
|
if (denominator[unit]) { |
|
var localRes = numerator[unit] / denominator[unit]; |
|
if (!isInt(localRes) || (res !== null && res !== localRes)) { |
|
return null; |
|
} |
|
res = localRes; |
|
} |
|
else if (numerator[unit]) { |
|
// needs to divide by something but can't! |
|
return null; |
|
} |
|
} |
|
return res; |
|
} |
|
function greatestDurationDenominator(dur) { |
|
var ms = dur.milliseconds; |
|
if (ms) { |
|
if (ms % 1000 !== 0) { |
|
return { unit: 'millisecond', value: ms }; |
|
} |
|
if (ms % (1000 * 60) !== 0) { |
|
return { unit: 'second', value: ms / 1000 }; |
|
} |
|
if (ms % (1000 * 60 * 60) !== 0) { |
|
return { unit: 'minute', value: ms / (1000 * 60) }; |
|
} |
|
if (ms) { |
|
return { unit: 'hour', value: ms / (1000 * 60 * 60) }; |
|
} |
|
} |
|
if (dur.days) { |
|
if (dur.specifiedWeeks && dur.days % 7 === 0) { |
|
return { unit: 'week', value: dur.days / 7 }; |
|
} |
|
return { unit: 'day', value: dur.days }; |
|
} |
|
if (dur.months) { |
|
return { unit: 'month', value: dur.months }; |
|
} |
|
if (dur.years) { |
|
return { unit: 'year', value: dur.years }; |
|
} |
|
return { unit: 'millisecond', value: 0 }; |
|
} |
|
|
|
// timeZoneOffset is in minutes |
|
function buildIsoString(marker, timeZoneOffset, stripZeroTime) { |
|
if (stripZeroTime === void 0) { stripZeroTime = false; } |
|
var s = marker.toISOString(); |
|
s = s.replace('.000', ''); |
|
if (stripZeroTime) { |
|
s = s.replace('T00:00:00Z', ''); |
|
} |
|
if (s.length > 10) { // time part wasn't stripped, can add timezone info |
|
if (timeZoneOffset == null) { |
|
s = s.replace('Z', ''); |
|
} |
|
else if (timeZoneOffset !== 0) { |
|
s = s.replace('Z', formatTimeZoneOffset(timeZoneOffset, true)); |
|
} |
|
// otherwise, its UTC-0 and we want to keep the Z |
|
} |
|
return s; |
|
} |
|
// formats the date, but with no time part |
|
// TODO: somehow merge with buildIsoString and stripZeroTime |
|
// TODO: rename. omit "string" |
|
function formatDayString(marker) { |
|
return marker.toISOString().replace(/T.*$/, ''); |
|
} |
|
// TODO: use Date::toISOString and use everything after the T? |
|
function formatIsoTimeString(marker) { |
|
return padStart(marker.getUTCHours(), 2) + ':' + |
|
padStart(marker.getUTCMinutes(), 2) + ':' + |
|
padStart(marker.getUTCSeconds(), 2); |
|
} |
|
function formatTimeZoneOffset(minutes, doIso) { |
|
if (doIso === void 0) { doIso = false; } |
|
var sign = minutes < 0 ? '-' : '+'; |
|
var abs = Math.abs(minutes); |
|
var hours = Math.floor(abs / 60); |
|
var mins = Math.round(abs % 60); |
|
if (doIso) { |
|
return sign + padStart(hours, 2) + ":" + padStart(mins, 2); |
|
} |
|
return "GMT" + sign + hours + (mins ? ":" + padStart(mins, 2) : ''); |
|
} |
|
|
|
// TODO: new util arrayify? |
|
function removeExact(array, exactVal) { |
|
var removeCnt = 0; |
|
var i = 0; |
|
while (i < array.length) { |
|
if (array[i] === exactVal) { |
|
array.splice(i, 1); |
|
removeCnt += 1; |
|
} |
|
else { |
|
i += 1; |
|
} |
|
} |
|
return removeCnt; |
|
} |
|
function isArraysEqual(a0, a1, equalityFunc) { |
|
if (a0 === a1) { |
|
return true; |
|
} |
|
var len = a0.length; |
|
var i; |
|
if (len !== a1.length) { // not array? or not same length? |
|
return false; |
|
} |
|
for (i = 0; i < len; i += 1) { |
|
if (!(equalityFunc ? equalityFunc(a0[i], a1[i]) : a0[i] === a1[i])) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
function memoize(workerFunc, resEquality, teardownFunc) { |
|
var currentArgs; |
|
var currentRes; |
|
return function () { |
|
var newArgs = []; |
|
for (var _i = 0; _i < arguments.length; _i++) { |
|
newArgs[_i] = arguments[_i]; |
|
} |
|
if (!currentArgs) { |
|
currentRes = workerFunc.apply(this, newArgs); |
|
} |
|
else if (!isArraysEqual(currentArgs, newArgs)) { |
|
if (teardownFunc) { |
|
teardownFunc(currentRes); |
|
} |
|
var res = workerFunc.apply(this, newArgs); |
|
if (!resEquality || !resEquality(res, currentRes)) { |
|
currentRes = res; |
|
} |
|
} |
|
currentArgs = newArgs; |
|
return currentRes; |
|
}; |
|
} |
|
function memoizeObjArg(workerFunc, resEquality, teardownFunc) { |
|
var _this = this; |
|
var currentArg; |
|
var currentRes; |
|
return function (newArg) { |
|
if (!currentArg) { |
|
currentRes = workerFunc.call(_this, newArg); |
|
} |
|
else if (!isPropsEqual(currentArg, newArg)) { |
|
if (teardownFunc) { |
|
teardownFunc(currentRes); |
|
} |
|
var res = workerFunc.call(_this, newArg); |
|
if (!resEquality || !resEquality(res, currentRes)) { |
|
currentRes = res; |
|
} |
|
} |
|
currentArg = newArg; |
|
return currentRes; |
|
}; |
|
} |
|
function memoizeArraylike(// used at all? |
|
workerFunc, resEquality, teardownFunc) { |
|
var _this = this; |
|
var currentArgSets = []; |
|
var currentResults = []; |
|
return function (newArgSets) { |
|
var currentLen = currentArgSets.length; |
|
var newLen = newArgSets.length; |
|
var i = 0; |
|
for (; i < currentLen; i += 1) { |
|
if (!newArgSets[i]) { // one of the old sets no longer exists |
|
if (teardownFunc) { |
|
teardownFunc(currentResults[i]); |
|
} |
|
} |
|
else if (!isArraysEqual(currentArgSets[i], newArgSets[i])) { |
|
if (teardownFunc) { |
|
teardownFunc(currentResults[i]); |
|
} |
|
var res = workerFunc.apply(_this, newArgSets[i]); |
|
if (!resEquality || !resEquality(res, currentResults[i])) { |
|
currentResults[i] = res; |
|
} |
|
} |
|
} |
|
for (; i < newLen; i += 1) { |
|
currentResults[i] = workerFunc.apply(_this, newArgSets[i]); |
|
} |
|
currentArgSets = newArgSets; |
|
currentResults.splice(newLen); // remove excess |
|
return currentResults; |
|
}; |
|
} |
|
function memoizeHashlike(// used? |
|
workerFunc, resEquality, teardownFunc) { |
|
var _this = this; |
|
var currentArgHash = {}; |
|
var currentResHash = {}; |
|
return function (newArgHash) { |
|
var newResHash = {}; |
|
for (var key in newArgHash) { |
|
if (!currentResHash[key]) { |
|
newResHash[key] = workerFunc.apply(_this, newArgHash[key]); |
|
} |
|
else if (!isArraysEqual(currentArgHash[key], newArgHash[key])) { |
|
if (teardownFunc) { |
|
teardownFunc(currentResHash[key]); |
|
} |
|
var res = workerFunc.apply(_this, newArgHash[key]); |
|
newResHash[key] = (resEquality && resEquality(res, currentResHash[key])) |
|
? currentResHash[key] |
|
: res; |
|
} |
|
else { |
|
newResHash[key] = currentResHash[key]; |
|
} |
|
} |
|
currentArgHash = newArgHash; |
|
currentResHash = newResHash; |
|
return newResHash; |
|
}; |
|
} |
|
|
|
var EXTENDED_SETTINGS_AND_SEVERITIES = { |
|
week: 3, |
|
separator: 0, |
|
omitZeroMinute: 0, |
|
meridiem: 0, |
|
omitCommas: 0, |
|
}; |
|
var STANDARD_DATE_PROP_SEVERITIES = { |
|
timeZoneName: 7, |
|
era: 6, |
|
year: 5, |
|
month: 4, |
|
day: 2, |
|
weekday: 2, |
|
hour: 1, |
|
minute: 1, |
|
second: 1, |
|
}; |
|
var MERIDIEM_RE = /\s*([ap])\.?m\.?/i; // eats up leading spaces too |
|
var COMMA_RE = /,/g; // we need re for globalness |
|
var MULTI_SPACE_RE = /\s+/g; |
|
var LTR_RE = /\u200e/g; // control character |
|
var UTC_RE = /UTC|GMT/; |
|
var NativeFormatter = /** @class */ (function () { |
|
function NativeFormatter(formatSettings) { |
|
var standardDateProps = {}; |
|
var extendedSettings = {}; |
|
var severity = 0; |
|
for (var name_1 in formatSettings) { |
|
if (name_1 in EXTENDED_SETTINGS_AND_SEVERITIES) { |
|
extendedSettings[name_1] = formatSettings[name_1]; |
|
severity = Math.max(EXTENDED_SETTINGS_AND_SEVERITIES[name_1], severity); |
|
} |
|
else { |
|
standardDateProps[name_1] = formatSettings[name_1]; |
|
if (name_1 in STANDARD_DATE_PROP_SEVERITIES) { // TODO: what about hour12? no severity |
|
severity = Math.max(STANDARD_DATE_PROP_SEVERITIES[name_1], severity); |
|
} |
|
} |
|
} |
|
this.standardDateProps = standardDateProps; |
|
this.extendedSettings = extendedSettings; |
|
this.severity = severity; |
|
this.buildFormattingFunc = memoize(buildFormattingFunc); |
|
} |
|
NativeFormatter.prototype.format = function (date, context) { |
|
return this.buildFormattingFunc(this.standardDateProps, this.extendedSettings, context)(date); |
|
}; |
|
NativeFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) { |
|
var _a = this, standardDateProps = _a.standardDateProps, extendedSettings = _a.extendedSettings; |
|
var diffSeverity = computeMarkerDiffSeverity(start.marker, end.marker, context.calendarSystem); |
|
if (!diffSeverity) { |
|
return this.format(start, context); |
|
} |
|
var biggestUnitForPartial = diffSeverity; |
|
if (biggestUnitForPartial > 1 && // the two dates are different in a way that's larger scale than time |
|
(standardDateProps.year === 'numeric' || standardDateProps.year === '2-digit') && |
|
(standardDateProps.month === 'numeric' || standardDateProps.month === '2-digit') && |
|
(standardDateProps.day === 'numeric' || standardDateProps.day === '2-digit')) { |
|
biggestUnitForPartial = 1; // make it look like the dates are only different in terms of time |
|
} |
|
var full0 = this.format(start, context); |
|
var full1 = this.format(end, context); |
|
if (full0 === full1) { |
|
return full0; |
|
} |
|
var partialDateProps = computePartialFormattingOptions(standardDateProps, biggestUnitForPartial); |
|
var partialFormattingFunc = buildFormattingFunc(partialDateProps, extendedSettings, context); |
|
var partial0 = partialFormattingFunc(start); |
|
var partial1 = partialFormattingFunc(end); |
|
var insertion = findCommonInsertion(full0, partial0, full1, partial1); |
|
var separator = extendedSettings.separator || betterDefaultSeparator || context.defaultSeparator || ''; |
|
if (insertion) { |
|
return insertion.before + partial0 + separator + partial1 + insertion.after; |
|
} |
|
return full0 + separator + full1; |
|
}; |
|
NativeFormatter.prototype.getLargestUnit = function () { |
|
switch (this.severity) { |
|
case 7: |
|
case 6: |
|
case 5: |
|
return 'year'; |
|
case 4: |
|
return 'month'; |
|
case 3: |
|
return 'week'; |
|
case 2: |
|
return 'day'; |
|
default: |
|
return 'time'; // really? |
|
} |
|
}; |
|
return NativeFormatter; |
|
}()); |
|
function buildFormattingFunc(standardDateProps, extendedSettings, context) { |
|
var standardDatePropCnt = Object.keys(standardDateProps).length; |
|
if (standardDatePropCnt === 1 && standardDateProps.timeZoneName === 'short') { |
|
return function (date) { return (formatTimeZoneOffset(date.timeZoneOffset)); }; |
|
} |
|
if (standardDatePropCnt === 0 && extendedSettings.week) { |
|
return function (date) { return (formatWeekNumber(context.computeWeekNumber(date.marker), context.weekText, context.locale, extendedSettings.week)); }; |
|
} |
|
return buildNativeFormattingFunc(standardDateProps, extendedSettings, context); |
|
} |
|
function buildNativeFormattingFunc(standardDateProps, extendedSettings, context) { |
|
standardDateProps = __assign({}, standardDateProps); // copy |
|
extendedSettings = __assign({}, extendedSettings); // copy |
|
sanitizeSettings(standardDateProps, extendedSettings); |
|
standardDateProps.timeZone = 'UTC'; // we leverage the only guaranteed timeZone for our UTC markers |
|
var normalFormat = new Intl.DateTimeFormat(context.locale.codes, standardDateProps); |
|
var zeroFormat; // needed? |
|
if (extendedSettings.omitZeroMinute) { |
|
var zeroProps = __assign({}, standardDateProps); |
|
delete zeroProps.minute; // seconds and ms were already considered in sanitizeSettings |
|
zeroFormat = new Intl.DateTimeFormat(context.locale.codes, zeroProps); |
|
} |
|
return function (date) { |
|
var marker = date.marker; |
|
var format; |
|
if (zeroFormat && !marker.getUTCMinutes()) { |
|
format = zeroFormat; |
|
} |
|
else { |
|
format = normalFormat; |
|
} |
|
var s = format.format(marker); |
|
return postProcess(s, date, standardDateProps, extendedSettings, context); |
|
}; |
|
} |
|
function sanitizeSettings(standardDateProps, extendedSettings) { |
|
// deal with a browser inconsistency where formatting the timezone |
|
// requires that the hour/minute be present. |
|
if (standardDateProps.timeZoneName) { |
|
if (!standardDateProps.hour) { |
|
standardDateProps.hour = '2-digit'; |
|
} |
|
if (!standardDateProps.minute) { |
|
standardDateProps.minute = '2-digit'; |
|
} |
|
} |
|
// only support short timezone names |
|
if (standardDateProps.timeZoneName === 'long') { |
|
standardDateProps.timeZoneName = 'short'; |
|
} |
|
// if requesting to display seconds, MUST display minutes |
|
if (extendedSettings.omitZeroMinute && (standardDateProps.second || standardDateProps.millisecond)) { |
|
delete extendedSettings.omitZeroMinute; |
|
} |
|
} |
|
function postProcess(s, date, standardDateProps, extendedSettings, context) { |
|
s = s.replace(LTR_RE, ''); // remove left-to-right control chars. do first. good for other regexes |
|
if (standardDateProps.timeZoneName === 'short') { |
|
s = injectTzoStr(s, (context.timeZone === 'UTC' || date.timeZoneOffset == null) ? |
|
'UTC' : // important to normalize for IE, which does "GMT" |
|
formatTimeZoneOffset(date.timeZoneOffset)); |
|
} |
|
if (extendedSettings.omitCommas) { |
|
s = s.replace(COMMA_RE, '').trim(); |
|
} |
|
if (extendedSettings.omitZeroMinute) { |
|
s = s.replace(':00', ''); // zeroFormat doesn't always achieve this |
|
} |
|
// ^ do anything that might create adjacent spaces before this point, |
|
// because MERIDIEM_RE likes to eat up loading spaces |
|
if (extendedSettings.meridiem === false) { |
|
s = s.replace(MERIDIEM_RE, '').trim(); |
|
} |
|
else if (extendedSettings.meridiem === 'narrow') { // a/p |
|
s = s.replace(MERIDIEM_RE, function (m0, m1) { return m1.toLocaleLowerCase(); }); |
|
} |
|
else if (extendedSettings.meridiem === 'short') { // am/pm |
|
s = s.replace(MERIDIEM_RE, function (m0, m1) { return m1.toLocaleLowerCase() + "m"; }); |
|
} |
|
else if (extendedSettings.meridiem === 'lowercase') { // other meridiem transformers already converted to lowercase |
|
s = s.replace(MERIDIEM_RE, function (m0) { return m0.toLocaleLowerCase(); }); |
|
} |
|
s = s.replace(MULTI_SPACE_RE, ' '); |
|
s = s.trim(); |
|
return s; |
|
} |
|
function injectTzoStr(s, tzoStr) { |
|
var replaced = false; |
|
s = s.replace(UTC_RE, function () { |
|
replaced = true; |
|
return tzoStr; |
|
}); |
|
// IE11 doesn't include UTC/GMT in the original string, so append to end |
|
if (!replaced) { |
|
s += " " + tzoStr; |
|
} |
|
return s; |
|
} |
|
function formatWeekNumber(num, weekText, locale, display) { |
|
var parts = []; |
|
if (display === 'narrow') { |
|
parts.push(weekText); |
|
} |
|
else if (display === 'short') { |
|
parts.push(weekText, ' '); |
|
} |
|
// otherwise, considered 'numeric' |
|
parts.push(locale.simpleNumberFormat.format(num)); |
|
if (locale.options.direction === 'rtl') { // TODO: use control characters instead? |
|
parts.reverse(); |
|
} |
|
return parts.join(''); |
|
} |
|
// Range Formatting Utils |
|
// 0 = exactly the same |
|
// 1 = different by time |
|
// and bigger |
|
function computeMarkerDiffSeverity(d0, d1, ca) { |
|
if (ca.getMarkerYear(d0) !== ca.getMarkerYear(d1)) { |
|
return 5; |
|
} |
|
if (ca.getMarkerMonth(d0) !== ca.getMarkerMonth(d1)) { |
|
return 4; |
|
} |
|
if (ca.getMarkerDay(d0) !== ca.getMarkerDay(d1)) { |
|
return 2; |
|
} |
|
if (timeAsMs(d0) !== timeAsMs(d1)) { |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
function computePartialFormattingOptions(options, biggestUnit) { |
|
var partialOptions = {}; |
|
for (var name_2 in options) { |
|
if (!(name_2 in STANDARD_DATE_PROP_SEVERITIES) || // not a date part prop (like timeZone) |
|
STANDARD_DATE_PROP_SEVERITIES[name_2] <= biggestUnit) { |
|
partialOptions[name_2] = options[name_2]; |
|
} |
|
} |
|
return partialOptions; |
|
} |
|
function findCommonInsertion(full0, partial0, full1, partial1) { |
|
var i0 = 0; |
|
while (i0 < full0.length) { |
|
var found0 = full0.indexOf(partial0, i0); |
|
if (found0 === -1) { |
|
break; |
|
} |
|
var before0 = full0.substr(0, found0); |
|
i0 = found0 + partial0.length; |
|
var after0 = full0.substr(i0); |
|
var i1 = 0; |
|
while (i1 < full1.length) { |
|
var found1 = full1.indexOf(partial1, i1); |
|
if (found1 === -1) { |
|
break; |
|
} |
|
var before1 = full1.substr(0, found1); |
|
i1 = found1 + partial1.length; |
|
var after1 = full1.substr(i1); |
|
if (before0 === before1 && after0 === after1) { |
|
return { |
|
before: before0, |
|
after: after0, |
|
}; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
function expandZonedMarker(dateInfo, calendarSystem) { |
|
var a = calendarSystem.markerToArray(dateInfo.marker); |
|
return { |
|
marker: dateInfo.marker, |
|
timeZoneOffset: dateInfo.timeZoneOffset, |
|
array: a, |
|
year: a[0], |
|
month: a[1], |
|
day: a[2], |
|
hour: a[3], |
|
minute: a[4], |
|
second: a[5], |
|
millisecond: a[6], |
|
}; |
|
} |
|
|
|
function createVerboseFormattingArg(start, end, context, betterDefaultSeparator) { |
|
var startInfo = expandZonedMarker(start, context.calendarSystem); |
|
var endInfo = end ? expandZonedMarker(end, context.calendarSystem) : null; |
|
return { |
|
date: startInfo, |
|
start: startInfo, |
|
end: endInfo, |
|
timeZone: context.timeZone, |
|
localeCodes: context.locale.codes, |
|
defaultSeparator: betterDefaultSeparator || context.defaultSeparator, |
|
}; |
|
} |
|
|
|
/* |
|
TODO: fix the terminology of "formatter" vs "formatting func" |
|
*/ |
|
/* |
|
At the time of instantiation, this object does not know which cmd-formatting system it will use. |
|
It receives this at the time of formatting, as a setting. |
|
*/ |
|
var CmdFormatter = /** @class */ (function () { |
|
function CmdFormatter(cmdStr) { |
|
this.cmdStr = cmdStr; |
|
} |
|
CmdFormatter.prototype.format = function (date, context, betterDefaultSeparator) { |
|
return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(date, null, context, betterDefaultSeparator)); |
|
}; |
|
CmdFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) { |
|
return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(start, end, context, betterDefaultSeparator)); |
|
}; |
|
return CmdFormatter; |
|
}()); |
|
|
|
var FuncFormatter = /** @class */ (function () { |
|
function FuncFormatter(func) { |
|
this.func = func; |
|
} |
|
FuncFormatter.prototype.format = function (date, context, betterDefaultSeparator) { |
|
return this.func(createVerboseFormattingArg(date, null, context, betterDefaultSeparator)); |
|
}; |
|
FuncFormatter.prototype.formatRange = function (start, end, context, betterDefaultSeparator) { |
|
return this.func(createVerboseFormattingArg(start, end, context, betterDefaultSeparator)); |
|
}; |
|
return FuncFormatter; |
|
}()); |
|
|
|
function createFormatter(input) { |
|
if (typeof input === 'object' && input) { // non-null object |
|
return new NativeFormatter(input); |
|
} |
|
if (typeof input === 'string') { |
|
return new CmdFormatter(input); |
|
} |
|
if (typeof input === 'function') { |
|
return new FuncFormatter(input); |
|
} |
|
return null; |
|
} |
|
|
|
// base options |
|
// ------------ |
|
var BASE_OPTION_REFINERS = { |
|
navLinkDayClick: identity, |
|
navLinkWeekClick: identity, |
|
duration: createDuration, |
|
bootstrapFontAwesome: identity, |
|
buttonIcons: identity, |
|
customButtons: identity, |
|
defaultAllDayEventDuration: createDuration, |
|
defaultTimedEventDuration: createDuration, |
|
nextDayThreshold: createDuration, |
|
scrollTime: createDuration, |
|
slotMinTime: createDuration, |
|
slotMaxTime: createDuration, |
|
dayPopoverFormat: createFormatter, |
|
slotDuration: createDuration, |
|
snapDuration: createDuration, |
|
headerToolbar: identity, |
|
footerToolbar: identity, |
|
defaultRangeSeparator: String, |
|
titleRangeSeparator: String, |
|
forceEventDuration: Boolean, |
|
dayHeaders: Boolean, |
|
dayHeaderFormat: createFormatter, |
|
dayHeaderClassNames: identity, |
|
dayHeaderContent: identity, |
|
dayHeaderDidMount: identity, |
|
dayHeaderWillUnmount: identity, |
|
dayCellClassNames: identity, |
|
dayCellContent: identity, |
|
dayCellDidMount: identity, |
|
dayCellWillUnmount: identity, |
|
initialView: String, |
|
aspectRatio: Number, |
|
weekends: Boolean, |
|
weekNumberCalculation: identity, |
|
weekNumbers: Boolean, |
|
weekNumberClassNames: identity, |
|
weekNumberContent: identity, |
|
weekNumberDidMount: identity, |
|
weekNumberWillUnmount: identity, |
|
editable: Boolean, |
|
viewClassNames: identity, |
|
viewDidMount: identity, |
|
viewWillUnmount: identity, |
|
nowIndicator: Boolean, |
|
nowIndicatorClassNames: identity, |
|
nowIndicatorContent: identity, |
|
nowIndicatorDidMount: identity, |
|
nowIndicatorWillUnmount: identity, |
|
showNonCurrentDates: Boolean, |
|
lazyFetching: Boolean, |
|
startParam: String, |
|
endParam: String, |
|
timeZoneParam: String, |
|
timeZone: String, |
|
locales: identity, |
|
locale: identity, |
|
themeSystem: String, |
|
dragRevertDuration: Number, |
|
dragScroll: Boolean, |
|
allDayMaintainDuration: Boolean, |
|
unselectAuto: Boolean, |
|
dropAccept: identity, |
|
eventOrder: parseFieldSpecs, |
|
handleWindowResize: Boolean, |
|
windowResizeDelay: Number, |
|
longPressDelay: Number, |
|
eventDragMinDistance: Number, |
|
expandRows: Boolean, |
|
height: identity, |
|
contentHeight: identity, |
|
direction: String, |
|
weekNumberFormat: createFormatter, |
|
eventResizableFromStart: Boolean, |
|
displayEventTime: Boolean, |
|
displayEventEnd: Boolean, |
|
weekText: String, |
|
progressiveEventRendering: Boolean, |
|
businessHours: identity, |
|
initialDate: identity, |
|
now: identity, |
|
eventDataTransform: identity, |
|
stickyHeaderDates: identity, |
|
stickyFooterScrollbar: identity, |
|
viewHeight: identity, |
|
defaultAllDay: Boolean, |
|
eventSourceFailure: identity, |
|
eventSourceSuccess: identity, |
|
eventDisplay: String, |
|
eventStartEditable: Boolean, |
|
eventDurationEditable: Boolean, |
|
eventOverlap: identity, |
|
eventConstraint: identity, |
|
eventAllow: identity, |
|
eventBackgroundColor: String, |
|
eventBorderColor: String, |
|
eventTextColor: String, |
|
eventColor: String, |
|
eventClassNames: identity, |
|
eventContent: identity, |
|
eventDidMount: identity, |
|
eventWillUnmount: identity, |
|
selectConstraint: identity, |
|
selectOverlap: identity, |
|
selectAllow: identity, |
|
droppable: Boolean, |
|
unselectCancel: String, |
|
slotLabelFormat: identity, |
|
slotLaneClassNames: identity, |
|
slotLaneContent: identity, |
|
slotLaneDidMount: identity, |
|
slotLaneWillUnmount: identity, |
|
slotLabelClassNames: identity, |
|
slotLabelContent: identity, |
|
slotLabelDidMount: identity, |
|
slotLabelWillUnmount: identity, |
|
dayMaxEvents: identity, |
|
dayMaxEventRows: identity, |
|
dayMinWidth: Number, |
|
slotLabelInterval: createDuration, |
|
allDayText: String, |
|
allDayClassNames: identity, |
|
allDayContent: identity, |
|
allDayDidMount: identity, |
|
allDayWillUnmount: identity, |
|
slotMinWidth: Number, |
|
navLinks: Boolean, |
|
eventTimeFormat: createFormatter, |
|
rerenderDelay: Number, |
|
moreLinkText: identity, |
|
selectMinDistance: Number, |
|
selectable: Boolean, |
|
selectLongPressDelay: Number, |
|
eventLongPressDelay: Number, |
|
selectMirror: Boolean, |
|
eventMinHeight: Number, |
|
slotEventOverlap: Boolean, |
|
plugins: identity, |
|
firstDay: Number, |
|
dayCount: Number, |
|
dateAlignment: String, |
|
dateIncrement: createDuration, |
|
hiddenDays: identity, |
|
monthMode: Boolean, |
|
fixedWeekCount: Boolean, |
|
validRange: identity, |
|
visibleRange: identity, |
|
titleFormat: identity, |
|
// only used by list-view, but languages define the value, so we need it in base options |
|
noEventsText: String, |
|
}; |
|
// do NOT give a type here. need `typeof BASE_OPTION_DEFAULTS` to give real results. |
|
// raw values. |
|
var BASE_OPTION_DEFAULTS = { |
|
eventDisplay: 'auto', |
|
defaultRangeSeparator: ' - ', |
|
titleRangeSeparator: ' \u2013 ', |
|
defaultTimedEventDuration: '01:00:00', |
|
defaultAllDayEventDuration: { day: 1 }, |
|
forceEventDuration: false, |
|
nextDayThreshold: '00:00:00', |
|
dayHeaders: true, |
|
initialView: '', |
|
aspectRatio: 1.35, |
|
headerToolbar: { |
|
start: 'title', |
|
center: '', |
|
end: 'today prev,next', |
|
}, |
|
weekends: true, |
|
weekNumbers: false, |
|
weekNumberCalculation: 'local', |
|
editable: false, |
|
nowIndicator: false, |
|
scrollTime: '06:00:00', |
|
slotMinTime: '00:00:00', |
|
slotMaxTime: '24:00:00', |
|
showNonCurrentDates: true, |
|
lazyFetching: true, |
|
startParam: 'start', |
|
endParam: 'end', |
|
timeZoneParam: 'timeZone', |
|
timeZone: 'local', |
|
locales: [], |
|
locale: '', |
|
themeSystem: 'standard', |
|
dragRevertDuration: 500, |
|
dragScroll: true, |
|
allDayMaintainDuration: false, |
|
unselectAuto: true, |
|
dropAccept: '*', |
|
eventOrder: 'start,-duration,allDay,title', |
|
dayPopoverFormat: { month: 'long', day: 'numeric', year: 'numeric' }, |
|
handleWindowResize: true, |
|
windowResizeDelay: 100, |
|
longPressDelay: 1000, |
|
eventDragMinDistance: 5, |
|
expandRows: false, |
|
navLinks: false, |
|
selectable: false, |
|
}; |
|
// calendar listeners |
|
// ------------------ |
|
var CALENDAR_LISTENER_REFINERS = { |
|
datesSet: identity, |
|
eventsSet: identity, |
|
eventAdd: identity, |
|
eventChange: identity, |
|
eventRemove: identity, |
|
windowResize: identity, |
|
eventClick: identity, |
|
eventMouseEnter: identity, |
|
eventMouseLeave: identity, |
|
select: identity, |
|
unselect: identity, |
|
loading: identity, |
|
// internal |
|
_unmount: identity, |
|
_beforeprint: identity, |
|
_afterprint: identity, |
|
_noEventDrop: identity, |
|
_noEventResize: identity, |
|
_resize: identity, |
|
_scrollRequest: identity, |
|
}; |
|
// calendar-specific options |
|
// ------------------------- |
|
var CALENDAR_OPTION_REFINERS = { |
|
buttonText: identity, |
|
views: identity, |
|
plugins: identity, |
|
initialEvents: identity, |
|
events: identity, |
|
eventSources: identity, |
|
}; |
|
var COMPLEX_OPTION_COMPARATORS = { |
|
headerToolbar: isBoolComplexEqual, |
|
footerToolbar: isBoolComplexEqual, |
|
buttonText: isBoolComplexEqual, |
|
buttonIcons: isBoolComplexEqual, |
|
}; |
|
function isBoolComplexEqual(a, b) { |
|
if (typeof a === 'object' && typeof b === 'object' && a && b) { // both non-null objects |
|
return isPropsEqual(a, b); |
|
} |
|
return a === b; |
|
} |
|
// view-specific options |
|
// --------------------- |
|
var VIEW_OPTION_REFINERS = { |
|
type: String, |
|
component: identity, |
|
buttonText: String, |
|
buttonTextKey: String, |
|
dateProfileGeneratorClass: identity, |
|
usesMinMaxTime: Boolean, |
|
classNames: identity, |
|
content: identity, |
|
didMount: identity, |
|
willUnmount: identity, |
|
}; |
|
// util funcs |
|
// ---------------------------------------------------------------------------------------------------- |
|
function mergeRawOptions(optionSets) { |
|
return mergeProps(optionSets, COMPLEX_OPTION_COMPARATORS); |
|
} |
|
function refineProps(input, refiners) { |
|
var refined = {}; |
|
var extra = {}; |
|
for (var propName in refiners) { |
|
if (propName in input) { |
|
refined[propName] = refiners[propName](input[propName]); |
|
} |
|
} |
|
for (var propName in input) { |
|
if (!(propName in refiners)) { |
|
extra[propName] = input[propName]; |
|
} |
|
} |
|
return { refined: refined, extra: extra }; |
|
} |
|
function identity(raw) { |
|
return raw; |
|
} |
|
|
|
function parseEvents(rawEvents, eventSource, context, allowOpenRange) { |
|
var eventStore = createEmptyEventStore(); |
|
var eventRefiners = buildEventRefiners(context); |
|
for (var _i = 0, rawEvents_1 = rawEvents; _i < rawEvents_1.length; _i++) { |
|
var rawEvent = rawEvents_1[_i]; |
|
var tuple = parseEvent(rawEvent, eventSource, context, allowOpenRange, eventRefiners); |
|
if (tuple) { |
|
eventTupleToStore(tuple, eventStore); |
|
} |
|
} |
|
return eventStore; |
|
} |
|
function eventTupleToStore(tuple, eventStore) { |
|
if (eventStore === void 0) { eventStore = createEmptyEventStore(); } |
|
eventStore.defs[tuple.def.defId] = tuple.def; |
|
if (tuple.instance) { |
|
eventStore.instances[tuple.instance.instanceId] = tuple.instance; |
|
} |
|
return eventStore; |
|
} |
|
// retrieves events that have the same groupId as the instance specified by `instanceId` |
|
// or they are the same as the instance. |
|
// why might instanceId not be in the store? an event from another calendar? |
|
function getRelevantEvents(eventStore, instanceId) { |
|
var instance = eventStore.instances[instanceId]; |
|
if (instance) { |
|
var def_1 = eventStore.defs[instance.defId]; |
|
// get events/instances with same group |
|
var newStore = filterEventStoreDefs(eventStore, function (lookDef) { return isEventDefsGrouped(def_1, lookDef); }); |
|
// add the original |
|
// TODO: wish we could use eventTupleToStore or something like it |
|
newStore.defs[def_1.defId] = def_1; |
|
newStore.instances[instance.instanceId] = instance; |
|
return newStore; |
|
} |
|
return createEmptyEventStore(); |
|
} |
|
function isEventDefsGrouped(def0, def1) { |
|
return Boolean(def0.groupId && def0.groupId === def1.groupId); |
|
} |
|
function createEmptyEventStore() { |
|
return { defs: {}, instances: {} }; |
|
} |
|
function mergeEventStores(store0, store1) { |
|
return { |
|
defs: __assign(__assign({}, store0.defs), store1.defs), |
|
instances: __assign(__assign({}, store0.instances), store1.instances), |
|
}; |
|
} |
|
function filterEventStoreDefs(eventStore, filterFunc) { |
|
var defs = filterHash(eventStore.defs, filterFunc); |
|
var instances = filterHash(eventStore.instances, function (instance) { return (defs[instance.defId] // still exists? |
|
); }); |
|
return { defs: defs, instances: instances }; |
|
} |
|
function excludeSubEventStore(master, sub) { |
|
var defs = master.defs, instances = master.instances; |
|
var filteredDefs = {}; |
|
var filteredInstances = {}; |
|
for (var defId in defs) { |
|
if (!sub.defs[defId]) { // not explicitly excluded |
|
filteredDefs[defId] = defs[defId]; |
|
} |
|
} |
|
for (var instanceId in instances) { |
|
if (!sub.instances[instanceId] && // not explicitly excluded |
|
filteredDefs[instances[instanceId].defId] // def wasn't filtered away |
|
) { |
|
filteredInstances[instanceId] = instances[instanceId]; |
|
} |
|
} |
|
return { |
|
defs: filteredDefs, |
|
instances: filteredInstances, |
|
}; |
|
} |
|
|
|
function normalizeConstraint(input, context) { |
|
if (Array.isArray(input)) { |
|
return parseEvents(input, null, context, true); // allowOpenRange=true |
|
} |
|
if (typeof input === 'object' && input) { // non-null object |
|
return parseEvents([input], null, context, true); // allowOpenRange=true |
|
} |
|
if (input != null) { |
|
return String(input); |
|
} |
|
return null; |
|
} |
|
|
|
function parseClassNames(raw) { |
|
if (Array.isArray(raw)) { |
|
return raw; |
|
} |
|
if (typeof raw === 'string') { |
|
return raw.split(/\s+/); |
|
} |
|
return []; |
|
} |
|
|
|
// TODO: better called "EventSettings" or "EventConfig" |
|
// TODO: move this file into structs |
|
// TODO: separate constraint/overlap/allow, because selection uses only that, not other props |
|
var EVENT_UI_REFINERS = { |
|
display: String, |
|
editable: Boolean, |
|
startEditable: Boolean, |
|
durationEditable: Boolean, |
|
constraint: identity, |
|
overlap: identity, |
|
allow: identity, |
|
className: parseClassNames, |
|
classNames: parseClassNames, |
|
color: String, |
|
backgroundColor: String, |
|
borderColor: String, |
|
textColor: String, |
|
}; |
|
var EMPTY_EVENT_UI = { |
|
display: null, |
|
startEditable: null, |
|
durationEditable: null, |
|
constraints: [], |
|
overlap: null, |
|
allows: [], |
|
backgroundColor: '', |
|
borderColor: '', |
|
textColor: '', |
|
classNames: [], |
|
}; |
|
function createEventUi(refined, context) { |
|
var constraint = normalizeConstraint(refined.constraint, context); |
|
return { |
|
display: refined.display || null, |
|
startEditable: refined.startEditable != null ? refined.startEditable : refined.editable, |
|
durationEditable: refined.durationEditable != null ? refined.durationEditable : refined.editable, |
|
constraints: constraint != null ? [constraint] : [], |
|
overlap: refined.overlap != null ? refined.overlap : null, |
|
allows: refined.allow != null ? [refined.allow] : [], |
|
backgroundColor: refined.backgroundColor || refined.color || '', |
|
borderColor: refined.borderColor || refined.color || '', |
|
textColor: refined.textColor || '', |
|
classNames: (refined.className || []).concat(refined.classNames || []), |
|
}; |
|
} |
|
// TODO: prevent against problems with <2 args! |
|
function combineEventUis(uis) { |
|
return uis.reduce(combineTwoEventUis, EMPTY_EVENT_UI); |
|
} |
|
function combineTwoEventUis(item0, item1) { |
|
return { |
|
display: item1.display != null ? item1.display : item0.display, |
|
startEditable: item1.startEditable != null ? item1.startEditable : item0.startEditable, |
|
durationEditable: item1.durationEditable != null ? item1.durationEditable : item0.durationEditable, |
|
constraints: item0.constraints.concat(item1.constraints), |
|
overlap: typeof item1.overlap === 'boolean' ? item1.overlap : item0.overlap, |
|
allows: item0.allows.concat(item1.allows), |
|
backgroundColor: item1.backgroundColor || item0.backgroundColor, |
|
borderColor: item1.borderColor || item0.borderColor, |
|
textColor: item1.textColor || item0.textColor, |
|
classNames: item0.classNames.concat(item1.classNames), |
|
}; |
|
} |
|
|
|
var EVENT_NON_DATE_REFINERS = { |
|
id: String, |
|
groupId: String, |
|
title: String, |
|
url: String, |
|
}; |
|
var EVENT_DATE_REFINERS = { |
|
start: identity, |
|
end: identity, |
|
date: identity, |
|
allDay: Boolean, |
|
}; |
|
var EVENT_REFINERS = __assign(__assign(__assign({}, EVENT_NON_DATE_REFINERS), EVENT_DATE_REFINERS), { extendedProps: identity }); |
|
function parseEvent(raw, eventSource, context, allowOpenRange, refiners) { |
|
if (refiners === void 0) { refiners = buildEventRefiners(context); } |
|
var _a = refineEventDef(raw, context, refiners), refined = _a.refined, extra = _a.extra; |
|
var defaultAllDay = computeIsDefaultAllDay(eventSource, context); |
|
var recurringRes = parseRecurring(refined, defaultAllDay, context.dateEnv, context.pluginHooks.recurringTypes); |
|
if (recurringRes) { |
|
var def = parseEventDef(refined, extra, eventSource ? eventSource.sourceId : '', recurringRes.allDay, Boolean(recurringRes.duration), context); |
|
def.recurringDef = { |
|
typeId: recurringRes.typeId, |
|
typeData: recurringRes.typeData, |
|
duration: recurringRes.duration, |
|
}; |
|
return { def: def, instance: null }; |
|
} |
|
var singleRes = parseSingle(refined, defaultAllDay, context, allowOpenRange); |
|
if (singleRes) { |
|
var def = parseEventDef(refined, extra, eventSource ? eventSource.sourceId : '', singleRes.allDay, singleRes.hasEnd, context); |
|
var instance = createEventInstance(def.defId, singleRes.range, singleRes.forcedStartTzo, singleRes.forcedEndTzo); |
|
return { def: def, instance: instance }; |
|
} |
|
return null; |
|
} |
|
function refineEventDef(raw, context, refiners) { |
|
if (refiners === void 0) { refiners = buildEventRefiners(context); } |
|
return refineProps(raw, refiners); |
|
} |
|
function buildEventRefiners(context) { |
|
return __assign(__assign(__assign({}, EVENT_UI_REFINERS), EVENT_REFINERS), context.pluginHooks.eventRefiners); |
|
} |
|
/* |
|
Will NOT populate extendedProps with the leftover properties. |
|
Will NOT populate date-related props. |
|
*/ |
|
function parseEventDef(refined, extra, sourceId, allDay, hasEnd, context) { |
|
var def = { |
|
title: refined.title || '', |
|
groupId: refined.groupId || '', |
|
publicId: refined.id || '', |
|
url: refined.url || '', |
|
recurringDef: null, |
|
defId: guid(), |
|
sourceId: sourceId, |
|
allDay: allDay, |
|
hasEnd: hasEnd, |
|
ui: createEventUi(refined, context), |
|
extendedProps: __assign(__assign({}, (refined.extendedProps || {})), extra), |
|
}; |
|
for (var _i = 0, _a = context.pluginHooks.eventDefMemberAdders; _i < _a.length; _i++) { |
|
var memberAdder = _a[_i]; |
|
__assign(def, memberAdder(refined)); |
|
} |
|
// help out EventApi from having user modify props |
|
Object.freeze(def.ui.classNames); |
|
Object.freeze(def.extendedProps); |
|
return def; |
|
} |
|
function parseSingle(refined, defaultAllDay, context, allowOpenRange) { |
|
var allDay = refined.allDay; |
|
var startMeta; |
|
var startMarker = null; |
|
var hasEnd = false; |
|
var endMeta; |
|
var endMarker = null; |
|
var startInput = refined.start != null ? refined.start : refined.date; |
|
startMeta = context.dateEnv.createMarkerMeta(startInput); |
|
if (startMeta) { |
|
startMarker = startMeta.marker; |
|
} |
|
else if (!allowOpenRange) { |
|
return null; |
|
} |
|
if (refined.end != null) { |
|
endMeta = context.dateEnv.createMarkerMeta(refined.end); |
|
} |
|
if (allDay == null) { |
|
if (defaultAllDay != null) { |
|
allDay = defaultAllDay; |
|
} |
|
else { |
|
// fall back to the date props LAST |
|
allDay = (!startMeta || startMeta.isTimeUnspecified) && |
|
(!endMeta || endMeta.isTimeUnspecified); |
|
} |
|
} |
|
if (allDay && startMarker) { |
|
startMarker = startOfDay(startMarker); |
|
} |
|
if (endMeta) { |
|
endMarker = endMeta.marker; |
|
if (allDay) { |
|
endMarker = startOfDay(endMarker); |
|
} |
|
if (startMarker && endMarker <= startMarker) { |
|
endMarker = null; |
|
} |
|
} |
|
if (endMarker) { |
|
hasEnd = true; |
|
} |
|
else if (!allowOpenRange) { |
|
hasEnd = context.options.forceEventDuration || false; |
|
endMarker = context.dateEnv.add(startMarker, allDay ? |
|
context.options.defaultAllDayEventDuration : |
|
context.options.defaultTimedEventDuration); |
|
} |
|
return { |
|
allDay: allDay, |
|
hasEnd: hasEnd, |
|
range: { start: startMarker, end: endMarker }, |
|
forcedStartTzo: startMeta ? startMeta.forcedTzo : null, |
|
forcedEndTzo: endMeta ? endMeta.forcedTzo : null, |
|
}; |
|
} |
|
function computeIsDefaultAllDay(eventSource, context) { |
|
var res = null; |
|
if (eventSource) { |
|
res = eventSource.defaultAllDay; |
|
} |
|
if (res == null) { |
|
res = context.options.defaultAllDay; |
|
} |
|
return res; |
|
} |
|
|
|
/* Date stuff that doesn't belong in datelib core |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
// given a timed range, computes an all-day range that has the same exact duration, |
|
// but whose start time is aligned with the start of the day. |
|
function computeAlignedDayRange(timedRange) { |
|
var dayCnt = Math.floor(diffDays(timedRange.start, timedRange.end)) || 1; |
|
var start = startOfDay(timedRange.start); |
|
var end = addDays(start, dayCnt); |
|
return { start: start, end: end }; |
|
} |
|
// given a timed range, computes an all-day range based on how for the end date bleeds into the next day |
|
// TODO: give nextDayThreshold a default arg |
|
function computeVisibleDayRange(timedRange, nextDayThreshold) { |
|
if (nextDayThreshold === void 0) { nextDayThreshold = createDuration(0); } |
|
var startDay = null; |
|
var endDay = null; |
|
if (timedRange.end) { |
|
endDay = startOfDay(timedRange.end); |
|
var endTimeMS = timedRange.end.valueOf() - endDay.valueOf(); // # of milliseconds into `endDay` |
|
// If the end time is actually inclusively part of the next day and is equal to or |
|
// beyond the next day threshold, adjust the end to be the exclusive end of `endDay`. |
|
// Otherwise, leaving it as inclusive will cause it to exclude `endDay`. |
|
if (endTimeMS && endTimeMS >= asRoughMs(nextDayThreshold)) { |
|
endDay = addDays(endDay, 1); |
|
} |
|
} |
|
if (timedRange.start) { |
|
startDay = startOfDay(timedRange.start); // the beginning of the day the range starts |
|
// If end is within `startDay` but not past nextDayThreshold, assign the default duration of one day. |
|
if (endDay && endDay <= startDay) { |
|
endDay = addDays(startDay, 1); |
|
} |
|
} |
|
return { start: startDay, end: endDay }; |
|
} |
|
// spans from one day into another? |
|
function isMultiDayRange(range) { |
|
var visibleRange = computeVisibleDayRange(range); |
|
return diffDays(visibleRange.start, visibleRange.end) > 1; |
|
} |
|
function diffDates(date0, date1, dateEnv, largeUnit) { |
|
if (largeUnit === 'year') { |
|
return createDuration(dateEnv.diffWholeYears(date0, date1), 'year'); |
|
} |
|
if (largeUnit === 'month') { |
|
return createDuration(dateEnv.diffWholeMonths(date0, date1), 'month'); |
|
} |
|
return diffDayAndTime(date0, date1); // returns a duration |
|
} |
|
|
|
function parseRange(input, dateEnv) { |
|
var start = null; |
|
var end = null; |
|
if (input.start) { |
|
start = dateEnv.createMarker(input.start); |
|
} |
|
if (input.end) { |
|
end = dateEnv.createMarker(input.end); |
|
} |
|
if (!start && !end) { |
|
return null; |
|
} |
|
if (start && end && end < start) { |
|
return null; |
|
} |
|
return { start: start, end: end }; |
|
} |
|
// SIDE-EFFECT: will mutate ranges. |
|
// Will return a new array result. |
|
function invertRanges(ranges, constraintRange) { |
|
var invertedRanges = []; |
|
var start = constraintRange.start; // the end of the previous range. the start of the new range |
|
var i; |
|
var dateRange; |
|
// ranges need to be in order. required for our date-walking algorithm |
|
ranges.sort(compareRanges); |
|
for (i = 0; i < ranges.length; i += 1) { |
|
dateRange = ranges[i]; |
|
// add the span of time before the event (if there is any) |
|
if (dateRange.start > start) { // compare millisecond time (skip any ambig logic) |
|
invertedRanges.push({ start: start, end: dateRange.start }); |
|
} |
|
if (dateRange.end > start) { |
|
start = dateRange.end; |
|
} |
|
} |
|
// add the span of time after the last event (if there is any) |
|
if (start < constraintRange.end) { // compare millisecond time (skip any ambig logic) |
|
invertedRanges.push({ start: start, end: constraintRange.end }); |
|
} |
|
return invertedRanges; |
|
} |
|
function compareRanges(range0, range1) { |
|
return range0.start.valueOf() - range1.start.valueOf(); // earlier ranges go first |
|
} |
|
function intersectRanges(range0, range1) { |
|
var start = range0.start, end = range0.end; |
|
var newRange = null; |
|
if (range1.start !== null) { |
|
if (start === null) { |
|
start = range1.start; |
|
} |
|
else { |
|
start = new Date(Math.max(start.valueOf(), range1.start.valueOf())); |
|
} |
|
} |
|
if (range1.end != null) { |
|
if (end === null) { |
|
end = range1.end; |
|
} |
|
else { |
|
end = new Date(Math.min(end.valueOf(), range1.end.valueOf())); |
|
} |
|
} |
|
if (start === null || end === null || start < end) { |
|
newRange = { start: start, end: end }; |
|
} |
|
return newRange; |
|
} |
|
function rangesEqual(range0, range1) { |
|
return (range0.start === null ? null : range0.start.valueOf()) === (range1.start === null ? null : range1.start.valueOf()) && |
|
(range0.end === null ? null : range0.end.valueOf()) === (range1.end === null ? null : range1.end.valueOf()); |
|
} |
|
function rangesIntersect(range0, range1) { |
|
return (range0.end === null || range1.start === null || range0.end > range1.start) && |
|
(range0.start === null || range1.end === null || range0.start < range1.end); |
|
} |
|
function rangeContainsRange(outerRange, innerRange) { |
|
return (outerRange.start === null || (innerRange.start !== null && innerRange.start >= outerRange.start)) && |
|
(outerRange.end === null || (innerRange.end !== null && innerRange.end <= outerRange.end)); |
|
} |
|
function rangeContainsMarker(range, date) { |
|
return (range.start === null || date >= range.start) && |
|
(range.end === null || date < range.end); |
|
} |
|
// If the given date is not within the given range, move it inside. |
|
// (If it's past the end, make it one millisecond before the end). |
|
function constrainMarkerToRange(date, range) { |
|
if (range.start != null && date < range.start) { |
|
return range.start; |
|
} |
|
if (range.end != null && date >= range.end) { |
|
return new Date(range.end.valueOf() - 1); |
|
} |
|
return date; |
|
} |
|
|
|
/* |
|
Specifying nextDayThreshold signals that all-day ranges should be sliced. |
|
*/ |
|
function sliceEventStore(eventStore, eventUiBases, framingRange, nextDayThreshold) { |
|
var inverseBgByGroupId = {}; |
|
var inverseBgByDefId = {}; |
|
var defByGroupId = {}; |
|
var bgRanges = []; |
|
var fgRanges = []; |
|
var eventUis = compileEventUis(eventStore.defs, eventUiBases); |
|
for (var defId in eventStore.defs) { |
|
var def = eventStore.defs[defId]; |
|
var ui = eventUis[def.defId]; |
|
if (ui.display === 'inverse-background') { |
|
if (def.groupId) { |
|
inverseBgByGroupId[def.groupId] = []; |
|
if (!defByGroupId[def.groupId]) { |
|
defByGroupId[def.groupId] = def; |
|
} |
|
} |
|
else { |
|
inverseBgByDefId[defId] = []; |
|
} |
|
} |
|
} |
|
for (var instanceId in eventStore.instances) { |
|
var instance = eventStore.instances[instanceId]; |
|
var def = eventStore.defs[instance.defId]; |
|
var ui = eventUis[def.defId]; |
|
var origRange = instance.range; |
|
var normalRange = (!def.allDay && nextDayThreshold) ? |
|
computeVisibleDayRange(origRange, nextDayThreshold) : |
|
origRange; |
|
var slicedRange = intersectRanges(normalRange, framingRange); |
|
if (slicedRange) { |
|
if (ui.display === 'inverse-background') { |
|
if (def.groupId) { |
|
inverseBgByGroupId[def.groupId].push(slicedRange); |
|
} |
|
else { |
|
inverseBgByDefId[instance.defId].push(slicedRange); |
|
} |
|
} |
|
else if (ui.display !== 'none') { |
|
(ui.display === 'background' ? bgRanges : fgRanges).push({ |
|
def: def, |
|
ui: ui, |
|
instance: instance, |
|
range: slicedRange, |
|
isStart: normalRange.start && normalRange.start.valueOf() === slicedRange.start.valueOf(), |
|
isEnd: normalRange.end && normalRange.end.valueOf() === slicedRange.end.valueOf(), |
|
}); |
|
} |
|
} |
|
} |
|
for (var groupId in inverseBgByGroupId) { // BY GROUP |
|
var ranges = inverseBgByGroupId[groupId]; |
|
var invertedRanges = invertRanges(ranges, framingRange); |
|
for (var _i = 0, invertedRanges_1 = invertedRanges; _i < invertedRanges_1.length; _i++) { |
|
var invertedRange = invertedRanges_1[_i]; |
|
var def = defByGroupId[groupId]; |
|
var ui = eventUis[def.defId]; |
|
bgRanges.push({ |
|
def: def, |
|
ui: ui, |
|
instance: null, |
|
range: invertedRange, |
|
isStart: false, |
|
isEnd: false, |
|
}); |
|
} |
|
} |
|
for (var defId in inverseBgByDefId) { |
|
var ranges = inverseBgByDefId[defId]; |
|
var invertedRanges = invertRanges(ranges, framingRange); |
|
for (var _a = 0, invertedRanges_2 = invertedRanges; _a < invertedRanges_2.length; _a++) { |
|
var invertedRange = invertedRanges_2[_a]; |
|
bgRanges.push({ |
|
def: eventStore.defs[defId], |
|
ui: eventUis[defId], |
|
instance: null, |
|
range: invertedRange, |
|
isStart: false, |
|
isEnd: false, |
|
}); |
|
} |
|
} |
|
return { bg: bgRanges, fg: fgRanges }; |
|
} |
|
function hasBgRendering(def) { |
|
return def.ui.display === 'background' || def.ui.display === 'inverse-background'; |
|
} |
|
function setElSeg(el, seg) { |
|
el.fcSeg = seg; |
|
} |
|
function getElSeg(el) { |
|
return el.fcSeg || |
|
el.parentNode.fcSeg || // for the harness |
|
null; |
|
} |
|
// event ui computation |
|
function compileEventUis(eventDefs, eventUiBases) { |
|
return mapHash(eventDefs, function (eventDef) { return compileEventUi(eventDef, eventUiBases); }); |
|
} |
|
function compileEventUi(eventDef, eventUiBases) { |
|
var uis = []; |
|
if (eventUiBases['']) { |
|
uis.push(eventUiBases['']); |
|
} |
|
if (eventUiBases[eventDef.defId]) { |
|
uis.push(eventUiBases[eventDef.defId]); |
|
} |
|
uis.push(eventDef.ui); |
|
return combineEventUis(uis); |
|
} |
|
function sortEventSegs(segs, eventOrderSpecs) { |
|
var objs = segs.map(buildSegCompareObj); |
|
objs.sort(function (obj0, obj1) { return compareByFieldSpecs(obj0, obj1, eventOrderSpecs); }); |
|
return objs.map(function (c) { return c._seg; }); |
|
} |
|
// returns a object with all primitive props that can be compared |
|
function buildSegCompareObj(seg) { |
|
var eventRange = seg.eventRange; |
|
var eventDef = eventRange.def; |
|
var range = eventRange.instance ? eventRange.instance.range : eventRange.range; |
|
var start = range.start ? range.start.valueOf() : 0; // TODO: better support for open-range events |
|
var end = range.end ? range.end.valueOf() : 0; // " |
|
return __assign(__assign(__assign({}, eventDef.extendedProps), eventDef), { id: eventDef.publicId, start: start, |
|
end: end, duration: end - start, allDay: Number(eventDef.allDay), _seg: seg }); |
|
} |
|
function computeSegDraggable(seg, context) { |
|
var pluginHooks = context.pluginHooks; |
|
var transformers = pluginHooks.isDraggableTransformers; |
|
var _a = seg.eventRange, def = _a.def, ui = _a.ui; |
|
var val = ui.startEditable; |
|
for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) { |
|
var transformer = transformers_1[_i]; |
|
val = transformer(val, def, ui, context); |
|
} |
|
return val; |
|
} |
|
function computeSegStartResizable(seg, context) { |
|
return seg.isStart && seg.eventRange.ui.durationEditable && context.options.eventResizableFromStart; |
|
} |
|
function computeSegEndResizable(seg, context) { |
|
return seg.isEnd && seg.eventRange.ui.durationEditable; |
|
} |
|
function buildSegTimeText(seg, timeFormat, context, defaultDisplayEventTime, // defaults to true |
|
defaultDisplayEventEnd, // defaults to true |
|
startOverride, endOverride) { |
|
var dateEnv = context.dateEnv, options = context.options; |
|
var displayEventTime = options.displayEventTime, displayEventEnd = options.displayEventEnd; |
|
var eventDef = seg.eventRange.def; |
|
var eventInstance = seg.eventRange.instance; |
|
if (displayEventTime == null) { |
|
displayEventTime = defaultDisplayEventTime !== false; |
|
} |
|
if (displayEventEnd == null) { |
|
displayEventEnd = defaultDisplayEventEnd !== false; |
|
} |
|
if (displayEventTime && !eventDef.allDay && (seg.isStart || seg.isEnd)) { |
|
var segStart = startOverride || (seg.isStart ? eventInstance.range.start : (seg.start || seg.eventRange.range.start)); |
|
var segEnd = endOverride || (seg.isEnd ? eventInstance.range.end : (seg.end || seg.eventRange.range.end)); |
|
if (displayEventEnd && eventDef.hasEnd) { |
|
return dateEnv.formatRange(segStart, segEnd, timeFormat, { |
|
forcedStartTzo: startOverride ? null : eventInstance.forcedStartTzo, |
|
forcedEndTzo: endOverride ? null : eventInstance.forcedEndTzo, |
|
}); |
|
} |
|
return dateEnv.format(segStart, timeFormat, { |
|
forcedTzo: startOverride ? null : eventInstance.forcedStartTzo, |
|
}); |
|
} |
|
return ''; |
|
} |
|
function getSegMeta(seg, todayRange, nowDate) { |
|
var segRange = seg.eventRange.range; |
|
return { |
|
isPast: segRange.end < (nowDate || todayRange.start), |
|
isFuture: segRange.start >= (nowDate || todayRange.end), |
|
isToday: todayRange && rangeContainsMarker(todayRange, segRange.start), |
|
}; |
|
} |
|
function getEventClassNames(props) { |
|
var classNames = ['fc-event']; |
|
if (props.isMirror) { |
|
classNames.push('fc-event-mirror'); |
|
} |
|
if (props.isDraggable) { |
|
classNames.push('fc-event-draggable'); |
|
} |
|
if (props.isStartResizable || props.isEndResizable) { |
|
classNames.push('fc-event-resizable'); |
|
} |
|
if (props.isDragging) { |
|
classNames.push('fc-event-dragging'); |
|
} |
|
if (props.isResizing) { |
|
classNames.push('fc-event-resizing'); |
|
} |
|
if (props.isSelected) { |
|
classNames.push('fc-event-selected'); |
|
} |
|
if (props.isStart) { |
|
classNames.push('fc-event-start'); |
|
} |
|
if (props.isEnd) { |
|
classNames.push('fc-event-end'); |
|
} |
|
if (props.isPast) { |
|
classNames.push('fc-event-past'); |
|
} |
|
if (props.isToday) { |
|
classNames.push('fc-event-today'); |
|
} |
|
if (props.isFuture) { |
|
classNames.push('fc-event-future'); |
|
} |
|
return classNames; |
|
} |
|
function buildEventRangeKey(eventRange) { |
|
return eventRange.instance |
|
? eventRange.instance.instanceId |
|
: eventRange.def.defId + ":" + eventRange.range.start.toISOString(); |
|
// inverse-background events don't have specific instances. TODO: better solution |
|
} |
|
|
|
var STANDARD_PROPS = { |
|
start: identity, |
|
end: identity, |
|
allDay: Boolean, |
|
}; |
|
function parseDateSpan(raw, dateEnv, defaultDuration) { |
|
var span = parseOpenDateSpan(raw, dateEnv); |
|
var range = span.range; |
|
if (!range.start) { |
|
return null; |
|
} |
|
if (!range.end) { |
|
if (defaultDuration == null) { |
|
return null; |
|
} |
|
range.end = dateEnv.add(range.start, defaultDuration); |
|
} |
|
return span; |
|
} |
|
/* |
|
TODO: somehow combine with parseRange? |
|
Will return null if the start/end props were present but parsed invalidly. |
|
*/ |
|
function parseOpenDateSpan(raw, dateEnv) { |
|
var _a = refineProps(raw, STANDARD_PROPS), standardProps = _a.refined, extra = _a.extra; |
|
var startMeta = standardProps.start ? dateEnv.createMarkerMeta(standardProps.start) : null; |
|
var endMeta = standardProps.end ? dateEnv.createMarkerMeta(standardProps.end) : null; |
|
var allDay = standardProps.allDay; |
|
if (allDay == null) { |
|
allDay = (startMeta && startMeta.isTimeUnspecified) && |
|
(!endMeta || endMeta.isTimeUnspecified); |
|
} |
|
return __assign({ range: { |
|
start: startMeta ? startMeta.marker : null, |
|
end: endMeta ? endMeta.marker : null, |
|
}, allDay: allDay }, extra); |
|
} |
|
function isDateSpansEqual(span0, span1) { |
|
return rangesEqual(span0.range, span1.range) && |
|
span0.allDay === span1.allDay && |
|
isSpanPropsEqual(span0, span1); |
|
} |
|
// the NON-DATE-RELATED props |
|
function isSpanPropsEqual(span0, span1) { |
|
for (var propName in span1) { |
|
if (propName !== 'range' && propName !== 'allDay') { |
|
if (span0[propName] !== span1[propName]) { |
|
return false; |
|
} |
|
} |
|
} |
|
// are there any props that span0 has that span1 DOESN'T have? |
|
// both have range/allDay, so no need to special-case. |
|
for (var propName in span0) { |
|
if (!(propName in span1)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
function buildDateSpanApi(span, dateEnv) { |
|
return __assign(__assign({}, buildRangeApi(span.range, dateEnv, span.allDay)), { allDay: span.allDay }); |
|
} |
|
function buildRangeApiWithTimeZone(range, dateEnv, omitTime) { |
|
return __assign(__assign({}, buildRangeApi(range, dateEnv, omitTime)), { timeZone: dateEnv.timeZone }); |
|
} |
|
function buildRangeApi(range, dateEnv, omitTime) { |
|
return { |
|
start: dateEnv.toDate(range.start), |
|
end: dateEnv.toDate(range.end), |
|
startStr: dateEnv.formatIso(range.start, { omitTime: omitTime }), |
|
endStr: dateEnv.formatIso(range.end, { omitTime: omitTime }), |
|
}; |
|
} |
|
function fabricateEventRange(dateSpan, eventUiBases, context) { |
|
var res = refineEventDef({ editable: false }, context); |
|
var def = parseEventDef(res.refined, res.extra, '', // sourceId |
|
dateSpan.allDay, true, // hasEnd |
|
context); |
|
return { |
|
def: def, |
|
ui: compileEventUi(def, eventUiBases), |
|
instance: createEventInstance(def.defId, dateSpan.range), |
|
range: dateSpan.range, |
|
isStart: true, |
|
isEnd: true, |
|
}; |
|
} |
|
|
|
function triggerDateSelect(selection, pev, context) { |
|
context.emitter.trigger('select', __assign(__assign({}, buildDateSpanApiWithContext(selection, context)), { jsEvent: pev ? pev.origEvent : null, view: context.viewApi || context.calendarApi.view })); |
|
} |
|
function triggerDateUnselect(pev, context) { |
|
context.emitter.trigger('unselect', { |
|
jsEvent: pev ? pev.origEvent : null, |
|
view: context.viewApi || context.calendarApi.view, |
|
}); |
|
} |
|
function buildDateSpanApiWithContext(dateSpan, context) { |
|
var props = {}; |
|
for (var _i = 0, _a = context.pluginHooks.dateSpanTransforms; _i < _a.length; _i++) { |
|
var transform = _a[_i]; |
|
__assign(props, transform(dateSpan, context)); |
|
} |
|
__assign(props, buildDateSpanApi(dateSpan, context.dateEnv)); |
|
return props; |
|
} |
|
// Given an event's allDay status and start date, return what its fallback end date should be. |
|
// TODO: rename to computeDefaultEventEnd |
|
function getDefaultEventEnd(allDay, marker, context) { |
|
var dateEnv = context.dateEnv, options = context.options; |
|
var end = marker; |
|
if (allDay) { |
|
end = startOfDay(end); |
|
end = dateEnv.add(end, options.defaultAllDayEventDuration); |
|
} |
|
else { |
|
end = dateEnv.add(end, options.defaultTimedEventDuration); |
|
} |
|
return end; |
|
} |
|
|
|
// applies the mutation to ALL defs/instances within the event store |
|
function applyMutationToEventStore(eventStore, eventConfigBase, mutation, context) { |
|
var eventConfigs = compileEventUis(eventStore.defs, eventConfigBase); |
|
var dest = createEmptyEventStore(); |
|
for (var defId in eventStore.defs) { |
|
var def = eventStore.defs[defId]; |
|
dest.defs[defId] = applyMutationToEventDef(def, eventConfigs[defId], mutation, context); |
|
} |
|
for (var instanceId in eventStore.instances) { |
|
var instance = eventStore.instances[instanceId]; |
|
var def = dest.defs[instance.defId]; // important to grab the newly modified def |
|
dest.instances[instanceId] = applyMutationToEventInstance(instance, def, eventConfigs[instance.defId], mutation, context); |
|
} |
|
return dest; |
|
} |
|
function applyMutationToEventDef(eventDef, eventConfig, mutation, context) { |
|
var standardProps = mutation.standardProps || {}; |
|
// if hasEnd has not been specified, guess a good value based on deltas. |
|
// if duration will change, there's no way the default duration will persist, |
|
// and thus, we need to mark the event as having a real end |
|
if (standardProps.hasEnd == null && |
|
eventConfig.durationEditable && |
|
(mutation.startDelta || mutation.endDelta)) { |
|
standardProps.hasEnd = true; // TODO: is this mutation okay? |
|
} |
|
var copy = __assign(__assign(__assign({}, eventDef), standardProps), { ui: __assign(__assign({}, eventDef.ui), standardProps.ui) }); |
|
if (mutation.extendedProps) { |
|
copy.extendedProps = __assign(__assign({}, copy.extendedProps), mutation.extendedProps); |
|
} |
|
for (var _i = 0, _a = context.pluginHooks.eventDefMutationAppliers; _i < _a.length; _i++) { |
|
var applier = _a[_i]; |
|
applier(copy, mutation, context); |
|
} |
|
if (!copy.hasEnd && context.options.forceEventDuration) { |
|
copy.hasEnd = true; |
|
} |
|
return copy; |
|
} |
|
function applyMutationToEventInstance(eventInstance, eventDef, // must first be modified by applyMutationToEventDef |
|
eventConfig, mutation, context) { |
|
var dateEnv = context.dateEnv; |
|
var forceAllDay = mutation.standardProps && mutation.standardProps.allDay === true; |
|
var clearEnd = mutation.standardProps && mutation.standardProps.hasEnd === false; |
|
var copy = __assign({}, eventInstance); |
|
if (forceAllDay) { |
|
copy.range = computeAlignedDayRange(copy.range); |
|
} |
|
if (mutation.datesDelta && eventConfig.startEditable) { |
|
copy.range = { |
|
start: dateEnv.add(copy.range.start, mutation.datesDelta), |
|
end: dateEnv.add(copy.range.end, mutation.datesDelta), |
|
}; |
|
} |
|
if (mutation.startDelta && eventConfig.durationEditable) { |
|
copy.range = { |
|
start: dateEnv.add(copy.range.start, mutation.startDelta), |
|
end: copy.range.end, |
|
}; |
|
} |
|
if (mutation.endDelta && eventConfig.durationEditable) { |
|
copy.range = { |
|
start: copy.range.start, |
|
end: dateEnv.add(copy.range.end, mutation.endDelta), |
|
}; |
|
} |
|
if (clearEnd) { |
|
copy.range = { |
|
start: copy.range.start, |
|
end: getDefaultEventEnd(eventDef.allDay, copy.range.start, context), |
|
}; |
|
} |
|
// in case event was all-day but the supplied deltas were not |
|
// better util for this? |
|
if (eventDef.allDay) { |
|
copy.range = { |
|
start: startOfDay(copy.range.start), |
|
end: startOfDay(copy.range.end), |
|
}; |
|
} |
|
// handle invalid durations |
|
if (copy.range.end < copy.range.start) { |
|
copy.range.end = getDefaultEventEnd(eventDef.allDay, copy.range.start, context); |
|
} |
|
return copy; |
|
} |
|
|
|
// no public types yet. when there are, export from: |
|
// import {} from './api-type-deps' |
|
var ViewApi = /** @class */ (function () { |
|
function ViewApi(type, getCurrentData, dateEnv) { |
|
this.type = type; |
|
this.getCurrentData = getCurrentData; |
|
this.dateEnv = dateEnv; |
|
} |
|
Object.defineProperty(ViewApi.prototype, "calendar", { |
|
get: function () { |
|
return this.getCurrentData().calendarApi; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(ViewApi.prototype, "title", { |
|
get: function () { |
|
return this.getCurrentData().viewTitle; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(ViewApi.prototype, "activeStart", { |
|
get: function () { |
|
return this.dateEnv.toDate(this.getCurrentData().dateProfile.activeRange.start); |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(ViewApi.prototype, "activeEnd", { |
|
get: function () { |
|
return this.dateEnv.toDate(this.getCurrentData().dateProfile.activeRange.end); |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(ViewApi.prototype, "currentStart", { |
|
get: function () { |
|
return this.dateEnv.toDate(this.getCurrentData().dateProfile.currentRange.start); |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(ViewApi.prototype, "currentEnd", { |
|
get: function () { |
|
return this.dateEnv.toDate(this.getCurrentData().dateProfile.currentRange.end); |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
ViewApi.prototype.getOption = function (name) { |
|
return this.getCurrentData().options[name]; // are the view-specific options |
|
}; |
|
return ViewApi; |
|
}()); |
|
|
|
var EVENT_SOURCE_REFINERS = { |
|
id: String, |
|
defaultAllDay: Boolean, |
|
url: String, |
|
format: String, |
|
events: identity, |
|
eventDataTransform: identity, |
|
// for any network-related sources |
|
success: identity, |
|
failure: identity, |
|
}; |
|
function parseEventSource(raw, context, refiners) { |
|
if (refiners === void 0) { refiners = buildEventSourceRefiners(context); } |
|
var rawObj; |
|
if (typeof raw === 'string') { |
|
rawObj = { url: raw }; |
|
} |
|
else if (typeof raw === 'function' || Array.isArray(raw)) { |
|
rawObj = { events: raw }; |
|
} |
|
else if (typeof raw === 'object' && raw) { // not null |
|
rawObj = raw; |
|
} |
|
if (rawObj) { |
|
var _a = refineProps(rawObj, refiners), refined = _a.refined, extra = _a.extra; |
|
var metaRes = buildEventSourceMeta(refined, context); |
|
if (metaRes) { |
|
return { |
|
_raw: raw, |
|
isFetching: false, |
|
latestFetchId: '', |
|
fetchRange: null, |
|
defaultAllDay: refined.defaultAllDay, |
|
eventDataTransform: refined.eventDataTransform, |
|
success: refined.success, |
|
failure: refined.failure, |
|
publicId: refined.id || '', |
|
sourceId: guid(), |
|
sourceDefId: metaRes.sourceDefId, |
|
meta: metaRes.meta, |
|
ui: createEventUi(refined, context), |
|
extendedProps: extra, |
|
}; |
|
} |
|
} |
|
return null; |
|
} |
|
function buildEventSourceRefiners(context) { |
|
return __assign(__assign(__assign({}, EVENT_UI_REFINERS), EVENT_SOURCE_REFINERS), context.pluginHooks.eventSourceRefiners); |
|
} |
|
function buildEventSourceMeta(raw, context) { |
|
var defs = context.pluginHooks.eventSourceDefs; |
|
for (var i = defs.length - 1; i >= 0; i -= 1) { // later-added plugins take precedence |
|
var def = defs[i]; |
|
var meta = def.parseMeta(raw); |
|
if (meta) { |
|
return { sourceDefId: i, meta: meta }; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
function reduceCurrentDate(currentDate, action) { |
|
switch (action.type) { |
|
case 'CHANGE_DATE': |
|
return action.dateMarker; |
|
default: |
|
return currentDate; |
|
} |
|
} |
|
function getInitialDate(options, dateEnv) { |
|
var initialDateInput = options.initialDate; |
|
// compute the initial ambig-timezone date |
|
if (initialDateInput != null) { |
|
return dateEnv.createMarker(initialDateInput); |
|
} |
|
return getNow(options.now, dateEnv); // getNow already returns unzoned |
|
} |
|
function getNow(nowInput, dateEnv) { |
|
if (typeof nowInput === 'function') { |
|
nowInput = nowInput(); |
|
} |
|
if (nowInput == null) { |
|
return dateEnv.createNowMarker(); |
|
} |
|
return dateEnv.createMarker(nowInput); |
|
} |
|
|
|
var CalendarApi = /** @class */ (function () { |
|
function CalendarApi() { |
|
} |
|
CalendarApi.prototype.getCurrentData = function () { |
|
return this.currentDataManager.getCurrentData(); |
|
}; |
|
CalendarApi.prototype.dispatch = function (action) { |
|
return this.currentDataManager.dispatch(action); |
|
}; |
|
Object.defineProperty(CalendarApi.prototype, "view", { |
|
get: function () { return this.getCurrentData().viewApi; } // for public API |
|
, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
CalendarApi.prototype.batchRendering = function (callback) { |
|
callback(); |
|
}; |
|
CalendarApi.prototype.updateSize = function () { |
|
this.trigger('_resize', true); |
|
}; |
|
// Options |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
CalendarApi.prototype.setOption = function (name, val) { |
|
this.dispatch({ |
|
type: 'SET_OPTION', |
|
optionName: name, |
|
rawOptionValue: val, |
|
}); |
|
}; |
|
CalendarApi.prototype.getOption = function (name) { |
|
return this.currentDataManager.currentCalendarOptionsInput[name]; |
|
}; |
|
CalendarApi.prototype.getAvailableLocaleCodes = function () { |
|
return Object.keys(this.getCurrentData().availableRawLocales); |
|
}; |
|
// Trigger |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
CalendarApi.prototype.on = function (handlerName, handler) { |
|
var currentDataManager = this.currentDataManager; |
|
if (currentDataManager.currentCalendarOptionsRefiners[handlerName]) { |
|
currentDataManager.emitter.on(handlerName, handler); |
|
} |
|
else { |
|
console.warn("Unknown listener name '" + handlerName + "'"); |
|
} |
|
}; |
|
CalendarApi.prototype.off = function (handlerName, handler) { |
|
this.currentDataManager.emitter.off(handlerName, handler); |
|
}; |
|
// not meant for public use |
|
CalendarApi.prototype.trigger = function (handlerName) { |
|
var _a; |
|
var args = []; |
|
for (var _i = 1; _i < arguments.length; _i++) { |
|
args[_i - 1] = arguments[_i]; |
|
} |
|
(_a = this.currentDataManager.emitter).trigger.apply(_a, __spreadArrays([handlerName], args)); |
|
}; |
|
// View |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
CalendarApi.prototype.changeView = function (viewType, dateOrRange) { |
|
var _this = this; |
|
this.batchRendering(function () { |
|
_this.unselect(); |
|
if (dateOrRange) { |
|
if (dateOrRange.start && dateOrRange.end) { // a range |
|
_this.dispatch({ |
|
type: 'CHANGE_VIEW_TYPE', |
|
viewType: viewType, |
|
}); |
|
_this.dispatch({ |
|
type: 'SET_OPTION', |
|
optionName: 'visibleRange', |
|
rawOptionValue: dateOrRange, |
|
}); |
|
} |
|
else { |
|
var dateEnv = _this.getCurrentData().dateEnv; |
|
_this.dispatch({ |
|
type: 'CHANGE_VIEW_TYPE', |
|
viewType: viewType, |
|
dateMarker: dateEnv.createMarker(dateOrRange), |
|
}); |
|
} |
|
} |
|
else { |
|
_this.dispatch({ |
|
type: 'CHANGE_VIEW_TYPE', |
|
viewType: viewType, |
|
}); |
|
} |
|
}); |
|
}; |
|
// Forces navigation to a view for the given date. |
|
// `viewType` can be a specific view name or a generic one like "week" or "day". |
|
// needs to change |
|
CalendarApi.prototype.zoomTo = function (dateMarker, viewType) { |
|
var state = this.getCurrentData(); |
|
var spec; |
|
viewType = viewType || 'day'; // day is default zoom |
|
spec = state.viewSpecs[viewType] || this.getUnitViewSpec(viewType); |
|
this.unselect(); |
|
if (spec) { |
|
this.dispatch({ |
|
type: 'CHANGE_VIEW_TYPE', |
|
viewType: spec.type, |
|
dateMarker: dateMarker, |
|
}); |
|
} |
|
else { |
|
this.dispatch({ |
|
type: 'CHANGE_DATE', |
|
dateMarker: dateMarker, |
|
}); |
|
} |
|
}; |
|
// Given a duration singular unit, like "week" or "day", finds a matching view spec. |
|
// Preference is given to views that have corresponding buttons. |
|
CalendarApi.prototype.getUnitViewSpec = function (unit) { |
|
var _a = this.getCurrentData(), viewSpecs = _a.viewSpecs, toolbarConfig = _a.toolbarConfig; |
|
var viewTypes = [].concat(toolbarConfig.viewsWithButtons); |
|
var i; |
|
var spec; |
|
for (var viewType in viewSpecs) { |
|
viewTypes.push(viewType); |
|
} |
|
for (i = 0; i < viewTypes.length; i += 1) { |
|
spec = viewSpecs[viewTypes[i]]; |
|
if (spec) { |
|
if (spec.singleUnit === unit) { |
|
return spec; |
|
} |
|
} |
|
} |
|
return null; |
|
}; |
|
// Current Date |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
CalendarApi.prototype.prev = function () { |
|
this.unselect(); |
|
this.dispatch({ type: 'PREV' }); |
|
}; |
|
CalendarApi.prototype.next = function () { |
|
this.unselect(); |
|
this.dispatch({ type: 'NEXT' }); |
|
}; |
|
CalendarApi.prototype.prevYear = function () { |
|
var state = this.getCurrentData(); |
|
this.unselect(); |
|
this.dispatch({ |
|
type: 'CHANGE_DATE', |
|
dateMarker: state.dateEnv.addYears(state.currentDate, -1), |
|
}); |
|
}; |
|
CalendarApi.prototype.nextYear = function () { |
|
var state = this.getCurrentData(); |
|
this.unselect(); |
|
this.dispatch({ |
|
type: 'CHANGE_DATE', |
|
dateMarker: state.dateEnv.addYears(state.currentDate, 1), |
|
}); |
|
}; |
|
CalendarApi.prototype.today = function () { |
|
var state = this.getCurrentData(); |
|
this.unselect(); |
|
this.dispatch({ |
|
type: 'CHANGE_DATE', |
|
dateMarker: getNow(state.calendarOptions.now, state.dateEnv), |
|
}); |
|
}; |
|
CalendarApi.prototype.gotoDate = function (zonedDateInput) { |
|
var state = this.getCurrentData(); |
|
this.unselect(); |
|
this.dispatch({ |
|
type: 'CHANGE_DATE', |
|
dateMarker: state.dateEnv.createMarker(zonedDateInput), |
|
}); |
|
}; |
|
CalendarApi.prototype.incrementDate = function (deltaInput) { |
|
var state = this.getCurrentData(); |
|
var delta = createDuration(deltaInput); |
|
if (delta) { // else, warn about invalid input? |
|
this.unselect(); |
|
this.dispatch({ |
|
type: 'CHANGE_DATE', |
|
dateMarker: state.dateEnv.add(state.currentDate, delta), |
|
}); |
|
} |
|
}; |
|
// for external API |
|
CalendarApi.prototype.getDate = function () { |
|
var state = this.getCurrentData(); |
|
return state.dateEnv.toDate(state.currentDate); |
|
}; |
|
// Date Formatting Utils |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
CalendarApi.prototype.formatDate = function (d, formatter) { |
|
var dateEnv = this.getCurrentData().dateEnv; |
|
return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter)); |
|
}; |
|
// `settings` is for formatter AND isEndExclusive |
|
CalendarApi.prototype.formatRange = function (d0, d1, settings) { |
|
var dateEnv = this.getCurrentData().dateEnv; |
|
return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings), settings); |
|
}; |
|
CalendarApi.prototype.formatIso = function (d, omitTime) { |
|
var dateEnv = this.getCurrentData().dateEnv; |
|
return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime: omitTime }); |
|
}; |
|
// Date Selection / Event Selection / DayClick |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
// this public method receives start/end dates in any format, with any timezone |
|
// NOTE: args were changed from v3 |
|
CalendarApi.prototype.select = function (dateOrObj, endDate) { |
|
var selectionInput; |
|
if (endDate == null) { |
|
if (dateOrObj.start != null) { |
|
selectionInput = dateOrObj; |
|
} |
|
else { |
|
selectionInput = { |
|
start: dateOrObj, |
|
end: null, |
|
}; |
|
} |
|
} |
|
else { |
|
selectionInput = { |
|
start: dateOrObj, |
|
end: endDate, |
|
}; |
|
} |
|
var state = this.getCurrentData(); |
|
var selection = parseDateSpan(selectionInput, state.dateEnv, createDuration({ days: 1 })); |
|
if (selection) { // throw parse error otherwise? |
|
this.dispatch({ type: 'SELECT_DATES', selection: selection }); |
|
triggerDateSelect(selection, null, state); |
|
} |
|
}; |
|
// public method |
|
CalendarApi.prototype.unselect = function (pev) { |
|
var state = this.getCurrentData(); |
|
if (state.dateSelection) { |
|
this.dispatch({ type: 'UNSELECT_DATES' }); |
|
triggerDateUnselect(pev, state); |
|
} |
|
}; |
|
// Public Events API |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
CalendarApi.prototype.addEvent = function (eventInput, sourceInput) { |
|
if (eventInput instanceof EventApi) { |
|
var def = eventInput._def; |
|
var instance = eventInput._instance; |
|
var currentData = this.getCurrentData(); |
|
// not already present? don't want to add an old snapshot |
|
if (!currentData.eventStore.defs[def.defId]) { |
|
this.dispatch({ |
|
type: 'ADD_EVENTS', |
|
eventStore: eventTupleToStore({ def: def, instance: instance }), |
|
}); |
|
this.triggerEventAdd(eventInput); |
|
} |
|
return eventInput; |
|
} |
|
var state = this.getCurrentData(); |
|
var eventSource; |
|
if (sourceInput instanceof EventSourceApi) { |
|
eventSource = sourceInput.internalEventSource; |
|
} |
|
else if (typeof sourceInput === 'boolean') { |
|
if (sourceInput) { // true. part of the first event source |
|
eventSource = hashValuesToArray(state.eventSources)[0]; |
|
} |
|
} |
|
else if (sourceInput != null) { // an ID. accepts a number too |
|
var sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function |
|
if (!sourceApi) { |
|
console.warn("Could not find an event source with ID \"" + sourceInput + "\""); // TODO: test |
|
return null; |
|
} |
|
eventSource = sourceApi.internalEventSource; |
|
} |
|
var tuple = parseEvent(eventInput, eventSource, state, false); |
|
if (tuple) { |
|
var newEventApi = new EventApi(state, tuple.def, tuple.def.recurringDef ? null : tuple.instance); |
|
this.dispatch({ |
|
type: 'ADD_EVENTS', |
|
eventStore: eventTupleToStore(tuple), |
|
}); |
|
this.triggerEventAdd(newEventApi); |
|
return newEventApi; |
|
} |
|
return null; |
|
}; |
|
CalendarApi.prototype.triggerEventAdd = function (eventApi) { |
|
var _this = this; |
|
var emitter = this.getCurrentData().emitter; |
|
emitter.trigger('eventAdd', { |
|
event: eventApi, |
|
relatedEvents: [], |
|
revert: function () { |
|
_this.dispatch({ |
|
type: 'REMOVE_EVENTS', |
|
eventStore: eventApiToStore(eventApi), |
|
}); |
|
}, |
|
}); |
|
}; |
|
// TODO: optimize |
|
CalendarApi.prototype.getEventById = function (id) { |
|
var state = this.getCurrentData(); |
|
var _a = state.eventStore, defs = _a.defs, instances = _a.instances; |
|
id = String(id); |
|
for (var defId in defs) { |
|
var def = defs[defId]; |
|
if (def.publicId === id) { |
|
if (def.recurringDef) { |
|
return new EventApi(state, def, null); |
|
} |
|
for (var instanceId in instances) { |
|
var instance = instances[instanceId]; |
|
if (instance.defId === def.defId) { |
|
return new EventApi(state, def, instance); |
|
} |
|
} |
|
} |
|
} |
|
return null; |
|
}; |
|
CalendarApi.prototype.getEvents = function () { |
|
var currentData = this.getCurrentData(); |
|
return buildEventApis(currentData.eventStore, currentData); |
|
}; |
|
CalendarApi.prototype.removeAllEvents = function () { |
|
this.dispatch({ type: 'REMOVE_ALL_EVENTS' }); |
|
}; |
|
// Public Event Sources API |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
CalendarApi.prototype.getEventSources = function () { |
|
var state = this.getCurrentData(); |
|
var sourceHash = state.eventSources; |
|
var sourceApis = []; |
|
for (var internalId in sourceHash) { |
|
sourceApis.push(new EventSourceApi(state, sourceHash[internalId])); |
|
} |
|
return sourceApis; |
|
}; |
|
CalendarApi.prototype.getEventSourceById = function (id) { |
|
var state = this.getCurrentData(); |
|
var sourceHash = state.eventSources; |
|
id = String(id); |
|
for (var sourceId in sourceHash) { |
|
if (sourceHash[sourceId].publicId === id) { |
|
return new EventSourceApi(state, sourceHash[sourceId]); |
|
} |
|
} |
|
return null; |
|
}; |
|
CalendarApi.prototype.addEventSource = function (sourceInput) { |
|
var state = this.getCurrentData(); |
|
if (sourceInput instanceof EventSourceApi) { |
|
// not already present? don't want to add an old snapshot |
|
if (!state.eventSources[sourceInput.internalEventSource.sourceId]) { |
|
this.dispatch({ |
|
type: 'ADD_EVENT_SOURCES', |
|
sources: [sourceInput.internalEventSource], |
|
}); |
|
} |
|
return sourceInput; |
|
} |
|
var eventSource = parseEventSource(sourceInput, state); |
|
if (eventSource) { // TODO: error otherwise? |
|
this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] }); |
|
return new EventSourceApi(state, eventSource); |
|
} |
|
return null; |
|
}; |
|
CalendarApi.prototype.removeAllEventSources = function () { |
|
this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' }); |
|
}; |
|
CalendarApi.prototype.refetchEvents = function () { |
|
this.dispatch({ type: 'FETCH_EVENT_SOURCES' }); |
|
}; |
|
// Scroll |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
CalendarApi.prototype.scrollToTime = function (timeInput) { |
|
var time = createDuration(timeInput); |
|
if (time) { |
|
this.trigger('_scrollRequest', { time: time }); |
|
} |
|
}; |
|
return CalendarApi; |
|
}()); |
|
|
|
var EventApi = /** @class */ (function () { |
|
// instance will be null if expressing a recurring event that has no current instances, |
|
// OR if trying to validate an incoming external event that has no dates assigned |
|
function EventApi(context, def, instance) { |
|
this._context = context; |
|
this._def = def; |
|
this._instance = instance || null; |
|
} |
|
/* |
|
TODO: make event struct more responsible for this |
|
*/ |
|
EventApi.prototype.setProp = function (name, val) { |
|
var _a, _b; |
|
if (name in EVENT_DATE_REFINERS) { |
|
console.warn('Could not set date-related prop \'name\'. Use one of the date-related methods instead.'); |
|
} |
|
else if (name in EVENT_NON_DATE_REFINERS) { |
|
val = EVENT_NON_DATE_REFINERS[name](val); |
|
this.mutate({ |
|
standardProps: (_a = {}, _a[name] = val, _a), |
|
}); |
|
} |
|
else if (name in EVENT_UI_REFINERS) { |
|
var ui = EVENT_UI_REFINERS[name](val); |
|
if (name === 'color') { |
|
ui = { backgroundColor: val, borderColor: val }; |
|
} |
|
else if (name === 'editable') { |
|
ui = { startEditable: val, durationEditable: val }; |
|
} |
|
else { |
|
ui = (_b = {}, _b[name] = val, _b); |
|
} |
|
this.mutate({ |
|
standardProps: { ui: ui }, |
|
}); |
|
} |
|
else { |
|
console.warn("Could not set prop '" + name + "'. Use setExtendedProp instead."); |
|
} |
|
}; |
|
EventApi.prototype.setExtendedProp = function (name, val) { |
|
var _a; |
|
this.mutate({ |
|
extendedProps: (_a = {}, _a[name] = val, _a), |
|
}); |
|
}; |
|
EventApi.prototype.setStart = function (startInput, options) { |
|
if (options === void 0) { options = {}; } |
|
var dateEnv = this._context.dateEnv; |
|
var start = dateEnv.createMarker(startInput); |
|
if (start && this._instance) { // TODO: warning if parsed bad |
|
var instanceRange = this._instance.range; |
|
var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); // what if parsed bad!? |
|
if (options.maintainDuration) { |
|
this.mutate({ datesDelta: startDelta }); |
|
} |
|
else { |
|
this.mutate({ startDelta: startDelta }); |
|
} |
|
} |
|
}; |
|
EventApi.prototype.setEnd = function (endInput, options) { |
|
if (options === void 0) { options = {}; } |
|
var dateEnv = this._context.dateEnv; |
|
var end; |
|
if (endInput != null) { |
|
end = dateEnv.createMarker(endInput); |
|
if (!end) { |
|
return; // TODO: warning if parsed bad |
|
} |
|
} |
|
if (this._instance) { |
|
if (end) { |
|
var endDelta = diffDates(this._instance.range.end, end, dateEnv, options.granularity); |
|
this.mutate({ endDelta: endDelta }); |
|
} |
|
else { |
|
this.mutate({ standardProps: { hasEnd: false } }); |
|
} |
|
} |
|
}; |
|
EventApi.prototype.setDates = function (startInput, endInput, options) { |
|
if (options === void 0) { options = {}; } |
|
var dateEnv = this._context.dateEnv; |
|
var standardProps = { allDay: options.allDay }; |
|
var start = dateEnv.createMarker(startInput); |
|
var end; |
|
if (!start) { |
|
return; // TODO: warning if parsed bad |
|
} |
|
if (endInput != null) { |
|
end = dateEnv.createMarker(endInput); |
|
if (!end) { // TODO: warning if parsed bad |
|
return; |
|
} |
|
} |
|
if (this._instance) { |
|
var instanceRange = this._instance.range; |
|
// when computing the diff for an event being converted to all-day, |
|
// compute diff off of the all-day values the way event-mutation does. |
|
if (options.allDay === true) { |
|
instanceRange = computeAlignedDayRange(instanceRange); |
|
} |
|
var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); |
|
if (end) { |
|
var endDelta = diffDates(instanceRange.end, end, dateEnv, options.granularity); |
|
if (durationsEqual(startDelta, endDelta)) { |
|
this.mutate({ datesDelta: startDelta, standardProps: standardProps }); |
|
} |
|
else { |
|
this.mutate({ startDelta: startDelta, endDelta: endDelta, standardProps: standardProps }); |
|
} |
|
} |
|
else { // means "clear the end" |
|
standardProps.hasEnd = false; |
|
this.mutate({ datesDelta: startDelta, standardProps: standardProps }); |
|
} |
|
} |
|
}; |
|
EventApi.prototype.moveStart = function (deltaInput) { |
|
var delta = createDuration(deltaInput); |
|
if (delta) { // TODO: warning if parsed bad |
|
this.mutate({ startDelta: delta }); |
|
} |
|
}; |
|
EventApi.prototype.moveEnd = function (deltaInput) { |
|
var delta = createDuration(deltaInput); |
|
if (delta) { // TODO: warning if parsed bad |
|
this.mutate({ endDelta: delta }); |
|
} |
|
}; |
|
EventApi.prototype.moveDates = function (deltaInput) { |
|
var delta = createDuration(deltaInput); |
|
if (delta) { // TODO: warning if parsed bad |
|
this.mutate({ datesDelta: delta }); |
|
} |
|
}; |
|
EventApi.prototype.setAllDay = function (allDay, options) { |
|
if (options === void 0) { options = {}; } |
|
var standardProps = { allDay: allDay }; |
|
var maintainDuration = options.maintainDuration; |
|
if (maintainDuration == null) { |
|
maintainDuration = this._context.options.allDayMaintainDuration; |
|
} |
|
if (this._def.allDay !== allDay) { |
|
standardProps.hasEnd = maintainDuration; |
|
} |
|
this.mutate({ standardProps: standardProps }); |
|
}; |
|
EventApi.prototype.formatRange = function (formatInput) { |
|
var dateEnv = this._context.dateEnv; |
|
var instance = this._instance; |
|
var formatter = createFormatter(formatInput); |
|
if (this._def.hasEnd) { |
|
return dateEnv.formatRange(instance.range.start, instance.range.end, formatter, { |
|
forcedStartTzo: instance.forcedStartTzo, |
|
forcedEndTzo: instance.forcedEndTzo, |
|
}); |
|
} |
|
return dateEnv.format(instance.range.start, formatter, { |
|
forcedTzo: instance.forcedStartTzo, |
|
}); |
|
}; |
|
EventApi.prototype.mutate = function (mutation) { |
|
var instance = this._instance; |
|
if (instance) { |
|
var def = this._def; |
|
var context_1 = this._context; |
|
var eventStore_1 = context_1.getCurrentData().eventStore; |
|
var relevantEvents = getRelevantEvents(eventStore_1, instance.instanceId); |
|
var eventConfigBase = { |
|
'': { |
|
display: '', |
|
startEditable: true, |
|
durationEditable: true, |
|
constraints: [], |
|
overlap: null, |
|
allows: [], |
|
backgroundColor: '', |
|
borderColor: '', |
|
textColor: '', |
|
classNames: [], |
|
}, |
|
}; |
|
relevantEvents = applyMutationToEventStore(relevantEvents, eventConfigBase, mutation, context_1); |
|
var oldEvent = new EventApi(context_1, def, instance); // snapshot |
|
this._def = relevantEvents.defs[def.defId]; |
|
this._instance = relevantEvents.instances[instance.instanceId]; |
|
context_1.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: relevantEvents, |
|
}); |
|
context_1.emitter.trigger('eventChange', { |
|
oldEvent: oldEvent, |
|
event: this, |
|
relatedEvents: buildEventApis(relevantEvents, context_1, instance), |
|
revert: function () { |
|
context_1.dispatch({ |
|
type: 'RESET_EVENTS', |
|
eventStore: eventStore_1, |
|
}); |
|
}, |
|
}); |
|
} |
|
}; |
|
EventApi.prototype.remove = function () { |
|
var context = this._context; |
|
var asStore = eventApiToStore(this); |
|
context.dispatch({ |
|
type: 'REMOVE_EVENTS', |
|
eventStore: asStore, |
|
}); |
|
context.emitter.trigger('eventRemove', { |
|
event: this, |
|
relatedEvents: [], |
|
revert: function () { |
|
context.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: asStore, |
|
}); |
|
}, |
|
}); |
|
}; |
|
Object.defineProperty(EventApi.prototype, "source", { |
|
get: function () { |
|
var sourceId = this._def.sourceId; |
|
if (sourceId) { |
|
return new EventSourceApi(this._context, this._context.getCurrentData().eventSources[sourceId]); |
|
} |
|
return null; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "start", { |
|
get: function () { |
|
return this._instance ? |
|
this._context.dateEnv.toDate(this._instance.range.start) : |
|
null; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "end", { |
|
get: function () { |
|
return (this._instance && this._def.hasEnd) ? |
|
this._context.dateEnv.toDate(this._instance.range.end) : |
|
null; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "startStr", { |
|
get: function () { |
|
var instance = this._instance; |
|
if (instance) { |
|
return this._context.dateEnv.formatIso(instance.range.start, { |
|
omitTime: this._def.allDay, |
|
forcedTzo: instance.forcedStartTzo, |
|
}); |
|
} |
|
return ''; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "endStr", { |
|
get: function () { |
|
var instance = this._instance; |
|
if (instance && this._def.hasEnd) { |
|
return this._context.dateEnv.formatIso(instance.range.end, { |
|
omitTime: this._def.allDay, |
|
forcedTzo: instance.forcedEndTzo, |
|
}); |
|
} |
|
return ''; |
|
}, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "id", { |
|
// computable props that all access the def |
|
// TODO: find a TypeScript-compatible way to do this at scale |
|
get: function () { return this._def.publicId; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "groupId", { |
|
get: function () { return this._def.groupId; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "allDay", { |
|
get: function () { return this._def.allDay; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "title", { |
|
get: function () { return this._def.title; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "url", { |
|
get: function () { return this._def.url; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "display", { |
|
get: function () { return this._def.ui.display || 'auto'; } // bad. just normalize the type earlier |
|
, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "startEditable", { |
|
get: function () { return this._def.ui.startEditable; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "durationEditable", { |
|
get: function () { return this._def.ui.durationEditable; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "constraint", { |
|
get: function () { return this._def.ui.constraints[0] || null; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "overlap", { |
|
get: function () { return this._def.ui.overlap; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "allow", { |
|
get: function () { return this._def.ui.allows[0] || null; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "backgroundColor", { |
|
get: function () { return this._def.ui.backgroundColor; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "borderColor", { |
|
get: function () { return this._def.ui.borderColor; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "textColor", { |
|
get: function () { return this._def.ui.textColor; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "classNames", { |
|
// NOTE: user can't modify these because Object.freeze was called in event-def parsing |
|
get: function () { return this._def.ui.classNames; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Object.defineProperty(EventApi.prototype, "extendedProps", { |
|
get: function () { return this._def.extendedProps; }, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
EventApi.prototype.toPlainObject = function (settings) { |
|
if (settings === void 0) { settings = {}; } |
|
var def = this._def; |
|
var ui = def.ui; |
|
var _a = this, startStr = _a.startStr, endStr = _a.endStr; |
|
var res = {}; |
|
if (def.title) { |
|
res.title = def.title; |
|
} |
|
if (startStr) { |
|
res.start = startStr; |
|
} |
|
if (endStr) { |
|
res.end = endStr; |
|
} |
|
if (def.publicId) { |
|
res.id = def.publicId; |
|
} |
|
if (def.groupId) { |
|
res.groupId = def.groupId; |
|
} |
|
if (def.url) { |
|
res.url = def.url; |
|
} |
|
if (ui.display && ui.display !== 'auto') { |
|
res.display = ui.display; |
|
} |
|
// TODO: what about recurring-event properties??? |
|
// TODO: include startEditable/durationEditable/constraint/overlap/allow |
|
if (settings.collapseColor && ui.backgroundColor && ui.backgroundColor === ui.borderColor) { |
|
res.color = ui.backgroundColor; |
|
} |
|
else { |
|
if (ui.backgroundColor) { |
|
res.backgroundColor = ui.backgroundColor; |
|
} |
|
if (ui.borderColor) { |
|
res.borderColor = ui.borderColor; |
|
} |
|
} |
|
if (ui.textColor) { |
|
res.textColor = ui.textColor; |
|
} |
|
if (ui.classNames.length) { |
|
res.classNames = ui.classNames; |
|
} |
|
if (Object.keys(def.extendedProps).length) { |
|
if (settings.collapseExtendedProps) { |
|
__assign(res, def.extendedProps); |
|
} |
|
else { |
|
res.extendedProps = def.extendedProps; |
|
} |
|
} |
|
return res; |
|
}; |
|
EventApi.prototype.toJSON = function () { |
|
return this.toPlainObject(); |
|
}; |
|
return EventApi; |
|
}()); |
|
function eventApiToStore(eventApi) { |
|
var _a, _b; |
|
var def = eventApi._def; |
|
var instance = eventApi._instance; |
|
return { |
|
defs: (_a = {}, _a[def.defId] = def, _a), |
|
instances: instance |
|
? (_b = {}, _b[instance.instanceId] = instance, _b) : {}, |
|
}; |
|
} |
|
function buildEventApis(eventStore, context, excludeInstance) { |
|
var defs = eventStore.defs, instances = eventStore.instances; |
|
var eventApis = []; |
|
var excludeInstanceId = excludeInstance ? excludeInstance.instanceId : ''; |
|
for (var id in instances) { |
|
var instance = instances[id]; |
|
var def = defs[instance.defId]; |
|
if (instance.instanceId !== excludeInstanceId) { |
|
eventApis.push(new EventApi(context, def, instance)); |
|
} |
|
} |
|
return eventApis; |
|
} |
|
|
|
var calendarSystemClassMap = {}; |
|
function registerCalendarSystem(name, theClass) { |
|
calendarSystemClassMap[name] = theClass; |
|
} |
|
function createCalendarSystem(name) { |
|
return new calendarSystemClassMap[name](); |
|
} |
|
var GregorianCalendarSystem = /** @class */ (function () { |
|
function GregorianCalendarSystem() { |
|
} |
|
GregorianCalendarSystem.prototype.getMarkerYear = function (d) { |
|
return d.getUTCFullYear(); |
|
}; |
|
GregorianCalendarSystem.prototype.getMarkerMonth = function (d) { |
|
return d.getUTCMonth(); |
|
}; |
|
GregorianCalendarSystem.prototype.getMarkerDay = function (d) { |
|
return d.getUTCDate(); |
|
}; |
|
GregorianCalendarSystem.prototype.arrayToMarker = function (arr) { |
|
return arrayToUtcDate(arr); |
|
}; |
|
GregorianCalendarSystem.prototype.markerToArray = function (marker) { |
|
return dateToUtcArray(marker); |
|
}; |
|
return GregorianCalendarSystem; |
|
}()); |
|
registerCalendarSystem('gregory', GregorianCalendarSystem); |
|
|
|
var ISO_RE = /^\s*(\d{4})(-?(\d{2})(-?(\d{2})([T ](\d{2}):?(\d{2})(:?(\d{2})(\.(\d+))?)?(Z|(([-+])(\d{2})(:?(\d{2}))?))?)?)?)?$/; |
|
function parse(str) { |
|
var m = ISO_RE.exec(str); |
|
if (m) { |
|
var 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)); |
|
if (isValidDate(marker)) { |
|
var timeZoneOffset = null; |
|
if (m[13]) { |
|
timeZoneOffset = (m[15] === '-' ? -1 : 1) * (Number(m[16] || 0) * 60 + |
|
Number(m[18] || 0)); |
|
} |
|
return { |
|
marker: marker, |
|
isTimeUnspecified: !m[6], |
|
timeZoneOffset: timeZoneOffset, |
|
}; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
var DateEnv = /** @class */ (function () { |
|
function DateEnv(settings) { |
|
var timeZone = this.timeZone = settings.timeZone; |
|
var isNamedTimeZone = timeZone !== 'local' && timeZone !== 'UTC'; |
|
if (settings.namedTimeZoneImpl && isNamedTimeZone) { |
|
this.namedTimeZoneImpl = new settings.namedTimeZoneImpl(timeZone); |
|
} |
|
this.canComputeOffset = Boolean(!isNamedTimeZone || this.namedTimeZoneImpl); |
|
this.calendarSystem = createCalendarSystem(settings.calendarSystem); |
|
this.locale = settings.locale; |
|
this.weekDow = settings.locale.week.dow; |
|
this.weekDoy = settings.locale.week.doy; |
|
if (settings.weekNumberCalculation === 'ISO') { |
|
this.weekDow = 1; |
|
this.weekDoy = 4; |
|
} |
|
if (typeof settings.firstDay === 'number') { |
|
this.weekDow = settings.firstDay; |
|
} |
|
if (typeof settings.weekNumberCalculation === 'function') { |
|
this.weekNumberFunc = settings.weekNumberCalculation; |
|
} |
|
this.weekText = settings.weekText != null ? settings.weekText : settings.locale.options.weekText; |
|
this.cmdFormatter = settings.cmdFormatter; |
|
this.defaultSeparator = settings.defaultSeparator; |
|
} |
|
// Creating / Parsing |
|
DateEnv.prototype.createMarker = function (input) { |
|
var meta = this.createMarkerMeta(input); |
|
if (meta === null) { |
|
return null; |
|
} |
|
return meta.marker; |
|
}; |
|
DateEnv.prototype.createNowMarker = function () { |
|
if (this.canComputeOffset) { |
|
return this.timestampToMarker(new Date().valueOf()); |
|
} |
|
// if we can't compute the current date val for a timezone, |
|
// better to give the current local date vals than UTC |
|
return arrayToUtcDate(dateToLocalArray(new Date())); |
|
}; |
|
DateEnv.prototype.createMarkerMeta = function (input) { |
|
if (typeof input === 'string') { |
|
return this.parse(input); |
|
} |
|
var marker = null; |
|
if (typeof input === 'number') { |
|
marker = this.timestampToMarker(input); |
|
} |
|
else if (input instanceof Date) { |
|
input = input.valueOf(); |
|
if (!isNaN(input)) { |
|
marker = this.timestampToMarker(input); |
|
} |
|
} |
|
else if (Array.isArray(input)) { |
|
marker = arrayToUtcDate(input); |
|
} |
|
if (marker === null || !isValidDate(marker)) { |
|
return null; |
|
} |
|
return { marker: marker, isTimeUnspecified: false, forcedTzo: null }; |
|
}; |
|
DateEnv.prototype.parse = function (s) { |
|
var parts = parse(s); |
|
if (parts === null) { |
|
return null; |
|
} |
|
var marker = parts.marker; |
|
var forcedTzo = null; |
|
if (parts.timeZoneOffset !== null) { |
|
if (this.canComputeOffset) { |
|
marker = this.timestampToMarker(marker.valueOf() - parts.timeZoneOffset * 60 * 1000); |
|
} |
|
else { |
|
forcedTzo = parts.timeZoneOffset; |
|
} |
|
} |
|
return { marker: marker, isTimeUnspecified: parts.isTimeUnspecified, forcedTzo: forcedTzo }; |
|
}; |
|
// Accessors |
|
DateEnv.prototype.getYear = function (marker) { |
|
return this.calendarSystem.getMarkerYear(marker); |
|
}; |
|
DateEnv.prototype.getMonth = function (marker) { |
|
return this.calendarSystem.getMarkerMonth(marker); |
|
}; |
|
// Adding / Subtracting |
|
DateEnv.prototype.add = function (marker, dur) { |
|
var a = this.calendarSystem.markerToArray(marker); |
|
a[0] += dur.years; |
|
a[1] += dur.months; |
|
a[2] += dur.days; |
|
a[6] += dur.milliseconds; |
|
return this.calendarSystem.arrayToMarker(a); |
|
}; |
|
DateEnv.prototype.subtract = function (marker, dur) { |
|
var a = this.calendarSystem.markerToArray(marker); |
|
a[0] -= dur.years; |
|
a[1] -= dur.months; |
|
a[2] -= dur.days; |
|
a[6] -= dur.milliseconds; |
|
return this.calendarSystem.arrayToMarker(a); |
|
}; |
|
DateEnv.prototype.addYears = function (marker, n) { |
|
var a = this.calendarSystem.markerToArray(marker); |
|
a[0] += n; |
|
return this.calendarSystem.arrayToMarker(a); |
|
}; |
|
DateEnv.prototype.addMonths = function (marker, n) { |
|
var a = this.calendarSystem.markerToArray(marker); |
|
a[1] += n; |
|
return this.calendarSystem.arrayToMarker(a); |
|
}; |
|
// Diffing Whole Units |
|
DateEnv.prototype.diffWholeYears = function (m0, m1) { |
|
var calendarSystem = this.calendarSystem; |
|
if (timeAsMs(m0) === timeAsMs(m1) && |
|
calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1) && |
|
calendarSystem.getMarkerMonth(m0) === calendarSystem.getMarkerMonth(m1)) { |
|
return calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0); |
|
} |
|
return null; |
|
}; |
|
DateEnv.prototype.diffWholeMonths = function (m0, m1) { |
|
var calendarSystem = this.calendarSystem; |
|
if (timeAsMs(m0) === timeAsMs(m1) && |
|
calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1)) { |
|
return (calendarSystem.getMarkerMonth(m1) - calendarSystem.getMarkerMonth(m0)) + |
|
(calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)) * 12; |
|
} |
|
return null; |
|
}; |
|
// Range / Duration |
|
DateEnv.prototype.greatestWholeUnit = function (m0, m1) { |
|
var n = this.diffWholeYears(m0, m1); |
|
if (n !== null) { |
|
return { unit: 'year', value: n }; |
|
} |
|
n = this.diffWholeMonths(m0, m1); |
|
if (n !== null) { |
|
return { unit: 'month', value: n }; |
|
} |
|
n = diffWholeWeeks(m0, m1); |
|
if (n !== null) { |
|
return { unit: 'week', value: n }; |
|
} |
|
n = diffWholeDays(m0, m1); |
|
if (n !== null) { |
|
return { unit: 'day', value: n }; |
|
} |
|
n = diffHours(m0, m1); |
|
if (isInt(n)) { |
|
return { unit: 'hour', value: n }; |
|
} |
|
n = diffMinutes(m0, m1); |
|
if (isInt(n)) { |
|
return { unit: 'minute', value: n }; |
|
} |
|
n = diffSeconds(m0, m1); |
|
if (isInt(n)) { |
|
return { unit: 'second', value: n }; |
|
} |
|
return { unit: 'millisecond', value: m1.valueOf() - m0.valueOf() }; |
|
}; |
|
DateEnv.prototype.countDurationsBetween = function (m0, m1, d) { |
|
// TODO: can use greatestWholeUnit |
|
var diff; |
|
if (d.years) { |
|
diff = this.diffWholeYears(m0, m1); |
|
if (diff !== null) { |
|
return diff / asRoughYears(d); |
|
} |
|
} |
|
if (d.months) { |
|
diff = this.diffWholeMonths(m0, m1); |
|
if (diff !== null) { |
|
return diff / asRoughMonths(d); |
|
} |
|
} |
|
if (d.days) { |
|
diff = diffWholeDays(m0, m1); |
|
if (diff !== null) { |
|
return diff / asRoughDays(d); |
|
} |
|
} |
|
return (m1.valueOf() - m0.valueOf()) / asRoughMs(d); |
|
}; |
|
// Start-Of |
|
// these DON'T return zoned-dates. only UTC start-of dates |
|
DateEnv.prototype.startOf = function (m, unit) { |
|
if (unit === 'year') { |
|
return this.startOfYear(m); |
|
} |
|
if (unit === 'month') { |
|
return this.startOfMonth(m); |
|
} |
|
if (unit === 'week') { |
|
return this.startOfWeek(m); |
|
} |
|
if (unit === 'day') { |
|
return startOfDay(m); |
|
} |
|
if (unit === 'hour') { |
|
return startOfHour(m); |
|
} |
|
if (unit === 'minute') { |
|
return startOfMinute(m); |
|
} |
|
if (unit === 'second') { |
|
return startOfSecond(m); |
|
} |
|
return null; |
|
}; |
|
DateEnv.prototype.startOfYear = function (m) { |
|
return this.calendarSystem.arrayToMarker([ |
|
this.calendarSystem.getMarkerYear(m), |
|
]); |
|
}; |
|
DateEnv.prototype.startOfMonth = function (m) { |
|
return this.calendarSystem.arrayToMarker([ |
|
this.calendarSystem.getMarkerYear(m), |
|
this.calendarSystem.getMarkerMonth(m), |
|
]); |
|
}; |
|
DateEnv.prototype.startOfWeek = function (m) { |
|
return this.calendarSystem.arrayToMarker([ |
|
this.calendarSystem.getMarkerYear(m), |
|
this.calendarSystem.getMarkerMonth(m), |
|
m.getUTCDate() - ((m.getUTCDay() - this.weekDow + 7) % 7), |
|
]); |
|
}; |
|
// Week Number |
|
DateEnv.prototype.computeWeekNumber = function (marker) { |
|
if (this.weekNumberFunc) { |
|
return this.weekNumberFunc(this.toDate(marker)); |
|
} |
|
return weekOfYear(marker, this.weekDow, this.weekDoy); |
|
}; |
|
// TODO: choke on timeZoneName: long |
|
DateEnv.prototype.format = function (marker, formatter, dateOptions) { |
|
if (dateOptions === void 0) { dateOptions = {}; } |
|
return formatter.format({ |
|
marker: marker, |
|
timeZoneOffset: dateOptions.forcedTzo != null ? |
|
dateOptions.forcedTzo : |
|
this.offsetForMarker(marker), |
|
}, this); |
|
}; |
|
DateEnv.prototype.formatRange = function (start, end, formatter, dateOptions) { |
|
if (dateOptions === void 0) { dateOptions = {}; } |
|
if (dateOptions.isEndExclusive) { |
|
end = addMs(end, -1); |
|
} |
|
return formatter.formatRange({ |
|
marker: start, |
|
timeZoneOffset: dateOptions.forcedStartTzo != null ? |
|
dateOptions.forcedStartTzo : |
|
this.offsetForMarker(start), |
|
}, { |
|
marker: end, |
|
timeZoneOffset: dateOptions.forcedEndTzo != null ? |
|
dateOptions.forcedEndTzo : |
|
this.offsetForMarker(end), |
|
}, this, dateOptions.defaultSeparator); |
|
}; |
|
/* |
|
DUMB: the omitTime arg is dumb. if we omit the time, we want to omit the timezone offset. and if we do that, |
|
might as well use buildIsoString or some other util directly |
|
*/ |
|
DateEnv.prototype.formatIso = function (marker, extraOptions) { |
|
if (extraOptions === void 0) { extraOptions = {}; } |
|
var timeZoneOffset = null; |
|
if (!extraOptions.omitTimeZoneOffset) { |
|
if (extraOptions.forcedTzo != null) { |
|
timeZoneOffset = extraOptions.forcedTzo; |
|
} |
|
else { |
|
timeZoneOffset = this.offsetForMarker(marker); |
|
} |
|
} |
|
return buildIsoString(marker, timeZoneOffset, extraOptions.omitTime); |
|
}; |
|
// TimeZone |
|
DateEnv.prototype.timestampToMarker = function (ms) { |
|
if (this.timeZone === 'local') { |
|
return arrayToUtcDate(dateToLocalArray(new Date(ms))); |
|
} |
|
if (this.timeZone === 'UTC' || !this.namedTimeZoneImpl) { |
|
return new Date(ms); |
|
} |
|
return arrayToUtcDate(this.namedTimeZoneImpl.timestampToArray(ms)); |
|
}; |
|
DateEnv.prototype.offsetForMarker = function (m) { |
|
if (this.timeZone === 'local') { |
|
return -arrayToLocalDate(dateToUtcArray(m)).getTimezoneOffset(); // convert "inverse" offset to "normal" offset |
|
} |
|
if (this.timeZone === 'UTC') { |
|
return 0; |
|
} |
|
if (this.namedTimeZoneImpl) { |
|
return this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)); |
|
} |
|
return null; |
|
}; |
|
// Conversion |
|
DateEnv.prototype.toDate = function (m, forcedTzo) { |
|
if (this.timeZone === 'local') { |
|
return arrayToLocalDate(dateToUtcArray(m)); |
|
} |
|
if (this.timeZone === 'UTC') { |
|
return new Date(m.valueOf()); // make sure it's a copy |
|
} |
|
if (!this.namedTimeZoneImpl) { |
|
return new Date(m.valueOf() - (forcedTzo || 0)); |
|
} |
|
return new Date(m.valueOf() - |
|
this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)) * 1000 * 60); |
|
}; |
|
return DateEnv; |
|
}()); |
|
|
|
var globalLocales = []; |
|
|
|
var RAW_EN_LOCALE = { |
|
code: 'en', |
|
week: { |
|
dow: 0, |
|
doy: 4, |
|
}, |
|
direction: 'ltr', |
|
buttonText: { |
|
prev: 'prev', |
|
next: 'next', |
|
prevYear: 'prev year', |
|
nextYear: 'next year', |
|
year: 'year', |
|
today: 'today', |
|
month: 'month', |
|
week: 'week', |
|
day: 'day', |
|
list: 'list', |
|
}, |
|
weekText: 'W', |
|
allDayText: 'all-day', |
|
moreLinkText: 'more', |
|
noEventsText: 'No events to display', |
|
}; |
|
function organizeRawLocales(explicitRawLocales) { |
|
var defaultCode = explicitRawLocales.length > 0 ? explicitRawLocales[0].code : 'en'; |
|
var allRawLocales = globalLocales.concat(explicitRawLocales); |
|
var rawLocaleMap = { |
|
en: RAW_EN_LOCALE, |
|
}; |
|
for (var _i = 0, allRawLocales_1 = allRawLocales; _i < allRawLocales_1.length; _i++) { |
|
var rawLocale = allRawLocales_1[_i]; |
|
rawLocaleMap[rawLocale.code] = rawLocale; |
|
} |
|
return { |
|
map: rawLocaleMap, |
|
defaultCode: defaultCode, |
|
}; |
|
} |
|
function buildLocale(inputSingular, available) { |
|
if (typeof inputSingular === 'object' && !Array.isArray(inputSingular)) { |
|
return parseLocale(inputSingular.code, [inputSingular.code], inputSingular); |
|
} |
|
return queryLocale(inputSingular, available); |
|
} |
|
function queryLocale(codeArg, available) { |
|
var codes = [].concat(codeArg || []); // will convert to array |
|
var raw = queryRawLocale(codes, available) || RAW_EN_LOCALE; |
|
return parseLocale(codeArg, codes, raw); |
|
} |
|
function queryRawLocale(codes, available) { |
|
for (var i = 0; i < codes.length; i += 1) { |
|
var parts = codes[i].toLocaleLowerCase().split('-'); |
|
for (var j = parts.length; j > 0; j -= 1) { |
|
var simpleId = parts.slice(0, j).join('-'); |
|
if (available[simpleId]) { |
|
return available[simpleId]; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
function parseLocale(codeArg, codes, raw) { |
|
var merged = mergeProps([RAW_EN_LOCALE, raw], ['buttonText']); |
|
delete merged.code; // don't want this part of the options |
|
var week = merged.week; |
|
delete merged.week; |
|
return { |
|
codeArg: codeArg, |
|
codes: codes, |
|
week: week, |
|
simpleNumberFormat: new Intl.NumberFormat(codeArg), |
|
options: merged, |
|
}; |
|
} |
|
|
|
function formatDate(dateInput, options) { |
|
if (options === void 0) { options = {}; } |
|
var dateEnv = buildDateEnv(options); |
|
var formatter = createFormatter(options); |
|
var dateMeta = dateEnv.createMarkerMeta(dateInput); |
|
if (!dateMeta) { // TODO: warning? |
|
return ''; |
|
} |
|
return dateEnv.format(dateMeta.marker, formatter, { |
|
forcedTzo: dateMeta.forcedTzo, |
|
}); |
|
} |
|
function formatRange(startInput, endInput, options) { |
|
var dateEnv = buildDateEnv(typeof options === 'object' && options ? options : {}); // pass in if non-null object |
|
var formatter = createFormatter(options); |
|
var startMeta = dateEnv.createMarkerMeta(startInput); |
|
var endMeta = dateEnv.createMarkerMeta(endInput); |
|
if (!startMeta || !endMeta) { // TODO: warning? |
|
return ''; |
|
} |
|
return dateEnv.formatRange(startMeta.marker, endMeta.marker, formatter, { |
|
forcedStartTzo: startMeta.forcedTzo, |
|
forcedEndTzo: endMeta.forcedTzo, |
|
isEndExclusive: options.isEndExclusive, |
|
defaultSeparator: BASE_OPTION_DEFAULTS.defaultRangeSeparator, |
|
}); |
|
} |
|
// TODO: more DRY and optimized |
|
function buildDateEnv(settings) { |
|
var locale = buildLocale(settings.locale || 'en', organizeRawLocales([]).map); // TODO: don't hardcode 'en' everywhere |
|
return new DateEnv(__assign(__assign({ timeZone: BASE_OPTION_DEFAULTS.timeZone, calendarSystem: 'gregory' }, settings), { locale: locale })); |
|
} |
|
|
|
var DEF_DEFAULTS = { |
|
startTime: '09:00', |
|
endTime: '17:00', |
|
daysOfWeek: [1, 2, 3, 4, 5], |
|
display: 'inverse-background', |
|
classNames: 'fc-non-business', |
|
groupId: '_businessHours', |
|
}; |
|
/* |
|
TODO: pass around as EventDefHash!!! |
|
*/ |
|
function parseBusinessHours(input, context) { |
|
return parseEvents(refineInputs(input), null, context); |
|
} |
|
function refineInputs(input) { |
|
var rawDefs; |
|
if (input === true) { |
|
rawDefs = [{}]; // will get DEF_DEFAULTS verbatim |
|
} |
|
else if (Array.isArray(input)) { |
|
// if specifying an array, every sub-definition NEEDS a day-of-week |
|
rawDefs = input.filter(function (rawDef) { return rawDef.daysOfWeek; }); |
|
} |
|
else if (typeof input === 'object' && input) { // non-null object |
|
rawDefs = [input]; |
|
} |
|
else { // is probably false |
|
rawDefs = []; |
|
} |
|
rawDefs = rawDefs.map(function (rawDef) { return (__assign(__assign({}, DEF_DEFAULTS), rawDef)); }); |
|
return rawDefs; |
|
} |
|
|
|
function pointInsideRect(point, rect) { |
|
return point.left >= rect.left && |
|
point.left < rect.right && |
|
point.top >= rect.top && |
|
point.top < rect.bottom; |
|
} |
|
// Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false |
|
function intersectRects(rect1, rect2) { |
|
var res = { |
|
left: Math.max(rect1.left, rect2.left), |
|
right: Math.min(rect1.right, rect2.right), |
|
top: Math.max(rect1.top, rect2.top), |
|
bottom: Math.min(rect1.bottom, rect2.bottom), |
|
}; |
|
if (res.left < res.right && res.top < res.bottom) { |
|
return res; |
|
} |
|
return false; |
|
} |
|
function translateRect(rect, deltaX, deltaY) { |
|
return { |
|
left: rect.left + deltaX, |
|
right: rect.right + deltaX, |
|
top: rect.top + deltaY, |
|
bottom: rect.bottom + deltaY, |
|
}; |
|
} |
|
// Returns a new point that will have been moved to reside within the given rectangle |
|
function constrainPoint(point, rect) { |
|
return { |
|
left: Math.min(Math.max(point.left, rect.left), rect.right), |
|
top: Math.min(Math.max(point.top, rect.top), rect.bottom), |
|
}; |
|
} |
|
// Returns a point that is the center of the given rectangle |
|
function getRectCenter(rect) { |
|
return { |
|
left: (rect.left + rect.right) / 2, |
|
top: (rect.top + rect.bottom) / 2, |
|
}; |
|
} |
|
// Subtracts point2's coordinates from point1's coordinates, returning a delta |
|
function diffPoints(point1, point2) { |
|
return { |
|
left: point1.left - point2.left, |
|
top: point1.top - point2.top, |
|
}; |
|
} |
|
|
|
var canVGrowWithinCell; |
|
function getCanVGrowWithinCell() { |
|
if (canVGrowWithinCell == null) { |
|
canVGrowWithinCell = computeCanVGrowWithinCell(); |
|
} |
|
return canVGrowWithinCell; |
|
} |
|
function computeCanVGrowWithinCell() { |
|
// for SSR, because this function is call immediately at top-level |
|
// TODO: just make this logic execute top-level, immediately, instead of doing lazily |
|
if (typeof document === 'undefined') { |
|
return true; |
|
} |
|
var el = document.createElement('div'); |
|
el.style.position = 'absolute'; |
|
el.style.top = '0px'; |
|
el.style.left = '0px'; |
|
el.innerHTML = '<table><tr><td><div></div></td></tr></table>'; |
|
el.querySelector('table').style.height = '100px'; |
|
el.querySelector('div').style.height = '100%'; |
|
document.body.appendChild(el); |
|
var div = el.querySelector('div'); |
|
var possible = div.offsetHeight > 0; |
|
document.body.removeChild(el); |
|
return possible; |
|
} |
|
|
|
var EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere |
|
var Splitter = /** @class */ (function () { |
|
function Splitter() { |
|
this.getKeysForEventDefs = memoize(this._getKeysForEventDefs); |
|
this.splitDateSelection = memoize(this._splitDateSpan); |
|
this.splitEventStore = memoize(this._splitEventStore); |
|
this.splitIndividualUi = memoize(this._splitIndividualUi); |
|
this.splitEventDrag = memoize(this._splitInteraction); |
|
this.splitEventResize = memoize(this._splitInteraction); |
|
this.eventUiBuilders = {}; // TODO: typescript protection |
|
} |
|
Splitter.prototype.splitProps = function (props) { |
|
var _this = this; |
|
var keyInfos = this.getKeyInfo(props); |
|
var defKeys = this.getKeysForEventDefs(props.eventStore); |
|
var dateSelections = this.splitDateSelection(props.dateSelection); |
|
var individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases* |
|
var eventStores = this.splitEventStore(props.eventStore, defKeys); |
|
var eventDrags = this.splitEventDrag(props.eventDrag); |
|
var eventResizes = this.splitEventResize(props.eventResize); |
|
var splitProps = {}; |
|
this.eventUiBuilders = mapHash(keyInfos, function (info, key) { return _this.eventUiBuilders[key] || memoize(buildEventUiForKey); }); |
|
for (var key in keyInfos) { |
|
var keyInfo = keyInfos[key]; |
|
var eventStore = eventStores[key] || EMPTY_EVENT_STORE; |
|
var buildEventUi = this.eventUiBuilders[key]; |
|
splitProps[key] = { |
|
businessHours: keyInfo.businessHours || props.businessHours, |
|
dateSelection: dateSelections[key] || null, |
|
eventStore: eventStore, |
|
eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]), |
|
eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '', |
|
eventDrag: eventDrags[key] || null, |
|
eventResize: eventResizes[key] || null, |
|
}; |
|
} |
|
return splitProps; |
|
}; |
|
Splitter.prototype._splitDateSpan = function (dateSpan) { |
|
var dateSpans = {}; |
|
if (dateSpan) { |
|
var keys = this.getKeysForDateSpan(dateSpan); |
|
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { |
|
var key = keys_1[_i]; |
|
dateSpans[key] = dateSpan; |
|
} |
|
} |
|
return dateSpans; |
|
}; |
|
Splitter.prototype._getKeysForEventDefs = function (eventStore) { |
|
var _this = this; |
|
return mapHash(eventStore.defs, function (eventDef) { return _this.getKeysForEventDef(eventDef); }); |
|
}; |
|
Splitter.prototype._splitEventStore = function (eventStore, defKeys) { |
|
var defs = eventStore.defs, instances = eventStore.instances; |
|
var splitStores = {}; |
|
for (var defId in defs) { |
|
for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) { |
|
var key = _a[_i]; |
|
if (!splitStores[key]) { |
|
splitStores[key] = createEmptyEventStore(); |
|
} |
|
splitStores[key].defs[defId] = defs[defId]; |
|
} |
|
} |
|
for (var instanceId in instances) { |
|
var instance = instances[instanceId]; |
|
for (var _b = 0, _c = defKeys[instance.defId]; _b < _c.length; _b++) { |
|
var key = _c[_b]; |
|
if (splitStores[key]) { // must have already been created |
|
splitStores[key].instances[instanceId] = instance; |
|
} |
|
} |
|
} |
|
return splitStores; |
|
}; |
|
Splitter.prototype._splitIndividualUi = function (eventUiBases, defKeys) { |
|
var splitHashes = {}; |
|
for (var defId in eventUiBases) { |
|
if (defId) { // not the '' key |
|
for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) { |
|
var key = _a[_i]; |
|
if (!splitHashes[key]) { |
|
splitHashes[key] = {}; |
|
} |
|
splitHashes[key][defId] = eventUiBases[defId]; |
|
} |
|
} |
|
} |
|
return splitHashes; |
|
}; |
|
Splitter.prototype._splitInteraction = function (interaction) { |
|
var splitStates = {}; |
|
if (interaction) { |
|
var affectedStores_1 = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents)); |
|
// can't rely on defKeys because event data is mutated |
|
var mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents); |
|
var mutatedStores_1 = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId); |
|
var populate = function (key) { |
|
if (!splitStates[key]) { |
|
splitStates[key] = { |
|
affectedEvents: affectedStores_1[key] || EMPTY_EVENT_STORE, |
|
mutatedEvents: mutatedStores_1[key] || EMPTY_EVENT_STORE, |
|
isEvent: interaction.isEvent, |
|
}; |
|
} |
|
}; |
|
for (var key in affectedStores_1) { |
|
populate(key); |
|
} |
|
for (var key in mutatedStores_1) { |
|
populate(key); |
|
} |
|
} |
|
return splitStates; |
|
}; |
|
return Splitter; |
|
}()); |
|
function buildEventUiForKey(allUi, eventUiForKey, individualUi) { |
|
var baseParts = []; |
|
if (allUi) { |
|
baseParts.push(allUi); |
|
} |
|
if (eventUiForKey) { |
|
baseParts.push(eventUiForKey); |
|
} |
|
var stuff = { |
|
'': combineEventUis(baseParts), |
|
}; |
|
if (individualUi) { |
|
__assign(stuff, individualUi); |
|
} |
|
return stuff; |
|
} |
|
|
|
function getDateMeta(date, todayRange, nowDate, dateProfile) { |
|
return { |
|
dow: date.getUTCDay(), |
|
isDisabled: Boolean(dateProfile && !rangeContainsMarker(dateProfile.activeRange, date)), |
|
isOther: Boolean(dateProfile && !rangeContainsMarker(dateProfile.currentRange, date)), |
|
isToday: Boolean(todayRange && rangeContainsMarker(todayRange, date)), |
|
isPast: Boolean(nowDate ? (date < nowDate) : todayRange ? (date < todayRange.start) : false), |
|
isFuture: Boolean(nowDate ? (date > nowDate) : todayRange ? (date >= todayRange.end) : false), |
|
}; |
|
} |
|
function getDayClassNames(meta, theme) { |
|
var classNames = [ |
|
'fc-day', |
|
"fc-day-" + DAY_IDS[meta.dow], |
|
]; |
|
if (meta.isDisabled) { |
|
classNames.push('fc-day-disabled'); |
|
} |
|
else { |
|
if (meta.isToday) { |
|
classNames.push('fc-day-today'); |
|
classNames.push(theme.getClass('today')); |
|
} |
|
if (meta.isPast) { |
|
classNames.push('fc-day-past'); |
|
} |
|
if (meta.isFuture) { |
|
classNames.push('fc-day-future'); |
|
} |
|
if (meta.isOther) { |
|
classNames.push('fc-day-other'); |
|
} |
|
} |
|
return classNames; |
|
} |
|
function getSlotClassNames(meta, theme) { |
|
var classNames = [ |
|
'fc-slot', |
|
"fc-slot-" + DAY_IDS[meta.dow], |
|
]; |
|
if (meta.isDisabled) { |
|
classNames.push('fc-slot-disabled'); |
|
} |
|
else { |
|
if (meta.isToday) { |
|
classNames.push('fc-slot-today'); |
|
classNames.push(theme.getClass('today')); |
|
} |
|
if (meta.isPast) { |
|
classNames.push('fc-slot-past'); |
|
} |
|
if (meta.isFuture) { |
|
classNames.push('fc-slot-future'); |
|
} |
|
} |
|
return classNames; |
|
} |
|
|
|
function buildNavLinkData(date, type) { |
|
if (type === void 0) { type = 'day'; } |
|
return JSON.stringify({ |
|
date: formatDayString(date), |
|
type: type, |
|
}); |
|
} |
|
|
|
var _isRtlScrollbarOnLeft = null; |
|
function getIsRtlScrollbarOnLeft() { |
|
if (_isRtlScrollbarOnLeft === null) { |
|
_isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft(); |
|
} |
|
return _isRtlScrollbarOnLeft; |
|
} |
|
function computeIsRtlScrollbarOnLeft() { |
|
var outerEl = document.createElement('div'); |
|
applyStyle(outerEl, { |
|
position: 'absolute', |
|
top: -1000, |
|
left: 0, |
|
border: 0, |
|
padding: 0, |
|
overflow: 'scroll', |
|
direction: 'rtl', |
|
}); |
|
outerEl.innerHTML = '<div></div>'; |
|
document.body.appendChild(outerEl); |
|
var innerEl = outerEl.firstChild; |
|
var res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left; |
|
removeElement(outerEl); |
|
return res; |
|
} |
|
|
|
var _scrollbarWidths; |
|
function getScrollbarWidths() { |
|
if (!_scrollbarWidths) { |
|
_scrollbarWidths = computeScrollbarWidths(); |
|
} |
|
return _scrollbarWidths; |
|
} |
|
function computeScrollbarWidths() { |
|
var el = document.createElement('div'); |
|
el.style.overflow = 'scroll'; |
|
el.style.position = 'absolute'; |
|
el.style.top = '-9999px'; |
|
el.style.left = '-9999px'; |
|
document.body.appendChild(el); |
|
var res = computeScrollbarWidthsForEl(el); |
|
document.body.removeChild(el); |
|
return res; |
|
} |
|
// WARNING: will include border |
|
function computeScrollbarWidthsForEl(el) { |
|
return { |
|
x: el.offsetHeight - el.clientHeight, |
|
y: el.offsetWidth - el.clientWidth, |
|
}; |
|
} |
|
|
|
function computeEdges(el, getPadding) { |
|
if (getPadding === void 0) { getPadding = false; } |
|
var computedStyle = window.getComputedStyle(el); |
|
var borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0; |
|
var borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0; |
|
var borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0; |
|
var borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0; |
|
var badScrollbarWidths = computeScrollbarWidthsForEl(el); // includes border! |
|
var scrollbarLeftRight = badScrollbarWidths.y - borderLeft - borderRight; |
|
var scrollbarBottom = badScrollbarWidths.x - borderTop - borderBottom; |
|
var res = { |
|
borderLeft: borderLeft, |
|
borderRight: borderRight, |
|
borderTop: borderTop, |
|
borderBottom: borderBottom, |
|
scrollbarBottom: scrollbarBottom, |
|
scrollbarLeft: 0, |
|
scrollbarRight: 0, |
|
}; |
|
if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side? |
|
res.scrollbarLeft = scrollbarLeftRight; |
|
} |
|
else { |
|
res.scrollbarRight = scrollbarLeftRight; |
|
} |
|
if (getPadding) { |
|
res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0; |
|
res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0; |
|
res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0; |
|
res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0; |
|
} |
|
return res; |
|
} |
|
function computeInnerRect(el, goWithinPadding, doFromWindowViewport) { |
|
if (goWithinPadding === void 0) { goWithinPadding = false; } |
|
var outerRect = doFromWindowViewport ? el.getBoundingClientRect() : computeRect(el); |
|
var edges = computeEdges(el, goWithinPadding); |
|
var res = { |
|
left: outerRect.left + edges.borderLeft + edges.scrollbarLeft, |
|
right: outerRect.right - edges.borderRight - edges.scrollbarRight, |
|
top: outerRect.top + edges.borderTop, |
|
bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom, |
|
}; |
|
if (goWithinPadding) { |
|
res.left += edges.paddingLeft; |
|
res.right -= edges.paddingRight; |
|
res.top += edges.paddingTop; |
|
res.bottom -= edges.paddingBottom; |
|
} |
|
return res; |
|
} |
|
function computeRect(el) { |
|
var rect = el.getBoundingClientRect(); |
|
return { |
|
left: rect.left + window.pageXOffset, |
|
top: rect.top + window.pageYOffset, |
|
right: rect.right + window.pageXOffset, |
|
bottom: rect.bottom + window.pageYOffset, |
|
}; |
|
} |
|
function computeHeightAndMargins(el) { |
|
return el.getBoundingClientRect().height + computeVMargins(el); |
|
} |
|
function computeVMargins(el) { |
|
var computed = window.getComputedStyle(el); |
|
return parseInt(computed.marginTop, 10) + |
|
parseInt(computed.marginBottom, 10); |
|
} |
|
// does not return window |
|
function getClippingParents(el) { |
|
var parents = []; |
|
while (el instanceof HTMLElement) { // will stop when gets to document or null |
|
var computedStyle = window.getComputedStyle(el); |
|
if (computedStyle.position === 'fixed') { |
|
break; |
|
} |
|
if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) { |
|
parents.push(el); |
|
} |
|
el = el.parentNode; |
|
} |
|
return parents; |
|
} |
|
|
|
// given a function that resolves a result asynchronously. |
|
// the function can either call passed-in success and failure callbacks, |
|
// or it can return a promise. |
|
// if you need to pass additional params to func, bind them first. |
|
function unpromisify(func, success, failure) { |
|
// guard against success/failure callbacks being called more than once |
|
// and guard against a promise AND callback being used together. |
|
var isResolved = false; |
|
var wrappedSuccess = function () { |
|
if (!isResolved) { |
|
isResolved = true; |
|
success.apply(this, arguments); // eslint-disable-line prefer-rest-params |
|
} |
|
}; |
|
var wrappedFailure = function () { |
|
if (!isResolved) { |
|
isResolved = true; |
|
if (failure) { |
|
failure.apply(this, arguments); // eslint-disable-line prefer-rest-params |
|
} |
|
} |
|
}; |
|
var res = func(wrappedSuccess, wrappedFailure); |
|
if (res && typeof res.then === 'function') { |
|
res.then(wrappedSuccess, wrappedFailure); |
|
} |
|
} |
|
|
|
var Emitter = /** @class */ (function () { |
|
function Emitter() { |
|
this.handlers = {}; |
|
this.thisContext = null; |
|
} |
|
Emitter.prototype.setThisContext = function (thisContext) { |
|
this.thisContext = thisContext; |
|
}; |
|
Emitter.prototype.setOptions = function (options) { |
|
this.options = options; |
|
}; |
|
Emitter.prototype.on = function (type, handler) { |
|
addToHash(this.handlers, type, handler); |
|
}; |
|
Emitter.prototype.off = function (type, handler) { |
|
removeFromHash(this.handlers, type, handler); |
|
}; |
|
Emitter.prototype.trigger = function (type) { |
|
var args = []; |
|
for (var _i = 1; _i < arguments.length; _i++) { |
|
args[_i - 1] = arguments[_i]; |
|
} |
|
var attachedHandlers = this.handlers[type] || []; |
|
var optionHandler = this.options && this.options[type]; |
|
var handlers = [].concat(optionHandler || [], attachedHandlers); |
|
for (var _a = 0, handlers_1 = handlers; _a < handlers_1.length; _a++) { |
|
var handler = handlers_1[_a]; |
|
handler.apply(this.thisContext, args); |
|
} |
|
}; |
|
Emitter.prototype.hasHandlers = function (type) { |
|
return (this.handlers[type] && this.handlers[type].length) || |
|
(this.options && this.options[type]); |
|
}; |
|
return Emitter; |
|
}()); |
|
function addToHash(hash, type, handler) { |
|
(hash[type] || (hash[type] = [])) |
|
.push(handler); |
|
} |
|
function removeFromHash(hash, type, handler) { |
|
if (handler) { |
|
if (hash[type]) { |
|
hash[type] = hash[type].filter(function (func) { return func !== handler; }); |
|
} |
|
} |
|
else { |
|
delete hash[type]; // remove all handler funcs for this type |
|
} |
|
} |
|
|
|
/* |
|
Records offset information for a set of elements, relative to an origin element. |
|
Can record the left/right OR the top/bottom OR both. |
|
Provides methods for querying the cache by position. |
|
*/ |
|
var PositionCache = /** @class */ (function () { |
|
function PositionCache(originEl, els, isHorizontal, isVertical) { |
|
this.els = els; |
|
var originClientRect = this.originClientRect = originEl.getBoundingClientRect(); // relative to viewport top-left |
|
if (isHorizontal) { |
|
this.buildElHorizontals(originClientRect.left); |
|
} |
|
if (isVertical) { |
|
this.buildElVerticals(originClientRect.top); |
|
} |
|
} |
|
// Populates the left/right internal coordinate arrays |
|
PositionCache.prototype.buildElHorizontals = function (originClientLeft) { |
|
var lefts = []; |
|
var rights = []; |
|
for (var _i = 0, _a = this.els; _i < _a.length; _i++) { |
|
var el = _a[_i]; |
|
var rect = el.getBoundingClientRect(); |
|
lefts.push(rect.left - originClientLeft); |
|
rights.push(rect.right - originClientLeft); |
|
} |
|
this.lefts = lefts; |
|
this.rights = rights; |
|
}; |
|
// Populates the top/bottom internal coordinate arrays |
|
PositionCache.prototype.buildElVerticals = function (originClientTop) { |
|
var tops = []; |
|
var bottoms = []; |
|
for (var _i = 0, _a = this.els; _i < _a.length; _i++) { |
|
var el = _a[_i]; |
|
var rect = el.getBoundingClientRect(); |
|
tops.push(rect.top - originClientTop); |
|
bottoms.push(rect.bottom - originClientTop); |
|
} |
|
this.tops = tops; |
|
this.bottoms = bottoms; |
|
}; |
|
// Given a left offset (from document left), returns the index of the el that it horizontally intersects. |
|
// If no intersection is made, returns undefined. |
|
PositionCache.prototype.leftToIndex = function (leftPosition) { |
|
var _a = this, lefts = _a.lefts, rights = _a.rights; |
|
var len = lefts.length; |
|
var i; |
|
for (i = 0; i < len; i += 1) { |
|
if (leftPosition >= lefts[i] && leftPosition < rights[i]) { |
|
return i; |
|
} |
|
} |
|
return undefined; // TODO: better |
|
}; |
|
// Given a top offset (from document top), returns the index of the el that it vertically intersects. |
|
// If no intersection is made, returns undefined. |
|
PositionCache.prototype.topToIndex = function (topPosition) { |
|
var _a = this, tops = _a.tops, bottoms = _a.bottoms; |
|
var len = tops.length; |
|
var i; |
|
for (i = 0; i < len; i += 1) { |
|
if (topPosition >= tops[i] && topPosition < bottoms[i]) { |
|
return i; |
|
} |
|
} |
|
return undefined; // TODO: better |
|
}; |
|
// Gets the width of the element at the given index |
|
PositionCache.prototype.getWidth = function (leftIndex) { |
|
return this.rights[leftIndex] - this.lefts[leftIndex]; |
|
}; |
|
// Gets the height of the element at the given index |
|
PositionCache.prototype.getHeight = function (topIndex) { |
|
return this.bottoms[topIndex] - this.tops[topIndex]; |
|
}; |
|
return PositionCache; |
|
}()); |
|
|
|
/* eslint max-classes-per-file: "off" */ |
|
/* |
|
An object for getting/setting scroll-related information for an element. |
|
Internally, this is done very differently for window versus DOM element, |
|
so this object serves as a common interface. |
|
*/ |
|
var ScrollController = /** @class */ (function () { |
|
function ScrollController() { |
|
} |
|
ScrollController.prototype.getMaxScrollTop = function () { |
|
return this.getScrollHeight() - this.getClientHeight(); |
|
}; |
|
ScrollController.prototype.getMaxScrollLeft = function () { |
|
return this.getScrollWidth() - this.getClientWidth(); |
|
}; |
|
ScrollController.prototype.canScrollVertically = function () { |
|
return this.getMaxScrollTop() > 0; |
|
}; |
|
ScrollController.prototype.canScrollHorizontally = function () { |
|
return this.getMaxScrollLeft() > 0; |
|
}; |
|
ScrollController.prototype.canScrollUp = function () { |
|
return this.getScrollTop() > 0; |
|
}; |
|
ScrollController.prototype.canScrollDown = function () { |
|
return this.getScrollTop() < this.getMaxScrollTop(); |
|
}; |
|
ScrollController.prototype.canScrollLeft = function () { |
|
return this.getScrollLeft() > 0; |
|
}; |
|
ScrollController.prototype.canScrollRight = function () { |
|
return this.getScrollLeft() < this.getMaxScrollLeft(); |
|
}; |
|
return ScrollController; |
|
}()); |
|
var ElementScrollController = /** @class */ (function (_super) { |
|
__extends(ElementScrollController, _super); |
|
function ElementScrollController(el) { |
|
var _this = _super.call(this) || this; |
|
_this.el = el; |
|
return _this; |
|
} |
|
ElementScrollController.prototype.getScrollTop = function () { |
|
return this.el.scrollTop; |
|
}; |
|
ElementScrollController.prototype.getScrollLeft = function () { |
|
return this.el.scrollLeft; |
|
}; |
|
ElementScrollController.prototype.setScrollTop = function (top) { |
|
this.el.scrollTop = top; |
|
}; |
|
ElementScrollController.prototype.setScrollLeft = function (left) { |
|
this.el.scrollLeft = left; |
|
}; |
|
ElementScrollController.prototype.getScrollWidth = function () { |
|
return this.el.scrollWidth; |
|
}; |
|
ElementScrollController.prototype.getScrollHeight = function () { |
|
return this.el.scrollHeight; |
|
}; |
|
ElementScrollController.prototype.getClientHeight = function () { |
|
return this.el.clientHeight; |
|
}; |
|
ElementScrollController.prototype.getClientWidth = function () { |
|
return this.el.clientWidth; |
|
}; |
|
return ElementScrollController; |
|
}(ScrollController)); |
|
var WindowScrollController = /** @class */ (function (_super) { |
|
__extends(WindowScrollController, _super); |
|
function WindowScrollController() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
WindowScrollController.prototype.getScrollTop = function () { |
|
return window.pageYOffset; |
|
}; |
|
WindowScrollController.prototype.getScrollLeft = function () { |
|
return window.pageXOffset; |
|
}; |
|
WindowScrollController.prototype.setScrollTop = function (n) { |
|
window.scroll(window.pageXOffset, n); |
|
}; |
|
WindowScrollController.prototype.setScrollLeft = function (n) { |
|
window.scroll(n, window.pageYOffset); |
|
}; |
|
WindowScrollController.prototype.getScrollWidth = function () { |
|
return document.documentElement.scrollWidth; |
|
}; |
|
WindowScrollController.prototype.getScrollHeight = function () { |
|
return document.documentElement.scrollHeight; |
|
}; |
|
WindowScrollController.prototype.getClientHeight = function () { |
|
return document.documentElement.clientHeight; |
|
}; |
|
WindowScrollController.prototype.getClientWidth = function () { |
|
return document.documentElement.clientWidth; |
|
}; |
|
return WindowScrollController; |
|
}(ScrollController)); |
|
|
|
var Theme = /** @class */ (function () { |
|
function Theme(calendarOptions) { |
|
if (this.iconOverrideOption) { |
|
this.setIconOverride(calendarOptions[this.iconOverrideOption]); |
|
} |
|
} |
|
Theme.prototype.setIconOverride = function (iconOverrideHash) { |
|
var iconClassesCopy; |
|
var buttonName; |
|
if (typeof iconOverrideHash === 'object' && iconOverrideHash) { // non-null object |
|
iconClassesCopy = __assign({}, this.iconClasses); |
|
for (buttonName in iconOverrideHash) { |
|
iconClassesCopy[buttonName] = this.applyIconOverridePrefix(iconOverrideHash[buttonName]); |
|
} |
|
this.iconClasses = iconClassesCopy; |
|
} |
|
else if (iconOverrideHash === false) { |
|
this.iconClasses = {}; |
|
} |
|
}; |
|
Theme.prototype.applyIconOverridePrefix = function (className) { |
|
var prefix = this.iconOverridePrefix; |
|
if (prefix && className.indexOf(prefix) !== 0) { // if not already present |
|
className = prefix + className; |
|
} |
|
return className; |
|
}; |
|
Theme.prototype.getClass = function (key) { |
|
return this.classes[key] || ''; |
|
}; |
|
Theme.prototype.getIconClass = function (buttonName, isRtl) { |
|
var className; |
|
if (isRtl && this.rtlIconClasses) { |
|
className = this.rtlIconClasses[buttonName] || this.iconClasses[buttonName]; |
|
} |
|
else { |
|
className = this.iconClasses[buttonName]; |
|
} |
|
if (className) { |
|
return this.baseIconClass + " " + className; |
|
} |
|
return ''; |
|
}; |
|
Theme.prototype.getCustomButtonIconClass = function (customButtonProps) { |
|
var className; |
|
if (this.iconOverrideCustomButtonOption) { |
|
className = customButtonProps[this.iconOverrideCustomButtonOption]; |
|
if (className) { |
|
return this.baseIconClass + " " + this.applyIconOverridePrefix(className); |
|
} |
|
} |
|
return ''; |
|
}; |
|
return Theme; |
|
}()); |
|
Theme.prototype.classes = {}; |
|
Theme.prototype.iconClasses = {}; |
|
Theme.prototype.baseIconClass = ''; |
|
Theme.prototype.iconOverridePrefix = ''; |
|
|
|
/// <reference types="@fullcalendar/core-preact" /> |
|
if (typeof FullCalendarVDom === 'undefined') { |
|
throw new Error('Please import the top-level fullcalendar lib before attempting to import a plugin.'); |
|
} |
|
var Component = FullCalendarVDom.Component; |
|
var createElement = FullCalendarVDom.createElement; |
|
var render = FullCalendarVDom.render; |
|
var createRef = FullCalendarVDom.createRef; |
|
var Fragment = FullCalendarVDom.Fragment; |
|
var createContext$1 = FullCalendarVDom.createContext; |
|
var flushToDom$1 = FullCalendarVDom.flushToDom; |
|
var unmountComponentAtNode$1 = FullCalendarVDom.unmountComponentAtNode; |
|
|
|
var ScrollResponder = /** @class */ (function () { |
|
function ScrollResponder(execFunc, emitter, scrollTime) { |
|
var _this = this; |
|
this.execFunc = execFunc; |
|
this.emitter = emitter; |
|
this.scrollTime = scrollTime; |
|
this.handleScrollRequest = function (request) { |
|
_this.queuedRequest = __assign({}, _this.queuedRequest || {}, request); |
|
_this.drain(); |
|
}; |
|
emitter.on('_scrollRequest', this.handleScrollRequest); |
|
this.fireInitialScroll(); |
|
} |
|
ScrollResponder.prototype.detach = function () { |
|
this.emitter.off('_scrollRequest', this.handleScrollRequest); |
|
}; |
|
ScrollResponder.prototype.update = function (isDatesNew) { |
|
if (isDatesNew) { |
|
this.fireInitialScroll(); // will drain |
|
} |
|
else { |
|
this.drain(); |
|
} |
|
}; |
|
ScrollResponder.prototype.fireInitialScroll = function () { |
|
this.handleScrollRequest({ |
|
time: this.scrollTime, |
|
}); |
|
}; |
|
ScrollResponder.prototype.drain = function () { |
|
if (this.queuedRequest && this.execFunc(this.queuedRequest)) { |
|
this.queuedRequest = null; |
|
} |
|
}; |
|
return ScrollResponder; |
|
}()); |
|
|
|
var ViewContextType = createContext$1({}); // for Components |
|
function buildViewContext(viewSpec, viewApi, viewOptions, dateProfileGenerator, dateEnv, theme, pluginHooks, dispatch, getCurrentData, emitter, calendarApi, registerInteractiveComponent, unregisterInteractiveComponent) { |
|
return { |
|
dateEnv: dateEnv, |
|
options: viewOptions, |
|
pluginHooks: pluginHooks, |
|
emitter: emitter, |
|
dispatch: dispatch, |
|
getCurrentData: getCurrentData, |
|
calendarApi: calendarApi, |
|
viewSpec: viewSpec, |
|
viewApi: viewApi, |
|
dateProfileGenerator: dateProfileGenerator, |
|
theme: theme, |
|
isRtl: viewOptions.direction === 'rtl', |
|
addResizeHandler: function (handler) { |
|
emitter.on('_resize', handler); |
|
}, |
|
removeResizeHandler: function (handler) { |
|
emitter.off('_resize', handler); |
|
}, |
|
createScrollResponder: function (execFunc) { |
|
return new ScrollResponder(execFunc, emitter, createDuration(viewOptions.scrollTime)); |
|
}, |
|
registerInteractiveComponent: registerInteractiveComponent, |
|
unregisterInteractiveComponent: unregisterInteractiveComponent, |
|
}; |
|
} |
|
|
|
/* eslint max-classes-per-file: off */ |
|
var PureComponent = /** @class */ (function (_super) { |
|
__extends(PureComponent, _super); |
|
function PureComponent() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
PureComponent.prototype.shouldComponentUpdate = function (nextProps, nextState) { |
|
if (this.debug) { |
|
// eslint-disable-next-line no-console |
|
console.log(getUnequalProps(nextProps, this.props), getUnequalProps(nextState, this.state)); |
|
} |
|
return !compareObjs(this.props, nextProps, this.propEquality) || |
|
!compareObjs(this.state, nextState, this.stateEquality); |
|
}; |
|
PureComponent.addPropsEquality = addPropsEquality; |
|
PureComponent.addStateEquality = addStateEquality; |
|
PureComponent.contextType = ViewContextType; |
|
return PureComponent; |
|
}(Component)); |
|
PureComponent.prototype.propEquality = {}; |
|
PureComponent.prototype.stateEquality = {}; |
|
var BaseComponent = /** @class */ (function (_super) { |
|
__extends(BaseComponent, _super); |
|
function BaseComponent() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
BaseComponent.contextType = ViewContextType; |
|
return BaseComponent; |
|
}(PureComponent)); |
|
function addPropsEquality(propEquality) { |
|
var hash = Object.create(this.prototype.propEquality); |
|
__assign(hash, propEquality); |
|
this.prototype.propEquality = hash; |
|
} |
|
function addStateEquality(stateEquality) { |
|
var hash = Object.create(this.prototype.stateEquality); |
|
__assign(hash, stateEquality); |
|
this.prototype.stateEquality = hash; |
|
} |
|
// use other one |
|
function setRef(ref, current) { |
|
if (typeof ref === 'function') { |
|
ref(current); |
|
} |
|
else if (ref) { |
|
// see https://github.com/facebook/react/issues/13029 |
|
ref.current = current; |
|
} |
|
} |
|
|
|
function reduceEventStore(eventStore, action, eventSources, dateProfile, context) { |
|
switch (action.type) { |
|
case 'RECEIVE_EVENTS': // raw |
|
return receiveRawEvents(eventStore, eventSources[action.sourceId], action.fetchId, action.fetchRange, action.rawEvents, context); |
|
case 'ADD_EVENTS': // already parsed, but not expanded |
|
return addEvent(eventStore, action.eventStore, // new ones |
|
dateProfile ? dateProfile.activeRange : null, context); |
|
case 'RESET_EVENTS': |
|
return action.eventStore; |
|
case 'MERGE_EVENTS': // already parsed and expanded |
|
return mergeEventStores(eventStore, action.eventStore); |
|
case 'PREV': // TODO: how do we track all actions that affect dateProfile :( |
|
case 'NEXT': |
|
case 'CHANGE_DATE': |
|
case 'CHANGE_VIEW_TYPE': |
|
if (dateProfile) { |
|
return expandRecurring(eventStore, dateProfile.activeRange, context); |
|
} |
|
return eventStore; |
|
case 'REMOVE_EVENTS': |
|
return excludeSubEventStore(eventStore, action.eventStore); |
|
case 'REMOVE_EVENT_SOURCE': |
|
return excludeEventsBySourceId(eventStore, action.sourceId); |
|
case 'REMOVE_ALL_EVENT_SOURCES': |
|
return filterEventStoreDefs(eventStore, function (eventDef) { return (!eventDef.sourceId // only keep events with no source id |
|
); }); |
|
case 'REMOVE_ALL_EVENTS': |
|
return createEmptyEventStore(); |
|
default: |
|
return eventStore; |
|
} |
|
} |
|
function receiveRawEvents(eventStore, eventSource, fetchId, fetchRange, rawEvents, context) { |
|
if (eventSource && // not already removed |
|
fetchId === eventSource.latestFetchId // TODO: wish this logic was always in event-sources |
|
) { |
|
var subset = parseEvents(transformRawEvents(rawEvents, eventSource, context), eventSource, context); |
|
if (fetchRange) { |
|
subset = expandRecurring(subset, fetchRange, context); |
|
} |
|
return mergeEventStores(excludeEventsBySourceId(eventStore, eventSource.sourceId), subset); |
|
} |
|
return eventStore; |
|
} |
|
function transformRawEvents(rawEvents, eventSource, context) { |
|
var calEachTransform = context.options.eventDataTransform; |
|
var sourceEachTransform = eventSource ? eventSource.eventDataTransform : null; |
|
if (sourceEachTransform) { |
|
rawEvents = transformEachRawEvent(rawEvents, sourceEachTransform); |
|
} |
|
if (calEachTransform) { |
|
rawEvents = transformEachRawEvent(rawEvents, calEachTransform); |
|
} |
|
return rawEvents; |
|
} |
|
function transformEachRawEvent(rawEvents, func) { |
|
var refinedEvents; |
|
if (!func) { |
|
refinedEvents = rawEvents; |
|
} |
|
else { |
|
refinedEvents = []; |
|
for (var _i = 0, rawEvents_1 = rawEvents; _i < rawEvents_1.length; _i++) { |
|
var rawEvent = rawEvents_1[_i]; |
|
var refinedEvent = func(rawEvent); |
|
if (refinedEvent) { |
|
refinedEvents.push(refinedEvent); |
|
} |
|
else if (refinedEvent == null) { |
|
refinedEvents.push(rawEvent); |
|
} // if a different falsy value, do nothing |
|
} |
|
} |
|
return refinedEvents; |
|
} |
|
function addEvent(eventStore, subset, expandRange, context) { |
|
if (expandRange) { |
|
subset = expandRecurring(subset, expandRange, context); |
|
} |
|
return mergeEventStores(eventStore, subset); |
|
} |
|
function rezoneEventStoreDates(eventStore, oldDateEnv, newDateEnv) { |
|
var defs = eventStore.defs; |
|
var instances = mapHash(eventStore.instances, function (instance) { |
|
var def = defs[instance.defId]; |
|
if (def.allDay || def.recurringDef) { |
|
return instance; // isn't dependent on timezone |
|
} |
|
return __assign(__assign({}, instance), { range: { |
|
start: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.start, instance.forcedStartTzo)), |
|
end: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.end, instance.forcedEndTzo)), |
|
}, forcedStartTzo: newDateEnv.canComputeOffset ? null : instance.forcedStartTzo, forcedEndTzo: newDateEnv.canComputeOffset ? null : instance.forcedEndTzo }); |
|
}); |
|
return { defs: defs, instances: instances }; |
|
} |
|
function excludeEventsBySourceId(eventStore, sourceId) { |
|
return filterEventStoreDefs(eventStore, function (eventDef) { return eventDef.sourceId !== sourceId; }); |
|
} |
|
// QUESTION: why not just return instances? do a general object-property-exclusion util |
|
function excludeInstances(eventStore, removals) { |
|
return { |
|
defs: eventStore.defs, |
|
instances: filterHash(eventStore.instances, function (instance) { return !removals[instance.instanceId]; }), |
|
}; |
|
} |
|
|
|
// high-level segmenting-aware tester functions |
|
// ------------------------------------------------------------------------------------------------------------------------ |
|
function isInteractionValid(interaction, context) { |
|
return isNewPropsValid({ eventDrag: interaction }, context); // HACK: the eventDrag props is used for ALL interactions |
|
} |
|
function isDateSelectionValid(dateSelection, context) { |
|
return isNewPropsValid({ dateSelection: dateSelection }, context); |
|
} |
|
function isNewPropsValid(newProps, context) { |
|
var calendarState = context.getCurrentData(); |
|
var props = __assign({ businessHours: calendarState.businessHours, dateSelection: '', eventStore: calendarState.eventStore, eventUiBases: calendarState.eventUiBases, eventSelection: '', eventDrag: null, eventResize: null }, newProps); |
|
return (context.pluginHooks.isPropsValid || isPropsValid)(props, context); |
|
} |
|
function isPropsValid(state, context, dateSpanMeta, filterConfig) { |
|
if (dateSpanMeta === void 0) { dateSpanMeta = {}; } |
|
if (state.eventDrag && !isInteractionPropsValid(state, context, dateSpanMeta, filterConfig)) { |
|
return false; |
|
} |
|
if (state.dateSelection && !isDateSelectionPropsValid(state, context, dateSpanMeta, filterConfig)) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
// Moving Event Validation |
|
// ------------------------------------------------------------------------------------------------------------------------ |
|
function isInteractionPropsValid(state, context, dateSpanMeta, filterConfig) { |
|
var currentState = context.getCurrentData(); |
|
var interaction = state.eventDrag; // HACK: the eventDrag props is used for ALL interactions |
|
var subjectEventStore = interaction.mutatedEvents; |
|
var subjectDefs = subjectEventStore.defs; |
|
var subjectInstances = subjectEventStore.instances; |
|
var subjectConfigs = compileEventUis(subjectDefs, interaction.isEvent ? |
|
state.eventUiBases : |
|
{ '': currentState.selectionConfig }); |
|
if (filterConfig) { |
|
subjectConfigs = mapHash(subjectConfigs, filterConfig); |
|
} |
|
// exclude the subject events. TODO: exclude defs too? |
|
var otherEventStore = excludeInstances(state.eventStore, interaction.affectedEvents.instances); |
|
var otherDefs = otherEventStore.defs; |
|
var otherInstances = otherEventStore.instances; |
|
var otherConfigs = compileEventUis(otherDefs, state.eventUiBases); |
|
for (var subjectInstanceId in subjectInstances) { |
|
var subjectInstance = subjectInstances[subjectInstanceId]; |
|
var subjectRange = subjectInstance.range; |
|
var subjectConfig = subjectConfigs[subjectInstance.defId]; |
|
var subjectDef = subjectDefs[subjectInstance.defId]; |
|
// constraint |
|
if (!allConstraintsPass(subjectConfig.constraints, subjectRange, otherEventStore, state.businessHours, context)) { |
|
return false; |
|
} |
|
// overlap |
|
var eventOverlap = context.options.eventOverlap; |
|
var eventOverlapFunc = typeof eventOverlap === 'function' ? eventOverlap : null; |
|
for (var otherInstanceId in otherInstances) { |
|
var otherInstance = otherInstances[otherInstanceId]; |
|
// intersect! evaluate |
|
if (rangesIntersect(subjectRange, otherInstance.range)) { |
|
var otherOverlap = otherConfigs[otherInstance.defId].overlap; |
|
// consider the other event's overlap. only do this if the subject event is a "real" event |
|
if (otherOverlap === false && interaction.isEvent) { |
|
return false; |
|
} |
|
if (subjectConfig.overlap === false) { |
|
return false; |
|
} |
|
if (eventOverlapFunc && !eventOverlapFunc(new EventApi(context, otherDefs[otherInstance.defId], otherInstance), // still event |
|
new EventApi(context, subjectDef, subjectInstance))) { |
|
return false; |
|
} |
|
} |
|
} |
|
// allow (a function) |
|
var calendarEventStore = currentState.eventStore; // need global-to-calendar, not local to component (splittable)state |
|
for (var _i = 0, _a = subjectConfig.allows; _i < _a.length; _i++) { |
|
var subjectAllow = _a[_i]; |
|
var subjectDateSpan = __assign(__assign({}, dateSpanMeta), { range: subjectInstance.range, allDay: subjectDef.allDay }); |
|
var origDef = calendarEventStore.defs[subjectDef.defId]; |
|
var origInstance = calendarEventStore.instances[subjectInstanceId]; |
|
var eventApi = void 0; |
|
if (origDef) { // was previously in the calendar |
|
eventApi = new EventApi(context, origDef, origInstance); |
|
} |
|
else { // was an external event |
|
eventApi = new EventApi(context, subjectDef); // no instance, because had no dates |
|
} |
|
if (!subjectAllow(buildDateSpanApiWithContext(subjectDateSpan, context), eventApi)) { |
|
return false; |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
// Date Selection Validation |
|
// ------------------------------------------------------------------------------------------------------------------------ |
|
function isDateSelectionPropsValid(state, context, dateSpanMeta, filterConfig) { |
|
var relevantEventStore = state.eventStore; |
|
var relevantDefs = relevantEventStore.defs; |
|
var relevantInstances = relevantEventStore.instances; |
|
var selection = state.dateSelection; |
|
var selectionRange = selection.range; |
|
var selectionConfig = context.getCurrentData().selectionConfig; |
|
if (filterConfig) { |
|
selectionConfig = filterConfig(selectionConfig); |
|
} |
|
// constraint |
|
if (!allConstraintsPass(selectionConfig.constraints, selectionRange, relevantEventStore, state.businessHours, context)) { |
|
return false; |
|
} |
|
// overlap |
|
var selectOverlap = context.options.selectOverlap; |
|
var selectOverlapFunc = typeof selectOverlap === 'function' ? selectOverlap : null; |
|
for (var relevantInstanceId in relevantInstances) { |
|
var relevantInstance = relevantInstances[relevantInstanceId]; |
|
// intersect! evaluate |
|
if (rangesIntersect(selectionRange, relevantInstance.range)) { |
|
if (selectionConfig.overlap === false) { |
|
return false; |
|
} |
|
if (selectOverlapFunc && !selectOverlapFunc(new EventApi(context, relevantDefs[relevantInstance.defId], relevantInstance), null)) { |
|
return false; |
|
} |
|
} |
|
} |
|
// allow (a function) |
|
for (var _i = 0, _a = selectionConfig.allows; _i < _a.length; _i++) { |
|
var selectionAllow = _a[_i]; |
|
var fullDateSpan = __assign(__assign({}, dateSpanMeta), selection); |
|
if (!selectionAllow(buildDateSpanApiWithContext(fullDateSpan, context), null)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
// Constraint Utils |
|
// ------------------------------------------------------------------------------------------------------------------------ |
|
function allConstraintsPass(constraints, subjectRange, otherEventStore, businessHoursUnexpanded, context) { |
|
for (var _i = 0, constraints_1 = constraints; _i < constraints_1.length; _i++) { |
|
var constraint = constraints_1[_i]; |
|
if (!anyRangesContainRange(constraintToRanges(constraint, subjectRange, otherEventStore, businessHoursUnexpanded, context), subjectRange)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
function constraintToRanges(constraint, subjectRange, // for expanding a recurring constraint, or expanding business hours |
|
otherEventStore, // for if constraint is an even group ID |
|
businessHoursUnexpanded, // for if constraint is 'businessHours' |
|
context) { |
|
if (constraint === 'businessHours') { |
|
return eventStoreToRanges(expandRecurring(businessHoursUnexpanded, subjectRange, context)); |
|
} |
|
if (typeof constraint === 'string') { // an group ID |
|
return eventStoreToRanges(filterEventStoreDefs(otherEventStore, function (eventDef) { return eventDef.groupId === constraint; })); |
|
} |
|
if (typeof constraint === 'object' && constraint) { // non-null object |
|
return eventStoreToRanges(expandRecurring(constraint, subjectRange, context)); |
|
} |
|
return []; // if it's false |
|
} |
|
// TODO: move to event-store file? |
|
function eventStoreToRanges(eventStore) { |
|
var instances = eventStore.instances; |
|
var ranges = []; |
|
for (var instanceId in instances) { |
|
ranges.push(instances[instanceId].range); |
|
} |
|
return ranges; |
|
} |
|
// TODO: move to geom file? |
|
function anyRangesContainRange(outerRanges, innerRange) { |
|
for (var _i = 0, outerRanges_1 = outerRanges; _i < outerRanges_1.length; _i++) { |
|
var outerRange = outerRanges_1[_i]; |
|
if (rangeContainsRange(outerRange, innerRange)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
/* |
|
an INTERACTABLE date component |
|
|
|
PURPOSES: |
|
- hook up to fg, fill, and mirror renderers |
|
- interface for dragging and hits |
|
*/ |
|
var DateComponent = /** @class */ (function (_super) { |
|
__extends(DateComponent, _super); |
|
function DateComponent() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.uid = guid(); |
|
return _this; |
|
} |
|
// Hit System |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
DateComponent.prototype.prepareHits = function () { |
|
}; |
|
DateComponent.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) { |
|
return null; // this should be abstract |
|
}; |
|
// Validation |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
DateComponent.prototype.isInteractionValid = function (interaction) { |
|
var dateProfile = this.props.dateProfile; // HACK |
|
var instances = interaction.mutatedEvents.instances; |
|
if (dateProfile) { // HACK for MorePopover |
|
for (var instanceId in instances) { |
|
if (!rangeContainsRange(dateProfile.validRange, instances[instanceId].range)) { |
|
return false; |
|
} |
|
} |
|
} |
|
return isInteractionValid(interaction, this.context); |
|
}; |
|
DateComponent.prototype.isDateSelectionValid = function (selection) { |
|
var dateProfile = this.props.dateProfile; // HACK |
|
if (dateProfile && // HACK for MorePopover |
|
!rangeContainsRange(dateProfile.validRange, selection.range)) { |
|
return false; |
|
} |
|
return isDateSelectionValid(selection, this.context); |
|
}; |
|
// Pointer Interaction Utils |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
DateComponent.prototype.isValidSegDownEl = function (el) { |
|
return !this.props.eventDrag && // HACK |
|
!this.props.eventResize && // HACK |
|
!elementClosest(el, '.fc-event-mirror'); |
|
}; |
|
DateComponent.prototype.isValidDateDownEl = function (el) { |
|
return !elementClosest(el, '.fc-event:not(.fc-bg-event)') && |
|
!elementClosest(el, '.fc-daygrid-more-link') && // a "more.." link |
|
!elementClosest(el, 'a[data-navlink]') && // a clickable nav link |
|
!elementClosest(el, '.fc-popover'); // hack |
|
}; |
|
return DateComponent; |
|
}(BaseComponent)); |
|
|
|
// TODO: easier way to add new hooks? need to update a million things |
|
function createPlugin(input) { |
|
return { |
|
id: guid(), |
|
deps: input.deps || [], |
|
reducers: input.reducers || [], |
|
isLoadingFuncs: input.isLoadingFuncs || [], |
|
contextInit: [].concat(input.contextInit || []), |
|
eventRefiners: input.eventRefiners || {}, |
|
eventDefMemberAdders: input.eventDefMemberAdders || [], |
|
eventSourceRefiners: input.eventSourceRefiners || {}, |
|
isDraggableTransformers: input.isDraggableTransformers || [], |
|
eventDragMutationMassagers: input.eventDragMutationMassagers || [], |
|
eventDefMutationAppliers: input.eventDefMutationAppliers || [], |
|
dateSelectionTransformers: input.dateSelectionTransformers || [], |
|
datePointTransforms: input.datePointTransforms || [], |
|
dateSpanTransforms: input.dateSpanTransforms || [], |
|
views: input.views || {}, |
|
viewPropsTransformers: input.viewPropsTransformers || [], |
|
isPropsValid: input.isPropsValid || null, |
|
externalDefTransforms: input.externalDefTransforms || [], |
|
eventResizeJoinTransforms: input.eventResizeJoinTransforms || [], |
|
viewContainerAppends: input.viewContainerAppends || [], |
|
eventDropTransformers: input.eventDropTransformers || [], |
|
componentInteractions: input.componentInteractions || [], |
|
calendarInteractions: input.calendarInteractions || [], |
|
themeClasses: input.themeClasses || {}, |
|
eventSourceDefs: input.eventSourceDefs || [], |
|
cmdFormatter: input.cmdFormatter, |
|
recurringTypes: input.recurringTypes || [], |
|
namedTimeZonedImpl: input.namedTimeZonedImpl, |
|
initialView: input.initialView || '', |
|
elementDraggingImpl: input.elementDraggingImpl, |
|
optionChangeHandlers: input.optionChangeHandlers || {}, |
|
scrollGridImpl: input.scrollGridImpl || null, |
|
contentTypeHandlers: input.contentTypeHandlers || {}, |
|
listenerRefiners: input.listenerRefiners || {}, |
|
optionRefiners: input.optionRefiners || {}, |
|
propSetHandlers: input.propSetHandlers || {}, |
|
}; |
|
} |
|
function buildPluginHooks(pluginDefs, globalDefs) { |
|
var isAdded = {}; |
|
var hooks = { |
|
reducers: [], |
|
isLoadingFuncs: [], |
|
contextInit: [], |
|
eventRefiners: {}, |
|
eventDefMemberAdders: [], |
|
eventSourceRefiners: {}, |
|
isDraggableTransformers: [], |
|
eventDragMutationMassagers: [], |
|
eventDefMutationAppliers: [], |
|
dateSelectionTransformers: [], |
|
datePointTransforms: [], |
|
dateSpanTransforms: [], |
|
views: {}, |
|
viewPropsTransformers: [], |
|
isPropsValid: null, |
|
externalDefTransforms: [], |
|
eventResizeJoinTransforms: [], |
|
viewContainerAppends: [], |
|
eventDropTransformers: [], |
|
componentInteractions: [], |
|
calendarInteractions: [], |
|
themeClasses: {}, |
|
eventSourceDefs: [], |
|
cmdFormatter: null, |
|
recurringTypes: [], |
|
namedTimeZonedImpl: null, |
|
initialView: '', |
|
elementDraggingImpl: null, |
|
optionChangeHandlers: {}, |
|
scrollGridImpl: null, |
|
contentTypeHandlers: {}, |
|
listenerRefiners: {}, |
|
optionRefiners: {}, |
|
propSetHandlers: {}, |
|
}; |
|
function addDefs(defs) { |
|
for (var _i = 0, defs_1 = defs; _i < defs_1.length; _i++) { |
|
var def = defs_1[_i]; |
|
if (!isAdded[def.id]) { |
|
isAdded[def.id] = true; |
|
addDefs(def.deps); |
|
hooks = combineHooks(hooks, def); |
|
} |
|
} |
|
} |
|
if (pluginDefs) { |
|
addDefs(pluginDefs); |
|
} |
|
addDefs(globalDefs); |
|
return hooks; |
|
} |
|
function buildBuildPluginHooks() { |
|
var currentOverrideDefs = []; |
|
var currentGlobalDefs = []; |
|
var currentHooks; |
|
return function (overrideDefs, globalDefs) { |
|
if (!currentHooks || !isArraysEqual(overrideDefs, currentOverrideDefs) || !isArraysEqual(globalDefs, currentGlobalDefs)) { |
|
currentHooks = buildPluginHooks(overrideDefs, globalDefs); |
|
} |
|
currentOverrideDefs = overrideDefs; |
|
currentGlobalDefs = globalDefs; |
|
return currentHooks; |
|
}; |
|
} |
|
function combineHooks(hooks0, hooks1) { |
|
return { |
|
reducers: hooks0.reducers.concat(hooks1.reducers), |
|
isLoadingFuncs: hooks0.isLoadingFuncs.concat(hooks1.isLoadingFuncs), |
|
contextInit: hooks0.contextInit.concat(hooks1.contextInit), |
|
eventRefiners: __assign(__assign({}, hooks0.eventRefiners), hooks1.eventRefiners), |
|
eventDefMemberAdders: hooks0.eventDefMemberAdders.concat(hooks1.eventDefMemberAdders), |
|
eventSourceRefiners: __assign(__assign({}, hooks0.eventSourceRefiners), hooks1.eventSourceRefiners), |
|
isDraggableTransformers: hooks0.isDraggableTransformers.concat(hooks1.isDraggableTransformers), |
|
eventDragMutationMassagers: hooks0.eventDragMutationMassagers.concat(hooks1.eventDragMutationMassagers), |
|
eventDefMutationAppliers: hooks0.eventDefMutationAppliers.concat(hooks1.eventDefMutationAppliers), |
|
dateSelectionTransformers: hooks0.dateSelectionTransformers.concat(hooks1.dateSelectionTransformers), |
|
datePointTransforms: hooks0.datePointTransforms.concat(hooks1.datePointTransforms), |
|
dateSpanTransforms: hooks0.dateSpanTransforms.concat(hooks1.dateSpanTransforms), |
|
views: __assign(__assign({}, hooks0.views), hooks1.views), |
|
viewPropsTransformers: hooks0.viewPropsTransformers.concat(hooks1.viewPropsTransformers), |
|
isPropsValid: hooks1.isPropsValid || hooks0.isPropsValid, |
|
externalDefTransforms: hooks0.externalDefTransforms.concat(hooks1.externalDefTransforms), |
|
eventResizeJoinTransforms: hooks0.eventResizeJoinTransforms.concat(hooks1.eventResizeJoinTransforms), |
|
viewContainerAppends: hooks0.viewContainerAppends.concat(hooks1.viewContainerAppends), |
|
eventDropTransformers: hooks0.eventDropTransformers.concat(hooks1.eventDropTransformers), |
|
calendarInteractions: hooks0.calendarInteractions.concat(hooks1.calendarInteractions), |
|
componentInteractions: hooks0.componentInteractions.concat(hooks1.componentInteractions), |
|
themeClasses: __assign(__assign({}, hooks0.themeClasses), hooks1.themeClasses), |
|
eventSourceDefs: hooks0.eventSourceDefs.concat(hooks1.eventSourceDefs), |
|
cmdFormatter: hooks1.cmdFormatter || hooks0.cmdFormatter, |
|
recurringTypes: hooks0.recurringTypes.concat(hooks1.recurringTypes), |
|
namedTimeZonedImpl: hooks1.namedTimeZonedImpl || hooks0.namedTimeZonedImpl, |
|
initialView: hooks0.initialView || hooks1.initialView, |
|
elementDraggingImpl: hooks0.elementDraggingImpl || hooks1.elementDraggingImpl, |
|
optionChangeHandlers: __assign(__assign({}, hooks0.optionChangeHandlers), hooks1.optionChangeHandlers), |
|
scrollGridImpl: hooks1.scrollGridImpl || hooks0.scrollGridImpl, |
|
contentTypeHandlers: __assign(__assign({}, hooks0.contentTypeHandlers), hooks1.contentTypeHandlers), |
|
listenerRefiners: __assign(__assign({}, hooks0.listenerRefiners), hooks1.listenerRefiners), |
|
optionRefiners: __assign(__assign({}, hooks0.optionRefiners), hooks1.optionRefiners), |
|
propSetHandlers: __assign(__assign({}, hooks0.propSetHandlers), hooks1.propSetHandlers), |
|
}; |
|
} |
|
|
|
var StandardTheme = /** @class */ (function (_super) { |
|
__extends(StandardTheme, _super); |
|
function StandardTheme() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
return StandardTheme; |
|
}(Theme)); |
|
StandardTheme.prototype.classes = { |
|
root: 'fc-theme-standard', |
|
tableCellShaded: 'fc-cell-shaded', |
|
buttonGroup: 'fc-button-group', |
|
button: 'fc-button fc-button-primary', |
|
buttonActive: 'fc-button-active', |
|
}; |
|
StandardTheme.prototype.baseIconClass = 'fc-icon'; |
|
StandardTheme.prototype.iconClasses = { |
|
close: 'fc-icon-x', |
|
prev: 'fc-icon-chevron-left', |
|
next: 'fc-icon-chevron-right', |
|
prevYear: 'fc-icon-chevrons-left', |
|
nextYear: 'fc-icon-chevrons-right', |
|
}; |
|
StandardTheme.prototype.rtlIconClasses = { |
|
prev: 'fc-icon-chevron-right', |
|
next: 'fc-icon-chevron-left', |
|
prevYear: 'fc-icon-chevrons-right', |
|
nextYear: 'fc-icon-chevrons-left', |
|
}; |
|
StandardTheme.prototype.iconOverrideOption = 'buttonIcons'; // TODO: make TS-friendly |
|
StandardTheme.prototype.iconOverrideCustomButtonOption = 'icon'; |
|
StandardTheme.prototype.iconOverridePrefix = 'fc-icon-'; |
|
|
|
function compileViewDefs(defaultConfigs, overrideConfigs) { |
|
var hash = {}; |
|
var viewType; |
|
for (viewType in defaultConfigs) { |
|
ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs); |
|
} |
|
for (viewType in overrideConfigs) { |
|
ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs); |
|
} |
|
return hash; |
|
} |
|
function ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs) { |
|
if (hash[viewType]) { |
|
return hash[viewType]; |
|
} |
|
var viewDef = buildViewDef(viewType, hash, defaultConfigs, overrideConfigs); |
|
if (viewDef) { |
|
hash[viewType] = viewDef; |
|
} |
|
return viewDef; |
|
} |
|
function buildViewDef(viewType, hash, defaultConfigs, overrideConfigs) { |
|
var defaultConfig = defaultConfigs[viewType]; |
|
var overrideConfig = overrideConfigs[viewType]; |
|
var queryProp = function (name) { return ((defaultConfig && defaultConfig[name] !== null) ? defaultConfig[name] : |
|
((overrideConfig && overrideConfig[name] !== null) ? overrideConfig[name] : null)); }; |
|
var theComponent = queryProp('component'); |
|
var superType = queryProp('superType'); |
|
var superDef = null; |
|
if (superType) { |
|
if (superType === viewType) { |
|
throw new Error('Can\'t have a custom view type that references itself'); |
|
} |
|
superDef = ensureViewDef(superType, hash, defaultConfigs, overrideConfigs); |
|
} |
|
if (!theComponent && superDef) { |
|
theComponent = superDef.component; |
|
} |
|
if (!theComponent) { |
|
return null; // don't throw a warning, might be settings for a single-unit view |
|
} |
|
return { |
|
type: viewType, |
|
component: theComponent, |
|
defaults: __assign(__assign({}, (superDef ? superDef.defaults : {})), (defaultConfig ? defaultConfig.rawOptions : {})), |
|
overrides: __assign(__assign({}, (superDef ? superDef.overrides : {})), (overrideConfig ? overrideConfig.rawOptions : {})), |
|
}; |
|
} |
|
|
|
/* eslint max-classes-per-file: off */ |
|
// NOTE: in JSX, you should always use this class with <HookProps> arg. otherwise, will default to any??? |
|
var RenderHook = /** @class */ (function (_super) { |
|
__extends(RenderHook, _super); |
|
function RenderHook() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.rootElRef = createRef(); |
|
_this.handleRootEl = function (el) { |
|
setRef(_this.rootElRef, el); |
|
if (_this.props.elRef) { |
|
setRef(_this.props.elRef, el); |
|
} |
|
}; |
|
return _this; |
|
} |
|
RenderHook.prototype.render = function () { |
|
var _this = this; |
|
var props = this.props; |
|
var hookProps = props.hookProps; |
|
return (createElement(MountHook, { hookProps: hookProps, didMount: props.didMount, willUnmount: props.willUnmount, elRef: this.handleRootEl }, function (rootElRef) { return (createElement(ContentHook, { hookProps: hookProps, content: props.content, defaultContent: props.defaultContent, backupElRef: _this.rootElRef }, function (innerElRef, innerContent) { return props.children(rootElRef, normalizeClassNames(props.classNames, hookProps), innerElRef, innerContent); })); })); |
|
}; |
|
return RenderHook; |
|
}(BaseComponent)); |
|
// TODO: rename to be about function, not default. use in above type |
|
// for forcing rerender of components that use the ContentHook |
|
var CustomContentRenderContext = createContext$1(0); |
|
function ContentHook(props) { |
|
return (createElement(CustomContentRenderContext.Consumer, null, function (renderId) { return (createElement(ContentHookInner, __assign({ renderId: renderId }, props))); })); |
|
} |
|
var ContentHookInner = /** @class */ (function (_super) { |
|
__extends(ContentHookInner, _super); |
|
function ContentHookInner() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.innerElRef = createRef(); |
|
return _this; |
|
} |
|
ContentHookInner.prototype.render = function () { |
|
return this.props.children(this.innerElRef, this.renderInnerContent()); |
|
}; |
|
ContentHookInner.prototype.componentDidMount = function () { |
|
this.updateCustomContent(); |
|
}; |
|
ContentHookInner.prototype.componentDidUpdate = function () { |
|
this.updateCustomContent(); |
|
}; |
|
ContentHookInner.prototype.componentWillUnmount = function () { |
|
if (this.customContentInfo && this.customContentInfo.destroy) { |
|
this.customContentInfo.destroy(); |
|
} |
|
}; |
|
ContentHookInner.prototype.renderInnerContent = function () { |
|
var contentTypeHandlers = this.context.pluginHooks.contentTypeHandlers; |
|
var _a = this, props = _a.props, customContentInfo = _a.customContentInfo; |
|
var rawVal = props.content; |
|
var innerContent = normalizeContent(rawVal, props.hookProps); |
|
var innerContentVDom = null; |
|
if (innerContent === undefined) { // use the default |
|
innerContent = normalizeContent(props.defaultContent, props.hookProps); |
|
} |
|
if (innerContent !== undefined) { // we allow custom content handlers to return nothing |
|
if (customContentInfo) { |
|
customContentInfo.contentVal = innerContent[customContentInfo.contentKey]; |
|
} |
|
else if (typeof innerContent === 'object') { |
|
// look for a prop that would indicate a custom content handler is needed |
|
for (var contentKey in contentTypeHandlers) { |
|
if (innerContent[contentKey] !== undefined) { |
|
var stuff = contentTypeHandlers[contentKey](); |
|
customContentInfo = this.customContentInfo = __assign({ contentKey: contentKey, contentVal: innerContent[contentKey] }, stuff); |
|
break; |
|
} |
|
} |
|
} |
|
if (customContentInfo) { |
|
innerContentVDom = []; // signal that something was specified |
|
} |
|
else { |
|
innerContentVDom = innerContent; // assume a [p]react vdom node. use it |
|
} |
|
} |
|
return innerContentVDom; |
|
}; |
|
ContentHookInner.prototype.updateCustomContent = function () { |
|
if (this.customContentInfo) { |
|
this.customContentInfo.render(this.innerElRef.current || this.props.backupElRef.current, // the element to render into |
|
this.customContentInfo.contentVal); |
|
} |
|
}; |
|
return ContentHookInner; |
|
}(BaseComponent)); |
|
var MountHook = /** @class */ (function (_super) { |
|
__extends(MountHook, _super); |
|
function MountHook() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.handleRootEl = function (rootEl) { |
|
_this.rootEl = rootEl; |
|
if (_this.props.elRef) { |
|
setRef(_this.props.elRef, rootEl); |
|
} |
|
}; |
|
return _this; |
|
} |
|
MountHook.prototype.render = function () { |
|
return this.props.children(this.handleRootEl); |
|
}; |
|
MountHook.prototype.componentDidMount = function () { |
|
var callback = this.props.didMount; |
|
if (callback) { |
|
callback(__assign(__assign({}, this.props.hookProps), { el: this.rootEl })); |
|
} |
|
}; |
|
MountHook.prototype.componentWillUnmount = function () { |
|
var callback = this.props.willUnmount; |
|
if (callback) { |
|
callback(__assign(__assign({}, this.props.hookProps), { el: this.rootEl })); |
|
} |
|
}; |
|
return MountHook; |
|
}(BaseComponent)); |
|
function buildClassNameNormalizer() { |
|
var currentGenerator; |
|
var currentHookProps; |
|
var currentClassNames = []; |
|
return function (generator, hookProps) { |
|
if (!currentHookProps || !isPropsEqual(currentHookProps, hookProps) || generator !== currentGenerator) { |
|
currentGenerator = generator; |
|
currentHookProps = hookProps; |
|
currentClassNames = normalizeClassNames(generator, hookProps); |
|
} |
|
return currentClassNames; |
|
}; |
|
} |
|
function normalizeClassNames(classNames, hookProps) { |
|
if (typeof classNames === 'function') { |
|
classNames = classNames(hookProps); |
|
} |
|
return parseClassNames(classNames); |
|
} |
|
function normalizeContent(input, hookProps) { |
|
if (typeof input === 'function') { |
|
return input(hookProps, createElement); // give the function the vdom-creation func |
|
} |
|
return input; |
|
} |
|
|
|
var ViewRoot = /** @class */ (function (_super) { |
|
__extends(ViewRoot, _super); |
|
function ViewRoot() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.normalizeClassNames = buildClassNameNormalizer(); |
|
return _this; |
|
} |
|
ViewRoot.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var options = context.options; |
|
var hookProps = { view: context.viewApi }; |
|
var customClassNames = this.normalizeClassNames(options.viewClassNames, hookProps); |
|
return (createElement(MountHook, { hookProps: hookProps, didMount: options.viewDidMount, willUnmount: options.viewWillUnmount, elRef: props.elRef }, function (rootElRef) { return props.children(rootElRef, ["fc-" + props.viewSpec.type + "-view", 'fc-view'].concat(customClassNames)); })); |
|
}; |
|
return ViewRoot; |
|
}(BaseComponent)); |
|
|
|
function parseViewConfigs(inputs) { |
|
return mapHash(inputs, parseViewConfig); |
|
} |
|
function parseViewConfig(input) { |
|
var rawOptions = typeof input === 'function' ? |
|
{ component: input } : |
|
input; |
|
var component = rawOptions.component; |
|
if (rawOptions.content) { |
|
component = createViewHookComponent(rawOptions); |
|
// TODO: remove content/classNames/didMount/etc from options? |
|
} |
|
return { |
|
superType: rawOptions.type, |
|
component: component, |
|
rawOptions: rawOptions, |
|
}; |
|
} |
|
function createViewHookComponent(options) { |
|
return function (viewProps) { return (createElement(ViewContextType.Consumer, null, function (context) { return (createElement(ViewRoot, { viewSpec: context.viewSpec }, function (viewElRef, viewClassNames) { |
|
var hookProps = __assign(__assign({}, viewProps), { nextDayThreshold: context.options.nextDayThreshold }); |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.classNames, content: options.content, didMount: options.didMount, willUnmount: options.willUnmount, elRef: viewElRef }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("div", { className: viewClassNames.concat(customClassNames).join(' '), ref: rootElRef }, innerContent)); })); |
|
})); })); }; |
|
} |
|
|
|
function buildViewSpecs(defaultInputs, optionOverrides, dynamicOptionOverrides, localeDefaults) { |
|
var defaultConfigs = parseViewConfigs(defaultInputs); |
|
var overrideConfigs = parseViewConfigs(optionOverrides.views); |
|
var viewDefs = compileViewDefs(defaultConfigs, overrideConfigs); |
|
return mapHash(viewDefs, function (viewDef) { return buildViewSpec(viewDef, overrideConfigs, optionOverrides, dynamicOptionOverrides, localeDefaults); }); |
|
} |
|
function buildViewSpec(viewDef, overrideConfigs, optionOverrides, dynamicOptionOverrides, localeDefaults) { |
|
var durationInput = viewDef.overrides.duration || |
|
viewDef.defaults.duration || |
|
dynamicOptionOverrides.duration || |
|
optionOverrides.duration; |
|
var duration = null; |
|
var durationUnit = ''; |
|
var singleUnit = ''; |
|
var singleUnitOverrides = {}; |
|
if (durationInput) { |
|
duration = createDurationCached(durationInput); |
|
if (duration) { // valid? |
|
var denom = greatestDurationDenominator(duration); |
|
durationUnit = denom.unit; |
|
if (denom.value === 1) { |
|
singleUnit = durationUnit; |
|
singleUnitOverrides = overrideConfigs[durationUnit] ? overrideConfigs[durationUnit].rawOptions : {}; |
|
} |
|
} |
|
} |
|
var queryButtonText = function (optionsSubset) { |
|
var buttonTextMap = optionsSubset.buttonText || {}; |
|
var buttonTextKey = viewDef.defaults.buttonTextKey; |
|
if (buttonTextKey != null && buttonTextMap[buttonTextKey] != null) { |
|
return buttonTextMap[buttonTextKey]; |
|
} |
|
if (buttonTextMap[viewDef.type] != null) { |
|
return buttonTextMap[viewDef.type]; |
|
} |
|
if (buttonTextMap[singleUnit] != null) { |
|
return buttonTextMap[singleUnit]; |
|
} |
|
return null; |
|
}; |
|
return { |
|
type: viewDef.type, |
|
component: viewDef.component, |
|
duration: duration, |
|
durationUnit: durationUnit, |
|
singleUnit: singleUnit, |
|
optionDefaults: viewDef.defaults, |
|
optionOverrides: __assign(__assign({}, singleUnitOverrides), viewDef.overrides), |
|
buttonTextOverride: queryButtonText(dynamicOptionOverrides) || |
|
queryButtonText(optionOverrides) || // constructor-specified buttonText lookup hash takes precedence |
|
viewDef.overrides.buttonText, |
|
buttonTextDefault: queryButtonText(localeDefaults) || |
|
viewDef.defaults.buttonText || |
|
queryButtonText(BASE_OPTION_DEFAULTS) || |
|
viewDef.type, |
|
}; |
|
} |
|
// hack to get memoization working |
|
var durationInputMap = {}; |
|
function createDurationCached(durationInput) { |
|
var json = JSON.stringify(durationInput); |
|
var res = durationInputMap[json]; |
|
if (res === undefined) { |
|
res = createDuration(durationInput); |
|
durationInputMap[json] = res; |
|
} |
|
return res; |
|
} |
|
|
|
var DateProfileGenerator = /** @class */ (function () { |
|
function DateProfileGenerator(props) { |
|
this.props = props; |
|
this.nowDate = getNow(props.nowInput, props.dateEnv); |
|
this.initHiddenDays(); |
|
} |
|
/* Date Range Computation |
|
------------------------------------------------------------------------------------------------------------------*/ |
|
// Builds a structure with info about what the dates/ranges will be for the "prev" view. |
|
DateProfileGenerator.prototype.buildPrev = function (currentDateProfile, currentDate, forceToValid) { |
|
var dateEnv = this.props.dateEnv; |
|
var prevDate = dateEnv.subtract(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month |
|
currentDateProfile.dateIncrement); |
|
return this.build(prevDate, -1, forceToValid); |
|
}; |
|
// Builds a structure with info about what the dates/ranges will be for the "next" view. |
|
DateProfileGenerator.prototype.buildNext = function (currentDateProfile, currentDate, forceToValid) { |
|
var dateEnv = this.props.dateEnv; |
|
var nextDate = dateEnv.add(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month |
|
currentDateProfile.dateIncrement); |
|
return this.build(nextDate, 1, forceToValid); |
|
}; |
|
// Builds a structure holding dates/ranges for rendering around the given date. |
|
// Optional direction param indicates whether the date is being incremented/decremented |
|
// from its previous value. decremented = -1, incremented = 1 (default). |
|
DateProfileGenerator.prototype.build = function (currentDate, direction, forceToValid) { |
|
if (forceToValid === void 0) { forceToValid = true; } |
|
var props = this.props; |
|
var validRange; |
|
var currentInfo; |
|
var isRangeAllDay; |
|
var renderRange; |
|
var activeRange; |
|
var isValid; |
|
validRange = this.buildValidRange(); |
|
validRange = this.trimHiddenDays(validRange); |
|
if (forceToValid) { |
|
currentDate = constrainMarkerToRange(currentDate, validRange); |
|
} |
|
currentInfo = this.buildCurrentRangeInfo(currentDate, direction); |
|
isRangeAllDay = /^(year|month|week|day)$/.test(currentInfo.unit); |
|
renderRange = this.buildRenderRange(this.trimHiddenDays(currentInfo.range), currentInfo.unit, isRangeAllDay); |
|
renderRange = this.trimHiddenDays(renderRange); |
|
activeRange = renderRange; |
|
if (!props.showNonCurrentDates) { |
|
activeRange = intersectRanges(activeRange, currentInfo.range); |
|
} |
|
activeRange = this.adjustActiveRange(activeRange); |
|
activeRange = intersectRanges(activeRange, validRange); // might return null |
|
// it's invalid if the originally requested date is not contained, |
|
// or if the range is completely outside of the valid range. |
|
isValid = rangesIntersect(currentInfo.range, validRange); |
|
return { |
|
// constraint for where prev/next operations can go and where events can be dragged/resized to. |
|
// an object with optional start and end properties. |
|
validRange: validRange, |
|
// range the view is formally responsible for. |
|
// for example, a month view might have 1st-31st, excluding padded dates |
|
currentRange: currentInfo.range, |
|
// name of largest unit being displayed, like "month" or "week" |
|
currentRangeUnit: currentInfo.unit, |
|
isRangeAllDay: isRangeAllDay, |
|
// dates that display events and accept drag-n-drop |
|
// will be `null` if no dates accept events |
|
activeRange: activeRange, |
|
// date range with a rendered skeleton |
|
// includes not-active days that need some sort of DOM |
|
renderRange: renderRange, |
|
// Duration object that denotes the first visible time of any given day |
|
slotMinTime: props.slotMinTime, |
|
// Duration object that denotes the exclusive visible end time of any given day |
|
slotMaxTime: props.slotMaxTime, |
|
isValid: isValid, |
|
// how far the current date will move for a prev/next operation |
|
dateIncrement: this.buildDateIncrement(currentInfo.duration), |
|
}; |
|
}; |
|
// Builds an object with optional start/end properties. |
|
// Indicates the minimum/maximum dates to display. |
|
// not responsible for trimming hidden days. |
|
DateProfileGenerator.prototype.buildValidRange = function () { |
|
var input = this.props.validRangeInput; |
|
var simpleInput = typeof input === 'function' |
|
? input.call(this.props.calendarApi, this.nowDate) |
|
: input; |
|
return this.refineRange(simpleInput) || |
|
{ start: null, end: null }; // completely open-ended |
|
}; |
|
// Builds a structure with info about the "current" range, the range that is |
|
// highlighted as being the current month for example. |
|
// See build() for a description of `direction`. |
|
// Guaranteed to have `range` and `unit` properties. `duration` is optional. |
|
DateProfileGenerator.prototype.buildCurrentRangeInfo = function (date, direction) { |
|
var props = this.props; |
|
var duration = null; |
|
var unit = null; |
|
var range = null; |
|
var dayCount; |
|
if (props.duration) { |
|
duration = props.duration; |
|
unit = props.durationUnit; |
|
range = this.buildRangeFromDuration(date, direction, duration, unit); |
|
} |
|
else if ((dayCount = this.props.dayCount)) { |
|
unit = 'day'; |
|
range = this.buildRangeFromDayCount(date, direction, dayCount); |
|
} |
|
else if ((range = this.buildCustomVisibleRange(date))) { |
|
unit = props.dateEnv.greatestWholeUnit(range.start, range.end).unit; |
|
} |
|
else { |
|
duration = this.getFallbackDuration(); |
|
unit = greatestDurationDenominator(duration).unit; |
|
range = this.buildRangeFromDuration(date, direction, duration, unit); |
|
} |
|
return { duration: duration, unit: unit, range: range }; |
|
}; |
|
DateProfileGenerator.prototype.getFallbackDuration = function () { |
|
return createDuration({ day: 1 }); |
|
}; |
|
// Returns a new activeRange to have time values (un-ambiguate) |
|
// slotMinTime or slotMaxTime causes the range to expand. |
|
DateProfileGenerator.prototype.adjustActiveRange = function (range) { |
|
var _a = this.props, dateEnv = _a.dateEnv, usesMinMaxTime = _a.usesMinMaxTime, slotMinTime = _a.slotMinTime, slotMaxTime = _a.slotMaxTime; |
|
var start = range.start, end = range.end; |
|
if (usesMinMaxTime) { |
|
// expand active range if slotMinTime is negative (why not when positive?) |
|
if (asRoughDays(slotMinTime) < 0) { |
|
start = startOfDay(start); // necessary? |
|
start = dateEnv.add(start, slotMinTime); |
|
} |
|
// expand active range if slotMaxTime is beyond one day (why not when negative?) |
|
if (asRoughDays(slotMaxTime) > 1) { |
|
end = startOfDay(end); // necessary? |
|
end = addDays(end, -1); |
|
end = dateEnv.add(end, slotMaxTime); |
|
} |
|
} |
|
return { start: start, end: end }; |
|
}; |
|
// Builds the "current" range when it is specified as an explicit duration. |
|
// `unit` is the already-computed greatestDurationDenominator unit of duration. |
|
DateProfileGenerator.prototype.buildRangeFromDuration = function (date, direction, duration, unit) { |
|
var _a = this.props, dateEnv = _a.dateEnv, dateAlignment = _a.dateAlignment; |
|
var start; |
|
var end; |
|
var res; |
|
// compute what the alignment should be |
|
if (!dateAlignment) { |
|
var dateIncrement = this.props.dateIncrement; |
|
if (dateIncrement) { |
|
// use the smaller of the two units |
|
if (asRoughMs(dateIncrement) < asRoughMs(duration)) { |
|
dateAlignment = greatestDurationDenominator(dateIncrement).unit; |
|
} |
|
else { |
|
dateAlignment = unit; |
|
} |
|
} |
|
else { |
|
dateAlignment = unit; |
|
} |
|
} |
|
// if the view displays a single day or smaller |
|
if (asRoughDays(duration) <= 1) { |
|
if (this.isHiddenDay(start)) { |
|
start = this.skipHiddenDays(start, direction); |
|
start = startOfDay(start); |
|
} |
|
} |
|
function computeRes() { |
|
start = dateEnv.startOf(date, dateAlignment); |
|
end = dateEnv.add(start, duration); |
|
res = { start: start, end: end }; |
|
} |
|
computeRes(); |
|
// if range is completely enveloped by hidden days, go past the hidden days |
|
if (!this.trimHiddenDays(res)) { |
|
date = this.skipHiddenDays(date, direction); |
|
computeRes(); |
|
} |
|
return res; |
|
}; |
|
// Builds the "current" range when a dayCount is specified. |
|
DateProfileGenerator.prototype.buildRangeFromDayCount = function (date, direction, dayCount) { |
|
var _a = this.props, dateEnv = _a.dateEnv, dateAlignment = _a.dateAlignment; |
|
var runningCount = 0; |
|
var start = date; |
|
var end; |
|
if (dateAlignment) { |
|
start = dateEnv.startOf(start, dateAlignment); |
|
} |
|
start = startOfDay(start); |
|
start = this.skipHiddenDays(start, direction); |
|
end = start; |
|
do { |
|
end = addDays(end, 1); |
|
if (!this.isHiddenDay(end)) { |
|
runningCount += 1; |
|
} |
|
} while (runningCount < dayCount); |
|
return { start: start, end: end }; |
|
}; |
|
// Builds a normalized range object for the "visible" range, |
|
// which is a way to define the currentRange and activeRange at the same time. |
|
DateProfileGenerator.prototype.buildCustomVisibleRange = function (date) { |
|
var props = this.props; |
|
var input = props.visibleRangeInput; |
|
var simpleInput = typeof input === 'function' |
|
? input.call(props.calendarApi, props.dateEnv.toDate(date)) |
|
: input; |
|
var range = this.refineRange(simpleInput); |
|
if (range && (range.start == null || range.end == null)) { |
|
return null; |
|
} |
|
return range; |
|
}; |
|
// Computes the range that will represent the element/cells for *rendering*, |
|
// but which may have voided days/times. |
|
// not responsible for trimming hidden days. |
|
DateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) { |
|
return currentRange; |
|
}; |
|
// Compute the duration value that should be added/substracted to the current date |
|
// when a prev/next operation happens. |
|
DateProfileGenerator.prototype.buildDateIncrement = function (fallback) { |
|
var dateIncrement = this.props.dateIncrement; |
|
var customAlignment; |
|
if (dateIncrement) { |
|
return dateIncrement; |
|
} |
|
if ((customAlignment = this.props.dateAlignment)) { |
|
return createDuration(1, customAlignment); |
|
} |
|
if (fallback) { |
|
return fallback; |
|
} |
|
return createDuration({ days: 1 }); |
|
}; |
|
DateProfileGenerator.prototype.refineRange = function (rangeInput) { |
|
if (rangeInput) { |
|
var range = parseRange(rangeInput, this.props.dateEnv); |
|
if (range) { |
|
range = computeVisibleDayRange(range); |
|
} |
|
return range; |
|
} |
|
return null; |
|
}; |
|
/* Hidden Days |
|
------------------------------------------------------------------------------------------------------------------*/ |
|
// Initializes internal variables related to calculating hidden days-of-week |
|
DateProfileGenerator.prototype.initHiddenDays = function () { |
|
var hiddenDays = this.props.hiddenDays || []; // array of day-of-week indices that are hidden |
|
var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool) |
|
var dayCnt = 0; |
|
var i; |
|
if (this.props.weekends === false) { |
|
hiddenDays.push(0, 6); // 0=sunday, 6=saturday |
|
} |
|
for (i = 0; i < 7; i += 1) { |
|
if (!(isHiddenDayHash[i] = hiddenDays.indexOf(i) !== -1)) { |
|
dayCnt += 1; |
|
} |
|
} |
|
if (!dayCnt) { |
|
throw new Error('invalid hiddenDays'); // all days were hidden? bad. |
|
} |
|
this.isHiddenDayHash = isHiddenDayHash; |
|
}; |
|
// Remove days from the beginning and end of the range that are computed as hidden. |
|
// If the whole range is trimmed off, returns null |
|
DateProfileGenerator.prototype.trimHiddenDays = function (range) { |
|
var start = range.start, end = range.end; |
|
if (start) { |
|
start = this.skipHiddenDays(start); |
|
} |
|
if (end) { |
|
end = this.skipHiddenDays(end, -1, true); |
|
} |
|
if (start == null || end == null || start < end) { |
|
return { start: start, end: end }; |
|
} |
|
return null; |
|
}; |
|
// Is the current day hidden? |
|
// `day` is a day-of-week index (0-6), or a Date (used for UTC) |
|
DateProfileGenerator.prototype.isHiddenDay = function (day) { |
|
if (day instanceof Date) { |
|
day = day.getUTCDay(); |
|
} |
|
return this.isHiddenDayHash[day]; |
|
}; |
|
// Incrementing the current day until it is no longer a hidden day, returning a copy. |
|
// DOES NOT CONSIDER validRange! |
|
// If the initial value of `date` is not a hidden day, don't do anything. |
|
// Pass `isExclusive` as `true` if you are dealing with an end date. |
|
// `inc` defaults to `1` (increment one day forward each time) |
|
DateProfileGenerator.prototype.skipHiddenDays = function (date, inc, isExclusive) { |
|
if (inc === void 0) { inc = 1; } |
|
if (isExclusive === void 0) { isExclusive = false; } |
|
while (this.isHiddenDayHash[(date.getUTCDay() + (isExclusive ? inc : 0) + 7) % 7]) { |
|
date = addDays(date, inc); |
|
} |
|
return date; |
|
}; |
|
return DateProfileGenerator; |
|
}()); |
|
|
|
function reduceViewType(viewType, action) { |
|
switch (action.type) { |
|
case 'CHANGE_VIEW_TYPE': |
|
viewType = action.viewType; |
|
} |
|
return viewType; |
|
} |
|
|
|
function reduceDynamicOptionOverrides(dynamicOptionOverrides, action) { |
|
var _a; |
|
switch (action.type) { |
|
case 'SET_OPTION': |
|
return __assign(__assign({}, dynamicOptionOverrides), (_a = {}, _a[action.optionName] = action.rawOptionValue, _a)); |
|
default: |
|
return dynamicOptionOverrides; |
|
} |
|
} |
|
|
|
function reduceDateProfile(currentDateProfile, action, currentDate, dateProfileGenerator) { |
|
var dp; |
|
switch (action.type) { |
|
case 'CHANGE_VIEW_TYPE': |
|
return dateProfileGenerator.build(action.dateMarker || currentDate); |
|
case 'CHANGE_DATE': |
|
if (!currentDateProfile.activeRange || |
|
!rangeContainsMarker(currentDateProfile.currentRange, action.dateMarker) // don't move if date already in view |
|
) { |
|
return dateProfileGenerator.build(action.dateMarker); |
|
} |
|
break; |
|
case 'PREV': |
|
dp = dateProfileGenerator.buildPrev(currentDateProfile, currentDate); |
|
if (dp.isValid) { |
|
return dp; |
|
} |
|
break; |
|
case 'NEXT': |
|
dp = dateProfileGenerator.buildNext(currentDateProfile, currentDate); |
|
if (dp.isValid) { |
|
return dp; |
|
} |
|
break; |
|
} |
|
return currentDateProfile; |
|
} |
|
|
|
function initEventSources(calendarOptions, dateProfile, context) { |
|
var activeRange = dateProfile ? dateProfile.activeRange : null; |
|
return addSources({}, parseInitialSources(calendarOptions, context), activeRange, context); |
|
} |
|
function reduceEventSources(eventSources, action, dateProfile, context) { |
|
var activeRange = dateProfile ? dateProfile.activeRange : null; // need this check? |
|
switch (action.type) { |
|
case 'ADD_EVENT_SOURCES': // already parsed |
|
return addSources(eventSources, action.sources, activeRange, context); |
|
case 'REMOVE_EVENT_SOURCE': |
|
return removeSource(eventSources, action.sourceId); |
|
case 'PREV': // TODO: how do we track all actions that affect dateProfile :( |
|
case 'NEXT': |
|
case 'CHANGE_DATE': |
|
case 'CHANGE_VIEW_TYPE': |
|
if (dateProfile) { |
|
return fetchDirtySources(eventSources, activeRange, context); |
|
} |
|
return eventSources; |
|
case 'FETCH_EVENT_SOURCES': |
|
return fetchSourcesByIds(eventSources, action.sourceIds ? // why no type? |
|
arrayToHash(action.sourceIds) : |
|
excludeStaticSources(eventSources, context), activeRange, context); |
|
case 'RECEIVE_EVENTS': |
|
case 'RECEIVE_EVENT_ERROR': |
|
return receiveResponse(eventSources, action.sourceId, action.fetchId, action.fetchRange); |
|
case 'REMOVE_ALL_EVENT_SOURCES': |
|
return {}; |
|
default: |
|
return eventSources; |
|
} |
|
} |
|
function reduceEventSourcesNewTimeZone(eventSources, dateProfile, context) { |
|
var activeRange = dateProfile ? dateProfile.activeRange : null; // need this check? |
|
return fetchSourcesByIds(eventSources, excludeStaticSources(eventSources, context), activeRange, context); |
|
} |
|
function computeEventSourcesLoading(eventSources) { |
|
for (var sourceId in eventSources) { |
|
if (eventSources[sourceId].isFetching) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
function addSources(eventSourceHash, sources, fetchRange, context) { |
|
var hash = {}; |
|
for (var _i = 0, sources_1 = sources; _i < sources_1.length; _i++) { |
|
var source = sources_1[_i]; |
|
hash[source.sourceId] = source; |
|
} |
|
if (fetchRange) { |
|
hash = fetchDirtySources(hash, fetchRange, context); |
|
} |
|
return __assign(__assign({}, eventSourceHash), hash); |
|
} |
|
function removeSource(eventSourceHash, sourceId) { |
|
return filterHash(eventSourceHash, function (eventSource) { return eventSource.sourceId !== sourceId; }); |
|
} |
|
function fetchDirtySources(sourceHash, fetchRange, context) { |
|
return fetchSourcesByIds(sourceHash, filterHash(sourceHash, function (eventSource) { return isSourceDirty(eventSource, fetchRange, context); }), fetchRange, context); |
|
} |
|
function isSourceDirty(eventSource, fetchRange, context) { |
|
if (!doesSourceNeedRange(eventSource, context)) { |
|
return !eventSource.latestFetchId; |
|
} |
|
return !context.options.lazyFetching || |
|
!eventSource.fetchRange || |
|
eventSource.isFetching || // always cancel outdated in-progress fetches |
|
fetchRange.start < eventSource.fetchRange.start || |
|
fetchRange.end > eventSource.fetchRange.end; |
|
} |
|
function fetchSourcesByIds(prevSources, sourceIdHash, fetchRange, context) { |
|
var nextSources = {}; |
|
for (var sourceId in prevSources) { |
|
var source = prevSources[sourceId]; |
|
if (sourceIdHash[sourceId]) { |
|
nextSources[sourceId] = fetchSource(source, fetchRange, context); |
|
} |
|
else { |
|
nextSources[sourceId] = source; |
|
} |
|
} |
|
return nextSources; |
|
} |
|
function fetchSource(eventSource, fetchRange, context) { |
|
var options = context.options, calendarApi = context.calendarApi; |
|
var sourceDef = context.pluginHooks.eventSourceDefs[eventSource.sourceDefId]; |
|
var fetchId = guid(); |
|
sourceDef.fetch({ |
|
eventSource: eventSource, |
|
range: fetchRange, |
|
context: context, |
|
}, function (res) { |
|
var rawEvents = res.rawEvents; |
|
if (options.eventSourceSuccess) { |
|
rawEvents = options.eventSourceSuccess.call(calendarApi, rawEvents, res.xhr) || rawEvents; |
|
} |
|
if (eventSource.success) { |
|
rawEvents = eventSource.success.call(calendarApi, rawEvents, res.xhr) || rawEvents; |
|
} |
|
context.dispatch({ |
|
type: 'RECEIVE_EVENTS', |
|
sourceId: eventSource.sourceId, |
|
fetchId: fetchId, |
|
fetchRange: fetchRange, |
|
rawEvents: rawEvents, |
|
}); |
|
}, function (error) { |
|
console.warn(error.message, error); |
|
if (options.eventSourceFailure) { |
|
options.eventSourceFailure.call(calendarApi, error); |
|
} |
|
if (eventSource.failure) { |
|
eventSource.failure(error); |
|
} |
|
context.dispatch({ |
|
type: 'RECEIVE_EVENT_ERROR', |
|
sourceId: eventSource.sourceId, |
|
fetchId: fetchId, |
|
fetchRange: fetchRange, |
|
error: error, |
|
}); |
|
}); |
|
return __assign(__assign({}, eventSource), { isFetching: true, latestFetchId: fetchId }); |
|
} |
|
function receiveResponse(sourceHash, sourceId, fetchId, fetchRange) { |
|
var _a; |
|
var eventSource = sourceHash[sourceId]; |
|
if (eventSource && // not already removed |
|
fetchId === eventSource.latestFetchId) { |
|
return __assign(__assign({}, sourceHash), (_a = {}, _a[sourceId] = __assign(__assign({}, eventSource), { isFetching: false, fetchRange: fetchRange }), _a)); |
|
} |
|
return sourceHash; |
|
} |
|
function excludeStaticSources(eventSources, context) { |
|
return filterHash(eventSources, function (eventSource) { return doesSourceNeedRange(eventSource, context); }); |
|
} |
|
function parseInitialSources(rawOptions, context) { |
|
var refiners = buildEventSourceRefiners(context); |
|
var rawSources = [].concat(rawOptions.eventSources || []); |
|
var sources = []; // parsed |
|
if (rawOptions.initialEvents) { |
|
rawSources.unshift(rawOptions.initialEvents); |
|
} |
|
if (rawOptions.events) { |
|
rawSources.unshift(rawOptions.events); |
|
} |
|
for (var _i = 0, rawSources_1 = rawSources; _i < rawSources_1.length; _i++) { |
|
var rawSource = rawSources_1[_i]; |
|
var source = parseEventSource(rawSource, context, refiners); |
|
if (source) { |
|
sources.push(source); |
|
} |
|
} |
|
return sources; |
|
} |
|
function doesSourceNeedRange(eventSource, context) { |
|
var defs = context.pluginHooks.eventSourceDefs; |
|
return !defs[eventSource.sourceDefId].ignoreRange; |
|
} |
|
|
|
function reduceDateSelection(currentSelection, action) { |
|
switch (action.type) { |
|
case 'UNSELECT_DATES': |
|
return null; |
|
case 'SELECT_DATES': |
|
return action.selection; |
|
default: |
|
return currentSelection; |
|
} |
|
} |
|
|
|
function reduceSelectedEvent(currentInstanceId, action) { |
|
switch (action.type) { |
|
case 'UNSELECT_EVENT': |
|
return ''; |
|
case 'SELECT_EVENT': |
|
return action.eventInstanceId; |
|
default: |
|
return currentInstanceId; |
|
} |
|
} |
|
|
|
function reduceEventDrag(currentDrag, action) { |
|
var newDrag; |
|
switch (action.type) { |
|
case 'UNSET_EVENT_DRAG': |
|
return null; |
|
case 'SET_EVENT_DRAG': |
|
newDrag = action.state; |
|
return { |
|
affectedEvents: newDrag.affectedEvents, |
|
mutatedEvents: newDrag.mutatedEvents, |
|
isEvent: newDrag.isEvent, |
|
}; |
|
default: |
|
return currentDrag; |
|
} |
|
} |
|
|
|
function reduceEventResize(currentResize, action) { |
|
var newResize; |
|
switch (action.type) { |
|
case 'UNSET_EVENT_RESIZE': |
|
return null; |
|
case 'SET_EVENT_RESIZE': |
|
newResize = action.state; |
|
return { |
|
affectedEvents: newResize.affectedEvents, |
|
mutatedEvents: newResize.mutatedEvents, |
|
isEvent: newResize.isEvent, |
|
}; |
|
default: |
|
return currentResize; |
|
} |
|
} |
|
|
|
function parseToolbars(calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi) { |
|
var viewsWithButtons = []; |
|
var headerToolbar = calendarOptions.headerToolbar ? parseToolbar(calendarOptions.headerToolbar, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons) : null; |
|
var footerToolbar = calendarOptions.footerToolbar ? parseToolbar(calendarOptions.footerToolbar, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons) : null; |
|
return { headerToolbar: headerToolbar, footerToolbar: footerToolbar, viewsWithButtons: viewsWithButtons }; |
|
} |
|
function parseToolbar(sectionStrHash, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons) { |
|
return mapHash(sectionStrHash, function (sectionStr) { return parseSection(sectionStr, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons); }); |
|
} |
|
/* |
|
BAD: querying icons and text here. should be done at render time |
|
*/ |
|
function parseSection(sectionStr, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi, viewsWithButtons) { |
|
var isRtl = calendarOptions.direction === 'rtl'; |
|
var calendarCustomButtons = calendarOptions.customButtons || {}; |
|
var calendarButtonTextOverrides = calendarOptionOverrides.buttonText || {}; |
|
var calendarButtonText = calendarOptions.buttonText || {}; |
|
var sectionSubstrs = sectionStr ? sectionStr.split(' ') : []; |
|
return sectionSubstrs.map(function (buttonGroupStr) { return (buttonGroupStr.split(',').map(function (buttonName) { |
|
if (buttonName === 'title') { |
|
return { buttonName: buttonName }; |
|
} |
|
var customButtonProps; |
|
var viewSpec; |
|
var buttonClick; |
|
var buttonIcon; // only one of these will be set |
|
var buttonText; // " |
|
if ((customButtonProps = calendarCustomButtons[buttonName])) { |
|
buttonClick = function (ev) { |
|
if (customButtonProps.click) { |
|
customButtonProps.click.call(ev.target, ev, ev.target); |
|
} |
|
}; |
|
(buttonIcon = theme.getCustomButtonIconClass(customButtonProps)) || |
|
(buttonIcon = theme.getIconClass(buttonName, isRtl)) || |
|
(buttonText = customButtonProps.text); |
|
} |
|
else if ((viewSpec = viewSpecs[buttonName])) { |
|
viewsWithButtons.push(buttonName); |
|
buttonClick = function () { |
|
calendarApi.changeView(buttonName); |
|
}; |
|
(buttonText = viewSpec.buttonTextOverride) || |
|
(buttonIcon = theme.getIconClass(buttonName, isRtl)) || |
|
(buttonText = viewSpec.buttonTextDefault); |
|
} |
|
else if (calendarApi[buttonName]) { // a calendarApi method |
|
buttonClick = function () { |
|
calendarApi[buttonName](); |
|
}; |
|
(buttonText = calendarButtonTextOverrides[buttonName]) || |
|
(buttonIcon = theme.getIconClass(buttonName, isRtl)) || |
|
(buttonText = calendarButtonText[buttonName]); |
|
// ^ everything else is considered default |
|
} |
|
return { buttonName: buttonName, buttonClick: buttonClick, buttonIcon: buttonIcon, buttonText: buttonText }; |
|
})); }); |
|
} |
|
|
|
var eventSourceDef = { |
|
ignoreRange: true, |
|
parseMeta: function (refined) { |
|
if (Array.isArray(refined.events)) { |
|
return refined.events; |
|
} |
|
return null; |
|
}, |
|
fetch: function (arg, success) { |
|
success({ |
|
rawEvents: arg.eventSource.meta, |
|
}); |
|
}, |
|
}; |
|
var arrayEventSourcePlugin = createPlugin({ |
|
eventSourceDefs: [eventSourceDef], |
|
}); |
|
|
|
var eventSourceDef$1 = { |
|
parseMeta: function (refined) { |
|
if (typeof refined.events === 'function') { |
|
return refined.events; |
|
} |
|
return null; |
|
}, |
|
fetch: function (arg, success, failure) { |
|
var dateEnv = arg.context.dateEnv; |
|
var func = arg.eventSource.meta; |
|
unpromisify(func.bind(null, buildRangeApiWithTimeZone(arg.range, dateEnv)), function (rawEvents) { |
|
success({ rawEvents: rawEvents }); // needs an object response |
|
}, failure); |
|
}, |
|
}; |
|
var funcEventSourcePlugin = createPlugin({ |
|
eventSourceDefs: [eventSourceDef$1], |
|
}); |
|
|
|
function requestJson(method, url, params, successCallback, failureCallback) { |
|
method = method.toUpperCase(); |
|
var body = null; |
|
if (method === 'GET') { |
|
url = injectQueryStringParams(url, params); |
|
} |
|
else { |
|
body = encodeParams(params); |
|
} |
|
var xhr = new XMLHttpRequest(); |
|
xhr.open(method, url, true); |
|
if (method !== 'GET') { |
|
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); |
|
} |
|
xhr.onload = function () { |
|
if (xhr.status >= 200 && xhr.status < 400) { |
|
var parsed = false; |
|
var res = void 0; |
|
try { |
|
res = JSON.parse(xhr.responseText); |
|
parsed = true; |
|
} |
|
catch (err) { |
|
// will handle parsed=false |
|
} |
|
if (parsed) { |
|
successCallback(res, xhr); |
|
} |
|
else { |
|
failureCallback('Failure parsing JSON', xhr); |
|
} |
|
} |
|
else { |
|
failureCallback('Request failed', xhr); |
|
} |
|
}; |
|
xhr.onerror = function () { |
|
failureCallback('Request failed', xhr); |
|
}; |
|
xhr.send(body); |
|
} |
|
function injectQueryStringParams(url, params) { |
|
return url + |
|
(url.indexOf('?') === -1 ? '?' : '&') + |
|
encodeParams(params); |
|
} |
|
function encodeParams(params) { |
|
var parts = []; |
|
for (var key in params) { |
|
parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(params[key])); |
|
} |
|
return parts.join('&'); |
|
} |
|
|
|
var JSON_FEED_EVENT_SOURCE_REFINERS = { |
|
method: String, |
|
extraParams: identity, |
|
startParam: String, |
|
endParam: String, |
|
timeZoneParam: String, |
|
}; |
|
|
|
var eventSourceDef$2 = { |
|
parseMeta: function (refined) { |
|
if (refined.url && (refined.format === 'json' || !refined.format)) { |
|
return { |
|
url: refined.url, |
|
format: 'json', |
|
method: (refined.method || 'GET').toUpperCase(), |
|
extraParams: refined.extraParams, |
|
startParam: refined.startParam, |
|
endParam: refined.endParam, |
|
timeZoneParam: refined.timeZoneParam, |
|
}; |
|
} |
|
return null; |
|
}, |
|
fetch: function (arg, success, failure) { |
|
var meta = arg.eventSource.meta; |
|
var requestParams = buildRequestParams(meta, arg.range, arg.context); |
|
requestJson(meta.method, meta.url, requestParams, function (rawEvents, xhr) { |
|
success({ rawEvents: rawEvents, xhr: xhr }); |
|
}, function (errorMessage, xhr) { |
|
failure({ message: errorMessage, xhr: xhr }); |
|
}); |
|
}, |
|
}; |
|
var jsonFeedEventSourcePlugin = createPlugin({ |
|
eventSourceRefiners: JSON_FEED_EVENT_SOURCE_REFINERS, |
|
eventSourceDefs: [eventSourceDef$2], |
|
}); |
|
function buildRequestParams(meta, range, context) { |
|
var dateEnv = context.dateEnv, options = context.options; |
|
var startParam; |
|
var endParam; |
|
var timeZoneParam; |
|
var customRequestParams; |
|
var params = {}; |
|
startParam = meta.startParam; |
|
if (startParam == null) { |
|
startParam = options.startParam; |
|
} |
|
endParam = meta.endParam; |
|
if (endParam == null) { |
|
endParam = options.endParam; |
|
} |
|
timeZoneParam = meta.timeZoneParam; |
|
if (timeZoneParam == null) { |
|
timeZoneParam = options.timeZoneParam; |
|
} |
|
// retrieve any outbound GET/POST data from the options |
|
if (typeof meta.extraParams === 'function') { |
|
// supplied as a function that returns a key/value object |
|
customRequestParams = meta.extraParams(); |
|
} |
|
else { |
|
// probably supplied as a straight key/value object |
|
customRequestParams = meta.extraParams || {}; |
|
} |
|
__assign(params, customRequestParams); |
|
params[startParam] = dateEnv.formatIso(range.start); |
|
params[endParam] = dateEnv.formatIso(range.end); |
|
if (dateEnv.timeZone !== 'local') { |
|
params[timeZoneParam] = dateEnv.timeZone; |
|
} |
|
return params; |
|
} |
|
|
|
var SIMPLE_RECURRING_REFINERS = { |
|
daysOfWeek: identity, |
|
startTime: createDuration, |
|
endTime: createDuration, |
|
duration: createDuration, |
|
startRecur: identity, |
|
endRecur: identity, |
|
}; |
|
|
|
var recurring = { |
|
parse: function (refined, dateEnv) { |
|
if (refined.daysOfWeek || refined.startTime || refined.endTime || refined.startRecur || refined.endRecur) { |
|
var recurringData = { |
|
daysOfWeek: refined.daysOfWeek || null, |
|
startTime: refined.startTime || null, |
|
endTime: refined.endTime || null, |
|
startRecur: refined.startRecur ? dateEnv.createMarker(refined.startRecur) : null, |
|
endRecur: refined.endRecur ? dateEnv.createMarker(refined.endRecur) : null, |
|
}; |
|
var duration = void 0; |
|
if (refined.duration) { |
|
duration = refined.duration; |
|
} |
|
if (!duration && refined.startTime && refined.endTime) { |
|
duration = subtractDurations(refined.endTime, refined.startTime); |
|
} |
|
return { |
|
allDayGuess: Boolean(!refined.startTime && !refined.endTime), |
|
duration: duration, |
|
typeData: recurringData, |
|
}; |
|
} |
|
return null; |
|
}, |
|
expand: function (typeData, framingRange, dateEnv) { |
|
var clippedFramingRange = intersectRanges(framingRange, { start: typeData.startRecur, end: typeData.endRecur }); |
|
if (clippedFramingRange) { |
|
return expandRanges(typeData.daysOfWeek, typeData.startTime, clippedFramingRange, dateEnv); |
|
} |
|
return []; |
|
}, |
|
}; |
|
var simpleRecurringEventsPlugin = createPlugin({ |
|
recurringTypes: [recurring], |
|
eventRefiners: SIMPLE_RECURRING_REFINERS, |
|
}); |
|
function expandRanges(daysOfWeek, startTime, framingRange, dateEnv) { |
|
var dowHash = daysOfWeek ? arrayToHash(daysOfWeek) : null; |
|
var dayMarker = startOfDay(framingRange.start); |
|
var endMarker = framingRange.end; |
|
var instanceStarts = []; |
|
while (dayMarker < endMarker) { |
|
var instanceStart |
|
// if everyday, or this particular day-of-week |
|
= void 0; |
|
// if everyday, or this particular day-of-week |
|
if (!dowHash || dowHash[dayMarker.getUTCDay()]) { |
|
if (startTime) { |
|
instanceStart = dateEnv.add(dayMarker, startTime); |
|
} |
|
else { |
|
instanceStart = dayMarker; |
|
} |
|
instanceStarts.push(instanceStart); |
|
} |
|
dayMarker = addDays(dayMarker, 1); |
|
} |
|
return instanceStarts; |
|
} |
|
|
|
var changeHandlerPlugin = createPlugin({ |
|
optionChangeHandlers: { |
|
events: function (events, context) { |
|
handleEventSources([events], context); |
|
}, |
|
eventSources: handleEventSources, |
|
}, |
|
}); |
|
/* |
|
BUG: if `event` was supplied, all previously-given `eventSources` will be wiped out |
|
*/ |
|
function handleEventSources(inputs, context) { |
|
var unfoundSources = hashValuesToArray(context.getCurrentData().eventSources); |
|
var newInputs = []; |
|
for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) { |
|
var input = inputs_1[_i]; |
|
var inputFound = false; |
|
for (var i = 0; i < unfoundSources.length; i += 1) { |
|
if (unfoundSources[i]._raw === input) { |
|
unfoundSources.splice(i, 1); // delete |
|
inputFound = true; |
|
break; |
|
} |
|
} |
|
if (!inputFound) { |
|
newInputs.push(input); |
|
} |
|
} |
|
for (var _a = 0, unfoundSources_1 = unfoundSources; _a < unfoundSources_1.length; _a++) { |
|
var unfoundSource = unfoundSources_1[_a]; |
|
context.dispatch({ |
|
type: 'REMOVE_EVENT_SOURCE', |
|
sourceId: unfoundSource.sourceId, |
|
}); |
|
} |
|
for (var _b = 0, newInputs_1 = newInputs; _b < newInputs_1.length; _b++) { |
|
var newInput = newInputs_1[_b]; |
|
context.calendarApi.addEventSource(newInput); |
|
} |
|
} |
|
|
|
function handleDateProfile(dateProfile, context) { |
|
context.emitter.trigger('datesSet', __assign(__assign({}, buildRangeApiWithTimeZone(dateProfile.activeRange, context.dateEnv)), { view: context.viewApi })); |
|
} |
|
|
|
function handleEventStore(eventStore, context) { |
|
var emitter = context.emitter; |
|
if (emitter.hasHandlers('eventsSet')) { |
|
emitter.trigger('eventsSet', buildEventApis(eventStore, context)); |
|
} |
|
} |
|
|
|
/* |
|
this array is exposed on the root namespace so that UMD plugins can add to it. |
|
see the rollup-bundles script. |
|
*/ |
|
var globalPlugins = [ |
|
arrayEventSourcePlugin, |
|
funcEventSourcePlugin, |
|
jsonFeedEventSourcePlugin, |
|
simpleRecurringEventsPlugin, |
|
changeHandlerPlugin, |
|
createPlugin({ |
|
isLoadingFuncs: [ |
|
function (state) { return computeEventSourcesLoading(state.eventSources); }, |
|
], |
|
contentTypeHandlers: { |
|
html: function () { return ({ render: injectHtml }); }, |
|
domNodes: function () { return ({ render: injectDomNodes }); }, |
|
}, |
|
propSetHandlers: { |
|
dateProfile: handleDateProfile, |
|
eventStore: handleEventStore, |
|
}, |
|
}), |
|
]; |
|
function injectHtml(el, html) { |
|
el.innerHTML = html; |
|
} |
|
function injectDomNodes(el, domNodes) { |
|
var oldNodes = Array.prototype.slice.call(el.childNodes); // TODO: use array util |
|
var newNodes = Array.prototype.slice.call(domNodes); // TODO: use array util |
|
if (!isArraysEqual(oldNodes, newNodes)) { |
|
for (var _i = 0, newNodes_1 = newNodes; _i < newNodes_1.length; _i++) { |
|
var newNode = newNodes_1[_i]; |
|
el.appendChild(newNode); |
|
} |
|
oldNodes.forEach(removeElement); |
|
} |
|
} |
|
|
|
var DelayedRunner = /** @class */ (function () { |
|
function DelayedRunner(drainedOption) { |
|
this.drainedOption = drainedOption; |
|
this.isRunning = false; |
|
this.isDirty = false; |
|
this.pauseDepths = {}; |
|
this.timeoutId = 0; |
|
} |
|
DelayedRunner.prototype.request = function (delay) { |
|
this.isDirty = true; |
|
if (!this.isPaused()) { |
|
this.clearTimeout(); |
|
if (delay == null) { |
|
this.tryDrain(); |
|
} |
|
else { |
|
this.timeoutId = setTimeout(// NOT OPTIMAL! TODO: look at debounce |
|
this.tryDrain.bind(this), delay); |
|
} |
|
} |
|
}; |
|
DelayedRunner.prototype.pause = function (scope) { |
|
if (scope === void 0) { scope = ''; } |
|
var pauseDepths = this.pauseDepths; |
|
pauseDepths[scope] = (pauseDepths[scope] || 0) + 1; |
|
this.clearTimeout(); |
|
}; |
|
DelayedRunner.prototype.resume = function (scope, force) { |
|
if (scope === void 0) { scope = ''; } |
|
var pauseDepths = this.pauseDepths; |
|
if (scope in pauseDepths) { |
|
if (force) { |
|
delete pauseDepths[scope]; |
|
} |
|
else { |
|
pauseDepths[scope] -= 1; |
|
var depth = pauseDepths[scope]; |
|
if (depth <= 0) { |
|
delete pauseDepths[scope]; |
|
} |
|
} |
|
this.tryDrain(); |
|
} |
|
}; |
|
DelayedRunner.prototype.isPaused = function () { |
|
return Object.keys(this.pauseDepths).length; |
|
}; |
|
DelayedRunner.prototype.tryDrain = function () { |
|
if (!this.isRunning && !this.isPaused()) { |
|
this.isRunning = true; |
|
while (this.isDirty) { |
|
this.isDirty = false; |
|
this.drained(); // might set isDirty to true again |
|
} |
|
this.isRunning = false; |
|
} |
|
}; |
|
DelayedRunner.prototype.clear = function () { |
|
this.clearTimeout(); |
|
this.isDirty = false; |
|
this.pauseDepths = {}; |
|
}; |
|
DelayedRunner.prototype.clearTimeout = function () { |
|
if (this.timeoutId) { |
|
clearTimeout(this.timeoutId); |
|
this.timeoutId = 0; |
|
} |
|
}; |
|
DelayedRunner.prototype.drained = function () { |
|
if (this.drainedOption) { |
|
this.drainedOption(); |
|
} |
|
}; |
|
return DelayedRunner; |
|
}()); |
|
|
|
var TaskRunner = /** @class */ (function () { |
|
function TaskRunner(runTaskOption, drainedOption) { |
|
this.runTaskOption = runTaskOption; |
|
this.drainedOption = drainedOption; |
|
this.queue = []; |
|
this.delayedRunner = new DelayedRunner(this.drain.bind(this)); |
|
} |
|
TaskRunner.prototype.request = function (task, delay) { |
|
this.queue.push(task); |
|
this.delayedRunner.request(delay); |
|
}; |
|
TaskRunner.prototype.pause = function (scope) { |
|
this.delayedRunner.pause(scope); |
|
}; |
|
TaskRunner.prototype.resume = function (scope, force) { |
|
this.delayedRunner.resume(scope, force); |
|
}; |
|
TaskRunner.prototype.drain = function () { |
|
var queue = this.queue; |
|
while (queue.length) { |
|
var completedTasks = []; |
|
var task = void 0; |
|
while ((task = queue.shift())) { |
|
this.runTask(task); |
|
completedTasks.push(task); |
|
} |
|
this.drained(completedTasks); |
|
} // keep going, in case new tasks were added in the drained handler |
|
}; |
|
TaskRunner.prototype.runTask = function (task) { |
|
if (this.runTaskOption) { |
|
this.runTaskOption(task); |
|
} |
|
}; |
|
TaskRunner.prototype.drained = function (completedTasks) { |
|
if (this.drainedOption) { |
|
this.drainedOption(completedTasks); |
|
} |
|
}; |
|
return TaskRunner; |
|
}()); |
|
|
|
// Computes what the title at the top of the calendarApi should be for this view |
|
function buildTitle(dateProfile, viewOptions, dateEnv) { |
|
var range; |
|
// for views that span a large unit of time, show the proper interval, ignoring stray days before and after |
|
if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) { |
|
range = dateProfile.currentRange; |
|
} |
|
else { // for day units or smaller, use the actual day range |
|
range = dateProfile.activeRange; |
|
} |
|
return dateEnv.formatRange(range.start, range.end, createFormatter(viewOptions.titleFormat || buildTitleFormat(dateProfile)), { |
|
isEndExclusive: dateProfile.isRangeAllDay, |
|
defaultSeparator: viewOptions.titleRangeSeparator, |
|
}); |
|
} |
|
// Generates the format string that should be used to generate the title for the current date range. |
|
// Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`. |
|
function buildTitleFormat(dateProfile) { |
|
var currentRangeUnit = dateProfile.currentRangeUnit; |
|
if (currentRangeUnit === 'year') { |
|
return { year: 'numeric' }; |
|
} |
|
if (currentRangeUnit === 'month') { |
|
return { year: 'numeric', month: 'long' }; // like "September 2014" |
|
} |
|
var days = diffWholeDays(dateProfile.currentRange.start, dateProfile.currentRange.end); |
|
if (days !== null && days > 1) { |
|
// multi-day range. shorter, like "Sep 9 - 10 2014" |
|
return { year: 'numeric', month: 'short', day: 'numeric' }; |
|
} |
|
// one day. longer, like "September 9 2014" |
|
return { year: 'numeric', month: 'long', day: 'numeric' }; |
|
} |
|
|
|
// in future refactor, do the redux-style function(state=initial) for initial-state |
|
// also, whatever is happening in constructor, have it happen in action queue too |
|
var CalendarDataManager = /** @class */ (function () { |
|
function CalendarDataManager(props) { |
|
var _this = this; |
|
this.computeOptionsData = memoize(this._computeOptionsData); |
|
this.computeCurrentViewData = memoize(this._computeCurrentViewData); |
|
this.organizeRawLocales = memoize(organizeRawLocales); |
|
this.buildLocale = memoize(buildLocale); |
|
this.buildPluginHooks = buildBuildPluginHooks(); |
|
this.buildDateEnv = memoize(buildDateEnv$1); |
|
this.buildTheme = memoize(buildTheme); |
|
this.parseToolbars = memoize(parseToolbars); |
|
this.buildViewSpecs = memoize(buildViewSpecs); |
|
this.buildDateProfileGenerator = memoizeObjArg(buildDateProfileGenerator); |
|
this.buildViewApi = memoize(buildViewApi); |
|
this.buildViewUiProps = memoizeObjArg(buildViewUiProps); |
|
this.buildEventUiBySource = memoize(buildEventUiBySource, isPropsEqual); |
|
this.buildEventUiBases = memoize(buildEventUiBases); |
|
this.parseContextBusinessHours = memoizeObjArg(parseContextBusinessHours); |
|
this.buildTitle = memoize(buildTitle); |
|
this.emitter = new Emitter(); |
|
this.actionRunner = new TaskRunner(this._handleAction.bind(this), this.updateData.bind(this)); |
|
this.currentCalendarOptionsInput = {}; |
|
this.currentCalendarOptionsRefined = {}; |
|
this.currentViewOptionsInput = {}; |
|
this.currentViewOptionsRefined = {}; |
|
this.currentCalendarOptionsRefiners = {}; |
|
this.getCurrentData = function () { return _this.data; }; |
|
this.dispatch = function (action) { |
|
_this.actionRunner.request(action); // protects against recursive calls to _handleAction |
|
}; |
|
this.props = props; |
|
this.actionRunner.pause(); |
|
var dynamicOptionOverrides = {}; |
|
var optionsData = this.computeOptionsData(props.optionOverrides, dynamicOptionOverrides, props.calendarApi); |
|
var currentViewType = optionsData.calendarOptions.initialView || optionsData.pluginHooks.initialView; |
|
var currentViewData = this.computeCurrentViewData(currentViewType, optionsData, props.optionOverrides, dynamicOptionOverrides); |
|
// wire things up |
|
// TODO: not DRY |
|
props.calendarApi.currentDataManager = this; |
|
this.emitter.setThisContext(props.calendarApi); |
|
this.emitter.setOptions(currentViewData.options); |
|
var currentDate = getInitialDate(optionsData.calendarOptions, optionsData.dateEnv); |
|
var dateProfile = currentViewData.dateProfileGenerator.build(currentDate); |
|
if (!rangeContainsMarker(dateProfile.activeRange, currentDate)) { |
|
currentDate = dateProfile.currentRange.start; |
|
} |
|
var calendarContext = { |
|
dateEnv: optionsData.dateEnv, |
|
options: optionsData.calendarOptions, |
|
pluginHooks: optionsData.pluginHooks, |
|
calendarApi: props.calendarApi, |
|
dispatch: this.dispatch, |
|
emitter: this.emitter, |
|
getCurrentData: this.getCurrentData, |
|
}; |
|
// needs to be after setThisContext |
|
for (var _i = 0, _a = optionsData.pluginHooks.contextInit; _i < _a.length; _i++) { |
|
var callback = _a[_i]; |
|
callback(calendarContext); |
|
} |
|
// NOT DRY |
|
var eventSources = initEventSources(optionsData.calendarOptions, dateProfile, calendarContext); |
|
var initialState = { |
|
dynamicOptionOverrides: dynamicOptionOverrides, |
|
currentViewType: currentViewType, |
|
currentDate: currentDate, |
|
dateProfile: dateProfile, |
|
businessHours: this.parseContextBusinessHours(calendarContext), |
|
eventSources: eventSources, |
|
eventUiBases: {}, |
|
eventStore: createEmptyEventStore(), |
|
renderableEventStore: createEmptyEventStore(), |
|
dateSelection: null, |
|
eventSelection: '', |
|
eventDrag: null, |
|
eventResize: null, |
|
selectionConfig: this.buildViewUiProps(calendarContext).selectionConfig, |
|
}; |
|
var contextAndState = __assign(__assign({}, calendarContext), initialState); |
|
for (var _b = 0, _c = optionsData.pluginHooks.reducers; _b < _c.length; _b++) { |
|
var reducer = _c[_b]; |
|
__assign(initialState, reducer(null, null, contextAndState)); |
|
} |
|
if (computeIsLoading(initialState, calendarContext)) { |
|
this.emitter.trigger('loading', true); // NOT DRY |
|
} |
|
this.state = initialState; |
|
this.updateData(); |
|
this.actionRunner.resume(); |
|
} |
|
CalendarDataManager.prototype.resetOptions = function (optionOverrides, append) { |
|
var props = this.props; |
|
props.optionOverrides = append |
|
? __assign(__assign({}, props.optionOverrides), optionOverrides) : optionOverrides; |
|
this.actionRunner.request({ |
|
type: 'NOTHING', |
|
}); |
|
}; |
|
CalendarDataManager.prototype._handleAction = function (action) { |
|
var _a = this, props = _a.props, state = _a.state, emitter = _a.emitter; |
|
var dynamicOptionOverrides = reduceDynamicOptionOverrides(state.dynamicOptionOverrides, action); |
|
var optionsData = this.computeOptionsData(props.optionOverrides, dynamicOptionOverrides, props.calendarApi); |
|
var currentViewType = reduceViewType(state.currentViewType, action); |
|
var currentViewData = this.computeCurrentViewData(currentViewType, optionsData, props.optionOverrides, dynamicOptionOverrides); |
|
// wire things up |
|
// TODO: not DRY |
|
props.calendarApi.currentDataManager = this; |
|
emitter.setThisContext(props.calendarApi); |
|
emitter.setOptions(currentViewData.options); |
|
var calendarContext = { |
|
dateEnv: optionsData.dateEnv, |
|
options: optionsData.calendarOptions, |
|
pluginHooks: optionsData.pluginHooks, |
|
calendarApi: props.calendarApi, |
|
dispatch: this.dispatch, |
|
emitter: emitter, |
|
getCurrentData: this.getCurrentData, |
|
}; |
|
var currentDate = state.currentDate, dateProfile = state.dateProfile; |
|
if (this.data && this.data.dateProfileGenerator !== currentViewData.dateProfileGenerator) { // hack |
|
dateProfile = currentViewData.dateProfileGenerator.build(currentDate); |
|
} |
|
currentDate = reduceCurrentDate(currentDate, action); |
|
dateProfile = reduceDateProfile(dateProfile, action, currentDate, currentViewData.dateProfileGenerator); |
|
if (!rangeContainsMarker(dateProfile.currentRange, currentDate)) { |
|
currentDate = dateProfile.currentRange.start; |
|
} |
|
var eventSources = reduceEventSources(state.eventSources, action, dateProfile, calendarContext); |
|
var eventStore = reduceEventStore(state.eventStore, action, eventSources, dateProfile, calendarContext); |
|
var isEventsLoading = computeEventSourcesLoading(eventSources); // BAD. also called in this func in computeIsLoading |
|
var renderableEventStore = (isEventsLoading && !currentViewData.options.progressiveEventRendering) ? |
|
(state.renderableEventStore || eventStore) : // try from previous state |
|
eventStore; |
|
var _b = this.buildViewUiProps(calendarContext), eventUiSingleBase = _b.eventUiSingleBase, selectionConfig = _b.selectionConfig; // will memoize obj |
|
var eventUiBySource = this.buildEventUiBySource(eventSources); |
|
var eventUiBases = this.buildEventUiBases(renderableEventStore.defs, eventUiSingleBase, eventUiBySource); |
|
var newState = { |
|
dynamicOptionOverrides: dynamicOptionOverrides, |
|
currentViewType: currentViewType, |
|
currentDate: currentDate, |
|
dateProfile: dateProfile, |
|
eventSources: eventSources, |
|
eventStore: eventStore, |
|
renderableEventStore: renderableEventStore, |
|
selectionConfig: selectionConfig, |
|
eventUiBases: eventUiBases, |
|
businessHours: this.parseContextBusinessHours(calendarContext), |
|
dateSelection: reduceDateSelection(state.dateSelection, action), |
|
eventSelection: reduceSelectedEvent(state.eventSelection, action), |
|
eventDrag: reduceEventDrag(state.eventDrag, action), |
|
eventResize: reduceEventResize(state.eventResize, action), |
|
}; |
|
var contextAndState = __assign(__assign({}, calendarContext), newState); |
|
for (var _i = 0, _c = optionsData.pluginHooks.reducers; _i < _c.length; _i++) { |
|
var reducer = _c[_i]; |
|
__assign(newState, reducer(state, action, contextAndState)); // give the OLD state, for old value |
|
} |
|
var wasLoading = computeIsLoading(state, calendarContext); |
|
var isLoading = computeIsLoading(newState, calendarContext); |
|
// TODO: use propSetHandlers in plugin system |
|
if (!wasLoading && isLoading) { |
|
emitter.trigger('loading', true); |
|
} |
|
else if (wasLoading && !isLoading) { |
|
emitter.trigger('loading', false); |
|
} |
|
this.state = newState; |
|
if (props.onAction) { |
|
props.onAction(action); |
|
} |
|
}; |
|
CalendarDataManager.prototype.updateData = function () { |
|
var _a = this, props = _a.props, state = _a.state; |
|
var oldData = this.data; |
|
var optionsData = this.computeOptionsData(props.optionOverrides, state.dynamicOptionOverrides, props.calendarApi); |
|
var currentViewData = this.computeCurrentViewData(state.currentViewType, optionsData, props.optionOverrides, state.dynamicOptionOverrides); |
|
var data = this.data = __assign(__assign(__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); |
|
var changeHandlers = optionsData.pluginHooks.optionChangeHandlers; |
|
var oldCalendarOptions = oldData && oldData.calendarOptions; |
|
var newCalendarOptions = optionsData.calendarOptions; |
|
if (oldCalendarOptions && oldCalendarOptions !== newCalendarOptions) { |
|
if (oldCalendarOptions.timeZone !== newCalendarOptions.timeZone) { |
|
// hack |
|
state.eventSources = data.eventSources = reduceEventSourcesNewTimeZone(data.eventSources, state.dateProfile, data); |
|
state.eventStore = data.eventStore = rezoneEventStoreDates(data.eventStore, oldData.dateEnv, data.dateEnv); |
|
} |
|
for (var optionName in changeHandlers) { |
|
if (oldCalendarOptions[optionName] !== newCalendarOptions[optionName]) { |
|
changeHandlers[optionName](newCalendarOptions[optionName], data); |
|
} |
|
} |
|
} |
|
if (props.onData) { |
|
props.onData(data); |
|
} |
|
}; |
|
CalendarDataManager.prototype._computeOptionsData = function (optionOverrides, dynamicOptionOverrides, calendarApi) { |
|
// TODO: blacklist options that are handled by optionChangeHandlers |
|
var _a = this.processRawCalendarOptions(optionOverrides, dynamicOptionOverrides), refinedOptions = _a.refinedOptions, pluginHooks = _a.pluginHooks, localeDefaults = _a.localeDefaults, availableLocaleData = _a.availableLocaleData, extra = _a.extra; |
|
warnUnknownOptions(extra); |
|
var dateEnv = this.buildDateEnv(refinedOptions.timeZone, refinedOptions.locale, refinedOptions.weekNumberCalculation, refinedOptions.firstDay, refinedOptions.weekText, pluginHooks, availableLocaleData, refinedOptions.defaultRangeSeparator); |
|
var viewSpecs = this.buildViewSpecs(pluginHooks.views, optionOverrides, dynamicOptionOverrides, localeDefaults); |
|
var theme = this.buildTheme(refinedOptions, pluginHooks); |
|
var toolbarConfig = this.parseToolbars(refinedOptions, optionOverrides, theme, viewSpecs, calendarApi); |
|
return { |
|
calendarOptions: refinedOptions, |
|
pluginHooks: pluginHooks, |
|
dateEnv: dateEnv, |
|
viewSpecs: viewSpecs, |
|
theme: theme, |
|
toolbarConfig: toolbarConfig, |
|
localeDefaults: localeDefaults, |
|
availableRawLocales: availableLocaleData.map, |
|
}; |
|
}; |
|
// always called from behind a memoizer |
|
CalendarDataManager.prototype.processRawCalendarOptions = function (optionOverrides, dynamicOptionOverrides) { |
|
var _a = mergeRawOptions([ |
|
BASE_OPTION_DEFAULTS, |
|
optionOverrides, |
|
dynamicOptionOverrides, |
|
]), locales = _a.locales, locale = _a.locale; |
|
var availableLocaleData = this.organizeRawLocales(locales); |
|
var availableRawLocales = availableLocaleData.map; |
|
var localeDefaults = this.buildLocale(locale || availableLocaleData.defaultCode, availableRawLocales).options; |
|
var pluginHooks = this.buildPluginHooks(optionOverrides.plugins || [], globalPlugins); |
|
var refiners = this.currentCalendarOptionsRefiners = __assign(__assign(__assign(__assign(__assign({}, BASE_OPTION_REFINERS), CALENDAR_LISTENER_REFINERS), CALENDAR_OPTION_REFINERS), pluginHooks.listenerRefiners), pluginHooks.optionRefiners); |
|
var extra = {}; |
|
var raw = mergeRawOptions([ |
|
BASE_OPTION_DEFAULTS, |
|
localeDefaults, |
|
optionOverrides, |
|
dynamicOptionOverrides, |
|
]); |
|
var refined = {}; |
|
var currentRaw = this.currentCalendarOptionsInput; |
|
var currentRefined = this.currentCalendarOptionsRefined; |
|
var anyChanges = false; |
|
for (var optionName in raw) { |
|
if (optionName !== 'plugins') { // because plugins is special-cased |
|
if (raw[optionName] === currentRaw[optionName] || |
|
(COMPLEX_OPTION_COMPARATORS[optionName] && |
|
(optionName in currentRaw) && |
|
COMPLEX_OPTION_COMPARATORS[optionName](currentRaw[optionName], raw[optionName]))) { |
|
refined[optionName] = currentRefined[optionName]; |
|
} |
|
else if (refiners[optionName]) { |
|
refined[optionName] = refiners[optionName](raw[optionName]); |
|
anyChanges = true; |
|
} |
|
else { |
|
extra[optionName] = currentRaw[optionName]; |
|
} |
|
} |
|
} |
|
if (anyChanges) { |
|
this.currentCalendarOptionsInput = raw; |
|
this.currentCalendarOptionsRefined = refined; |
|
} |
|
return { |
|
rawOptions: this.currentCalendarOptionsInput, |
|
refinedOptions: this.currentCalendarOptionsRefined, |
|
pluginHooks: pluginHooks, |
|
availableLocaleData: availableLocaleData, |
|
localeDefaults: localeDefaults, |
|
extra: extra, |
|
}; |
|
}; |
|
CalendarDataManager.prototype._computeCurrentViewData = function (viewType, optionsData, optionOverrides, dynamicOptionOverrides) { |
|
var viewSpec = optionsData.viewSpecs[viewType]; |
|
if (!viewSpec) { |
|
throw new Error("viewType \"" + viewType + "\" is not available. Please make sure you've loaded all neccessary plugins"); |
|
} |
|
var _a = this.processRawViewOptions(viewSpec, optionsData.pluginHooks, optionsData.localeDefaults, optionOverrides, dynamicOptionOverrides), refinedOptions = _a.refinedOptions, extra = _a.extra; |
|
warnUnknownOptions(extra); |
|
var dateProfileGenerator = this.buildDateProfileGenerator({ |
|
dateProfileGeneratorClass: viewSpec.optionDefaults.dateProfileGeneratorClass, |
|
duration: viewSpec.duration, |
|
durationUnit: viewSpec.durationUnit, |
|
usesMinMaxTime: viewSpec.optionDefaults.usesMinMaxTime, |
|
dateEnv: optionsData.dateEnv, |
|
calendarApi: this.props.calendarApi, |
|
slotMinTime: refinedOptions.slotMinTime, |
|
slotMaxTime: refinedOptions.slotMaxTime, |
|
showNonCurrentDates: refinedOptions.showNonCurrentDates, |
|
dayCount: refinedOptions.dayCount, |
|
dateAlignment: refinedOptions.dateAlignment, |
|
dateIncrement: refinedOptions.dateIncrement, |
|
hiddenDays: refinedOptions.hiddenDays, |
|
weekends: refinedOptions.weekends, |
|
nowInput: refinedOptions.now, |
|
validRangeInput: refinedOptions.validRange, |
|
visibleRangeInput: refinedOptions.visibleRange, |
|
monthMode: refinedOptions.monthMode, |
|
fixedWeekCount: refinedOptions.fixedWeekCount, |
|
}); |
|
var viewApi = this.buildViewApi(viewType, this.getCurrentData, optionsData.dateEnv); |
|
return { viewSpec: viewSpec, options: refinedOptions, dateProfileGenerator: dateProfileGenerator, viewApi: viewApi }; |
|
}; |
|
CalendarDataManager.prototype.processRawViewOptions = function (viewSpec, pluginHooks, localeDefaults, optionOverrides, dynamicOptionOverrides) { |
|
var raw = mergeRawOptions([ |
|
BASE_OPTION_DEFAULTS, |
|
viewSpec.optionDefaults, |
|
localeDefaults, |
|
optionOverrides, |
|
viewSpec.optionOverrides, |
|
dynamicOptionOverrides, |
|
]); |
|
var refiners = __assign(__assign(__assign(__assign(__assign(__assign({}, BASE_OPTION_REFINERS), CALENDAR_LISTENER_REFINERS), CALENDAR_OPTION_REFINERS), VIEW_OPTION_REFINERS), pluginHooks.listenerRefiners), pluginHooks.optionRefiners); |
|
var refined = {}; |
|
var currentRaw = this.currentViewOptionsInput; |
|
var currentRefined = this.currentViewOptionsRefined; |
|
var anyChanges = false; |
|
var extra = {}; |
|
for (var optionName in raw) { |
|
if (raw[optionName] === currentRaw[optionName]) { |
|
refined[optionName] = currentRefined[optionName]; |
|
} |
|
else { |
|
if (raw[optionName] === this.currentCalendarOptionsInput[optionName]) { |
|
if (optionName in this.currentCalendarOptionsRefined) { // might be an "extra" prop |
|
refined[optionName] = this.currentCalendarOptionsRefined[optionName]; |
|
} |
|
} |
|
else if (refiners[optionName]) { |
|
refined[optionName] = refiners[optionName](raw[optionName]); |
|
} |
|
else { |
|
extra[optionName] = raw[optionName]; |
|
} |
|
anyChanges = true; |
|
} |
|
} |
|
if (anyChanges) { |
|
this.currentViewOptionsInput = raw; |
|
this.currentViewOptionsRefined = refined; |
|
} |
|
return { |
|
rawOptions: this.currentViewOptionsInput, |
|
refinedOptions: this.currentViewOptionsRefined, |
|
extra: extra, |
|
}; |
|
}; |
|
return CalendarDataManager; |
|
}()); |
|
function buildDateEnv$1(timeZone, explicitLocale, weekNumberCalculation, firstDay, weekText, pluginHooks, availableLocaleData, defaultSeparator) { |
|
var locale = buildLocale(explicitLocale || availableLocaleData.defaultCode, availableLocaleData.map); |
|
return new DateEnv({ |
|
calendarSystem: 'gregory', |
|
timeZone: timeZone, |
|
namedTimeZoneImpl: pluginHooks.namedTimeZonedImpl, |
|
locale: locale, |
|
weekNumberCalculation: weekNumberCalculation, |
|
firstDay: firstDay, |
|
weekText: weekText, |
|
cmdFormatter: pluginHooks.cmdFormatter, |
|
defaultSeparator: defaultSeparator, |
|
}); |
|
} |
|
function buildTheme(options, pluginHooks) { |
|
var ThemeClass = pluginHooks.themeClasses[options.themeSystem] || StandardTheme; |
|
return new ThemeClass(options); |
|
} |
|
function buildDateProfileGenerator(props) { |
|
var DateProfileGeneratorClass = props.dateProfileGeneratorClass || DateProfileGenerator; |
|
return new DateProfileGeneratorClass(props); |
|
} |
|
function buildViewApi(type, getCurrentData, dateEnv) { |
|
return new ViewApi(type, getCurrentData, dateEnv); |
|
} |
|
function buildEventUiBySource(eventSources) { |
|
return mapHash(eventSources, function (eventSource) { return eventSource.ui; }); |
|
} |
|
function buildEventUiBases(eventDefs, eventUiSingleBase, eventUiBySource) { |
|
var eventUiBases = { '': eventUiSingleBase }; |
|
for (var defId in eventDefs) { |
|
var def = eventDefs[defId]; |
|
if (def.sourceId && eventUiBySource[def.sourceId]) { |
|
eventUiBases[defId] = eventUiBySource[def.sourceId]; |
|
} |
|
} |
|
return eventUiBases; |
|
} |
|
function buildViewUiProps(calendarContext) { |
|
var options = calendarContext.options; |
|
return { |
|
eventUiSingleBase: createEventUi({ |
|
display: options.eventDisplay, |
|
editable: options.editable, |
|
startEditable: options.eventStartEditable, |
|
durationEditable: options.eventDurationEditable, |
|
constraint: options.eventConstraint, |
|
overlap: typeof options.eventOverlap === 'boolean' ? options.eventOverlap : undefined, |
|
allow: options.eventAllow, |
|
backgroundColor: options.eventBackgroundColor, |
|
borderColor: options.eventBorderColor, |
|
textColor: options.eventTextColor, |
|
color: options.eventColor, |
|
}, calendarContext), |
|
selectionConfig: createEventUi({ |
|
constraint: options.selectConstraint, |
|
overlap: typeof options.selectOverlap === 'boolean' ? options.selectOverlap : undefined, |
|
allow: options.selectAllow, |
|
}, calendarContext), |
|
}; |
|
} |
|
function computeIsLoading(state, context) { |
|
for (var _i = 0, _a = context.pluginHooks.isLoadingFuncs; _i < _a.length; _i++) { |
|
var isLoadingFunc = _a[_i]; |
|
if (isLoadingFunc(state)) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
function parseContextBusinessHours(calendarContext) { |
|
return parseBusinessHours(calendarContext.options.businessHours, calendarContext); |
|
} |
|
function warnUnknownOptions(options, viewName) { |
|
for (var optionName in options) { |
|
console.warn("Unknown option '" + optionName + "'" + |
|
(viewName ? " for view '" + viewName + "'" : '')); |
|
} |
|
} |
|
|
|
// TODO: move this to react plugin? |
|
var CalendarDataProvider = /** @class */ (function (_super) { |
|
__extends(CalendarDataProvider, _super); |
|
function CalendarDataProvider(props) { |
|
var _this = _super.call(this, props) || this; |
|
_this.handleData = function (data) { |
|
if (!_this.dataManager) { // still within initial run, before assignment in constructor |
|
// eslint-disable-next-line react/no-direct-mutation-state |
|
_this.state = data; // can't use setState yet |
|
} |
|
else { |
|
_this.setState(data); |
|
} |
|
}; |
|
_this.dataManager = new CalendarDataManager({ |
|
optionOverrides: props.optionOverrides, |
|
calendarApi: props.calendarApi, |
|
onData: _this.handleData, |
|
}); |
|
return _this; |
|
} |
|
CalendarDataProvider.prototype.render = function () { |
|
return this.props.children(this.state); |
|
}; |
|
CalendarDataProvider.prototype.componentDidUpdate = function (prevProps) { |
|
var newOptionOverrides = this.props.optionOverrides; |
|
if (newOptionOverrides !== prevProps.optionOverrides) { // prevent recursive handleData |
|
this.dataManager.resetOptions(newOptionOverrides); |
|
} |
|
}; |
|
return CalendarDataProvider; |
|
}(Component)); |
|
|
|
// HELPERS |
|
/* |
|
if nextDayThreshold is specified, slicing is done in an all-day fashion. |
|
you can get nextDayThreshold from context.nextDayThreshold |
|
*/ |
|
function sliceEvents(props, allDay) { |
|
return sliceEventStore(props.eventStore, props.eventUiBases, props.dateProfile.activeRange, allDay ? props.nextDayThreshold : null).fg; |
|
} |
|
|
|
var NamedTimeZoneImpl = /** @class */ (function () { |
|
function NamedTimeZoneImpl(timeZoneName) { |
|
this.timeZoneName = timeZoneName; |
|
} |
|
return NamedTimeZoneImpl; |
|
}()); |
|
|
|
var Interaction = /** @class */ (function () { |
|
function Interaction(settings) { |
|
this.component = settings.component; |
|
} |
|
Interaction.prototype.destroy = function () { |
|
}; |
|
return Interaction; |
|
}()); |
|
function parseInteractionSettings(component, input) { |
|
return { |
|
component: component, |
|
el: input.el, |
|
useEventCenter: input.useEventCenter != null ? input.useEventCenter : true, |
|
}; |
|
} |
|
function interactionSettingsToStore(settings) { |
|
var _a; |
|
return _a = {}, |
|
_a[settings.component.uid] = settings, |
|
_a; |
|
} |
|
// global state |
|
var interactionSettingsStore = {}; |
|
|
|
/* |
|
An abstraction for a dragging interaction originating on an event. |
|
Does higher-level things than PointerDragger, such as possibly: |
|
- a "mirror" that moves with the pointer |
|
- a minimum number of pixels or other criteria for a true drag to begin |
|
|
|
subclasses must emit: |
|
- pointerdown |
|
- dragstart |
|
- dragmove |
|
- pointerup |
|
- dragend |
|
*/ |
|
var ElementDragging = /** @class */ (function () { |
|
function ElementDragging(el, selector) { |
|
this.emitter = new Emitter(); |
|
} |
|
ElementDragging.prototype.destroy = function () { |
|
}; |
|
ElementDragging.prototype.setMirrorIsVisible = function (bool) { |
|
// optional if subclass doesn't want to support a mirror |
|
}; |
|
ElementDragging.prototype.setMirrorNeedsRevert = function (bool) { |
|
// optional if subclass doesn't want to support a mirror |
|
}; |
|
ElementDragging.prototype.setAutoScrollEnabled = function (bool) { |
|
// optional |
|
}; |
|
return ElementDragging; |
|
}()); |
|
|
|
// TODO: get rid of this in favor of options system, |
|
// tho it's really easy to access this globally rather than pass thru options. |
|
var config = {}; |
|
|
|
/* |
|
Information about what will happen when an external element is dragged-and-dropped |
|
onto a calendar. Contains information for creating an event. |
|
*/ |
|
var DRAG_META_REFINERS = { |
|
startTime: createDuration, |
|
duration: createDuration, |
|
create: Boolean, |
|
sourceId: String, |
|
}; |
|
function parseDragMeta(raw) { |
|
var _a = refineProps(raw, DRAG_META_REFINERS), refined = _a.refined, extra = _a.extra; |
|
return { |
|
startTime: refined.startTime || null, |
|
duration: refined.duration || null, |
|
create: refined.create != null ? refined.create : true, |
|
sourceId: refined.sourceId, |
|
leftoverProps: extra, |
|
}; |
|
} |
|
|
|
var ToolbarSection = /** @class */ (function (_super) { |
|
__extends(ToolbarSection, _super); |
|
function ToolbarSection() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
ToolbarSection.prototype.render = function () { |
|
var _this = this; |
|
var children = this.props.widgetGroups.map(function (widgetGroup) { return _this.renderWidgetGroup(widgetGroup); }); |
|
return createElement.apply(void 0, __spreadArrays(['div', { className: 'fc-toolbar-chunk' }], children)); |
|
}; |
|
ToolbarSection.prototype.renderWidgetGroup = function (widgetGroup) { |
|
var props = this.props; |
|
var theme = this.context.theme; |
|
var children = []; |
|
var isOnlyButtons = true; |
|
for (var _i = 0, widgetGroup_1 = widgetGroup; _i < widgetGroup_1.length; _i++) { |
|
var widget = widgetGroup_1[_i]; |
|
var buttonName = widget.buttonName, buttonClick = widget.buttonClick, buttonText = widget.buttonText, buttonIcon = widget.buttonIcon; |
|
if (buttonName === 'title') { |
|
isOnlyButtons = false; |
|
children.push(createElement("h2", { className: "fc-toolbar-title" }, props.title)); |
|
} |
|
else { |
|
var ariaAttrs = buttonIcon ? { 'aria-label': buttonName } : {}; |
|
var buttonClasses = ["fc-" + buttonName + "-button", theme.getClass('button')]; |
|
if (buttonName === props.activeButton) { |
|
buttonClasses.push(theme.getClass('buttonActive')); |
|
} |
|
var isDisabled = (!props.isTodayEnabled && buttonName === 'today') || |
|
(!props.isPrevEnabled && buttonName === 'prev') || |
|
(!props.isNextEnabled && buttonName === 'next'); |
|
children.push(createElement("button", __assign({ disabled: isDisabled, className: buttonClasses.join(' '), onClick: buttonClick, type: "button" }, ariaAttrs), buttonText || (buttonIcon ? createElement("span", { className: buttonIcon }) : ''))); |
|
} |
|
} |
|
if (children.length > 1) { |
|
var groupClassName = (isOnlyButtons && theme.getClass('buttonGroup')) || ''; |
|
return createElement.apply(void 0, __spreadArrays(['div', { className: groupClassName }], children)); |
|
} |
|
return children[0]; |
|
}; |
|
return ToolbarSection; |
|
}(BaseComponent)); |
|
|
|
var Toolbar = /** @class */ (function (_super) { |
|
__extends(Toolbar, _super); |
|
function Toolbar() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
Toolbar.prototype.render = function () { |
|
var _a = this.props, model = _a.model, extraClassName = _a.extraClassName; |
|
var forceLtr = false; |
|
var startContent; |
|
var endContent; |
|
var centerContent = model.center; |
|
if (model.left) { |
|
forceLtr = true; |
|
startContent = model.left; |
|
} |
|
else { |
|
startContent = model.start; |
|
} |
|
if (model.right) { |
|
forceLtr = true; |
|
endContent = model.right; |
|
} |
|
else { |
|
endContent = model.end; |
|
} |
|
var classNames = [ |
|
extraClassName || '', |
|
'fc-toolbar', |
|
forceLtr ? 'fc-toolbar-ltr' : '', |
|
]; |
|
return (createElement("div", { className: classNames.join(' ') }, |
|
this.renderSection('start', startContent || []), |
|
this.renderSection('center', centerContent || []), |
|
this.renderSection('end', endContent || []))); |
|
}; |
|
Toolbar.prototype.renderSection = function (key, widgetGroups) { |
|
var props = this.props; |
|
return (createElement(ToolbarSection, { key: key, widgetGroups: widgetGroups, title: props.title, activeButton: props.activeButton, isTodayEnabled: props.isTodayEnabled, isPrevEnabled: props.isPrevEnabled, isNextEnabled: props.isNextEnabled })); |
|
}; |
|
return Toolbar; |
|
}(BaseComponent)); |
|
|
|
// TODO: do function component? |
|
var ViewContainer = /** @class */ (function (_super) { |
|
__extends(ViewContainer, _super); |
|
function ViewContainer() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.state = { |
|
availableWidth: null, |
|
}; |
|
_this.handleEl = function (el) { |
|
_this.el = el; |
|
setRef(_this.props.elRef, el); |
|
_this.updateAvailableWidth(); |
|
}; |
|
_this.handleResize = function () { |
|
_this.updateAvailableWidth(); |
|
}; |
|
return _this; |
|
} |
|
ViewContainer.prototype.render = function () { |
|
var _a = this, props = _a.props, state = _a.state; |
|
var aspectRatio = props.aspectRatio; |
|
var classNames = [ |
|
'fc-view-harness', |
|
(aspectRatio || props.liquid || props.height) |
|
? 'fc-view-harness-active' // harness controls the height |
|
: 'fc-view-harness-passive', |
|
]; |
|
var height = ''; |
|
var paddingBottom = ''; |
|
if (aspectRatio) { |
|
if (state.availableWidth !== null) { |
|
height = state.availableWidth / aspectRatio; |
|
} |
|
else { |
|
// while waiting to know availableWidth, we can't set height to *zero* |
|
// because will cause lots of unnecessary scrollbars within scrollgrid. |
|
// BETTER: don't start rendering ANYTHING yet until we know container width |
|
// NOTE: why not always use paddingBottom? Causes height oscillation (issue 5606) |
|
paddingBottom = (1 / aspectRatio) * 100 + "%"; |
|
} |
|
} |
|
else { |
|
height = props.height || ''; |
|
} |
|
return (createElement("div", { ref: this.handleEl, onClick: props.onClick, className: classNames.join(' '), style: { height: height, paddingBottom: paddingBottom } }, props.children)); |
|
}; |
|
ViewContainer.prototype.componentDidMount = function () { |
|
this.context.addResizeHandler(this.handleResize); |
|
}; |
|
ViewContainer.prototype.componentWillUnmount = function () { |
|
this.context.removeResizeHandler(this.handleResize); |
|
}; |
|
ViewContainer.prototype.updateAvailableWidth = function () { |
|
if (this.el && // needed. but why? |
|
this.props.aspectRatio // aspectRatio is the only height setting that needs availableWidth |
|
) { |
|
this.setState({ availableWidth: this.el.offsetWidth }); |
|
} |
|
}; |
|
return ViewContainer; |
|
}(BaseComponent)); |
|
|
|
/* |
|
Detects when the user clicks on an event within a DateComponent |
|
*/ |
|
var EventClicking = /** @class */ (function (_super) { |
|
__extends(EventClicking, _super); |
|
function EventClicking(settings) { |
|
var _this = _super.call(this, settings) || this; |
|
_this.handleSegClick = function (ev, segEl) { |
|
var component = _this.component; |
|
var context = component.context; |
|
var seg = getElSeg(segEl); |
|
if (seg && // might be the <div> surrounding the more link |
|
component.isValidSegDownEl(ev.target)) { |
|
// our way to simulate a link click for elements that can't be <a> tags |
|
// grab before trigger fired in case trigger trashes DOM thru rerendering |
|
var hasUrlContainer = elementClosest(ev.target, '.fc-event-forced-url'); |
|
var url = hasUrlContainer ? hasUrlContainer.querySelector('a[href]').href : ''; |
|
context.emitter.trigger('eventClick', { |
|
el: segEl, |
|
event: new EventApi(component.context, seg.eventRange.def, seg.eventRange.instance), |
|
jsEvent: ev, |
|
view: context.viewApi, |
|
}); |
|
if (url && !ev.defaultPrevented) { |
|
window.location.href = url; |
|
} |
|
} |
|
}; |
|
_this.destroy = listenBySelector(settings.el, 'click', '.fc-event', // on both fg and bg events |
|
_this.handleSegClick); |
|
return _this; |
|
} |
|
return EventClicking; |
|
}(Interaction)); |
|
|
|
/* |
|
Triggers events and adds/removes core classNames when the user's pointer |
|
enters/leaves event-elements of a component. |
|
*/ |
|
var EventHovering = /** @class */ (function (_super) { |
|
__extends(EventHovering, _super); |
|
function EventHovering(settings) { |
|
var _this = _super.call(this, settings) || this; |
|
// for simulating an eventMouseLeave when the event el is destroyed while mouse is over it |
|
_this.handleEventElRemove = function (el) { |
|
if (el === _this.currentSegEl) { |
|
_this.handleSegLeave(null, _this.currentSegEl); |
|
} |
|
}; |
|
_this.handleSegEnter = function (ev, segEl) { |
|
if (getElSeg(segEl)) { // TODO: better way to make sure not hovering over more+ link or its wrapper |
|
_this.currentSegEl = segEl; |
|
_this.triggerEvent('eventMouseEnter', ev, segEl); |
|
} |
|
}; |
|
_this.handleSegLeave = function (ev, segEl) { |
|
if (_this.currentSegEl) { |
|
_this.currentSegEl = null; |
|
_this.triggerEvent('eventMouseLeave', ev, segEl); |
|
} |
|
}; |
|
_this.removeHoverListeners = listenToHoverBySelector(settings.el, '.fc-event', // on both fg and bg events |
|
_this.handleSegEnter, _this.handleSegLeave); |
|
return _this; |
|
} |
|
EventHovering.prototype.destroy = function () { |
|
this.removeHoverListeners(); |
|
}; |
|
EventHovering.prototype.triggerEvent = function (publicEvName, ev, segEl) { |
|
var component = this.component; |
|
var context = component.context; |
|
var seg = getElSeg(segEl); |
|
if (!ev || component.isValidSegDownEl(ev.target)) { |
|
context.emitter.trigger(publicEvName, { |
|
el: segEl, |
|
event: new EventApi(context, seg.eventRange.def, seg.eventRange.instance), |
|
jsEvent: ev, |
|
view: context.viewApi, |
|
}); |
|
} |
|
}; |
|
return EventHovering; |
|
}(Interaction)); |
|
|
|
var CalendarContent = /** @class */ (function (_super) { |
|
__extends(CalendarContent, _super); |
|
function CalendarContent() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.buildViewContext = memoize(buildViewContext); |
|
_this.buildViewPropTransformers = memoize(buildViewPropTransformers); |
|
_this.buildToolbarProps = memoize(buildToolbarProps); |
|
_this.handleNavLinkClick = buildDelegationHandler('a[data-navlink]', _this._handleNavLinkClick.bind(_this)); |
|
_this.headerRef = createRef(); |
|
_this.footerRef = createRef(); |
|
_this.interactionsStore = {}; |
|
// Component Registration |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
_this.registerInteractiveComponent = function (component, settingsInput) { |
|
var settings = parseInteractionSettings(component, settingsInput); |
|
var DEFAULT_INTERACTIONS = [ |
|
EventClicking, |
|
EventHovering, |
|
]; |
|
var interactionClasses = DEFAULT_INTERACTIONS.concat(_this.props.pluginHooks.componentInteractions); |
|
var interactions = interactionClasses.map(function (TheInteractionClass) { return new TheInteractionClass(settings); }); |
|
_this.interactionsStore[component.uid] = interactions; |
|
interactionSettingsStore[component.uid] = settings; |
|
}; |
|
_this.unregisterInteractiveComponent = function (component) { |
|
for (var _i = 0, _a = _this.interactionsStore[component.uid]; _i < _a.length; _i++) { |
|
var listener = _a[_i]; |
|
listener.destroy(); |
|
} |
|
delete _this.interactionsStore[component.uid]; |
|
delete interactionSettingsStore[component.uid]; |
|
}; |
|
// Resizing |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
_this.resizeRunner = new DelayedRunner(function () { |
|
_this.props.emitter.trigger('_resize', true); // should window resizes be considered "forced" ? |
|
_this.props.emitter.trigger('windowResize', { view: _this.props.viewApi }); |
|
}); |
|
_this.handleWindowResize = function (ev) { |
|
var options = _this.props.options; |
|
if (options.handleWindowResize && |
|
ev.target === window // avoid jqui events |
|
) { |
|
_this.resizeRunner.request(options.windowResizeDelay); |
|
} |
|
}; |
|
return _this; |
|
} |
|
/* |
|
renders INSIDE of an outer div |
|
*/ |
|
CalendarContent.prototype.render = function () { |
|
var props = this.props; |
|
var toolbarConfig = props.toolbarConfig, options = props.options; |
|
var toolbarProps = this.buildToolbarProps(props.viewSpec, props.dateProfile, props.dateProfileGenerator, props.currentDate, getNow(props.options.now, props.dateEnv), // TODO: use NowTimer???? |
|
props.viewTitle); |
|
var viewVGrow = false; |
|
var viewHeight = ''; |
|
var viewAspectRatio; |
|
if (props.isHeightAuto || props.forPrint) { |
|
viewHeight = ''; |
|
} |
|
else if (options.height != null) { |
|
viewVGrow = true; |
|
} |
|
else if (options.contentHeight != null) { |
|
viewHeight = options.contentHeight; |
|
} |
|
else { |
|
viewAspectRatio = Math.max(options.aspectRatio, 0.5); // prevent from getting too tall |
|
} |
|
var 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); |
|
return (createElement(ViewContextType.Provider, { value: viewContext }, |
|
toolbarConfig.headerToolbar && (createElement(Toolbar, __assign({ ref: this.headerRef, extraClassName: "fc-header-toolbar", model: toolbarConfig.headerToolbar }, toolbarProps))), |
|
createElement(ViewContainer, { liquid: viewVGrow, height: viewHeight, aspectRatio: viewAspectRatio, onClick: this.handleNavLinkClick }, |
|
this.renderView(props), |
|
this.buildAppendContent()), |
|
toolbarConfig.footerToolbar && (createElement(Toolbar, __assign({ ref: this.footerRef, extraClassName: "fc-footer-toolbar", model: toolbarConfig.footerToolbar }, toolbarProps))))); |
|
}; |
|
CalendarContent.prototype.componentDidMount = function () { |
|
var props = this.props; |
|
this.calendarInteractions = props.pluginHooks.calendarInteractions |
|
.map(function (CalendarInteractionClass) { return new CalendarInteractionClass(props); }); |
|
window.addEventListener('resize', this.handleWindowResize); |
|
var propSetHandlers = props.pluginHooks.propSetHandlers; |
|
for (var propName in propSetHandlers) { |
|
propSetHandlers[propName](props[propName], props); |
|
} |
|
}; |
|
CalendarContent.prototype.componentDidUpdate = function (prevProps) { |
|
var props = this.props; |
|
var propSetHandlers = props.pluginHooks.propSetHandlers; |
|
for (var propName in propSetHandlers) { |
|
if (props[propName] !== prevProps[propName]) { |
|
propSetHandlers[propName](props[propName], props); |
|
} |
|
} |
|
}; |
|
CalendarContent.prototype.componentWillUnmount = function () { |
|
window.removeEventListener('resize', this.handleWindowResize); |
|
this.resizeRunner.clear(); |
|
for (var _i = 0, _a = this.calendarInteractions; _i < _a.length; _i++) { |
|
var interaction = _a[_i]; |
|
interaction.destroy(); |
|
} |
|
this.props.emitter.trigger('_unmount'); |
|
}; |
|
CalendarContent.prototype._handleNavLinkClick = function (ev, anchorEl) { |
|
var _a = this.props, dateEnv = _a.dateEnv, options = _a.options, calendarApi = _a.calendarApi; |
|
var navLinkOptions = anchorEl.getAttribute('data-navlink'); |
|
navLinkOptions = navLinkOptions ? JSON.parse(navLinkOptions) : {}; |
|
var dateMarker = dateEnv.createMarker(navLinkOptions.date); |
|
var viewType = navLinkOptions.type; |
|
var customAction = viewType === 'day' ? options.navLinkDayClick : |
|
viewType === 'week' ? options.navLinkWeekClick : null; |
|
if (typeof customAction === 'function') { |
|
customAction.call(calendarApi, dateEnv.toDate(dateMarker), ev); |
|
} |
|
else { |
|
if (typeof customAction === 'string') { |
|
viewType = customAction; |
|
} |
|
calendarApi.zoomTo(dateMarker, viewType); |
|
} |
|
}; |
|
CalendarContent.prototype.buildAppendContent = function () { |
|
var props = this.props; |
|
var children = props.pluginHooks.viewContainerAppends.map(function (buildAppendContent) { return buildAppendContent(props); }); |
|
return createElement.apply(void 0, __spreadArrays([Fragment, {}], children)); |
|
}; |
|
CalendarContent.prototype.renderView = function (props) { |
|
var pluginHooks = props.pluginHooks; |
|
var viewSpec = props.viewSpec; |
|
var viewProps = { |
|
dateProfile: props.dateProfile, |
|
businessHours: props.businessHours, |
|
eventStore: props.renderableEventStore, |
|
eventUiBases: props.eventUiBases, |
|
dateSelection: props.dateSelection, |
|
eventSelection: props.eventSelection, |
|
eventDrag: props.eventDrag, |
|
eventResize: props.eventResize, |
|
isHeightAuto: props.isHeightAuto, |
|
forPrint: props.forPrint, |
|
}; |
|
var transformers = this.buildViewPropTransformers(pluginHooks.viewPropsTransformers); |
|
for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) { |
|
var transformer = transformers_1[_i]; |
|
__assign(viewProps, transformer.transform(viewProps, props)); |
|
} |
|
var ViewComponent = viewSpec.component; |
|
return (createElement(ViewComponent, __assign({}, viewProps))); |
|
}; |
|
return CalendarContent; |
|
}(PureComponent)); |
|
function buildToolbarProps(viewSpec, dateProfile, dateProfileGenerator, currentDate, now, title) { |
|
// don't force any date-profiles to valid date profiles (the `false`) so that we can tell if it's invalid |
|
var todayInfo = dateProfileGenerator.build(now, undefined, false); // TODO: need `undefined` or else INFINITE LOOP for some reason |
|
var prevInfo = dateProfileGenerator.buildPrev(dateProfile, currentDate, false); |
|
var nextInfo = dateProfileGenerator.buildNext(dateProfile, currentDate, false); |
|
return { |
|
title: title, |
|
activeButton: viewSpec.type, |
|
isTodayEnabled: todayInfo.isValid && !rangeContainsMarker(dateProfile.currentRange, now), |
|
isPrevEnabled: prevInfo.isValid, |
|
isNextEnabled: nextInfo.isValid, |
|
}; |
|
} |
|
// Plugin |
|
// ----------------------------------------------------------------------------------------------------------------- |
|
function buildViewPropTransformers(theClasses) { |
|
return theClasses.map(function (TheClass) { return new TheClass(); }); |
|
} |
|
|
|
var CalendarRoot = /** @class */ (function (_super) { |
|
__extends(CalendarRoot, _super); |
|
function CalendarRoot() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.state = { |
|
forPrint: false, |
|
}; |
|
_this.handleBeforePrint = function () { |
|
_this.setState({ forPrint: true }); |
|
}; |
|
_this.handleAfterPrint = function () { |
|
_this.setState({ forPrint: false }); |
|
}; |
|
return _this; |
|
} |
|
CalendarRoot.prototype.render = function () { |
|
var props = this.props; |
|
var options = props.options; |
|
var forPrint = this.state.forPrint; |
|
var isHeightAuto = forPrint || options.height === 'auto' || options.contentHeight === 'auto'; |
|
var height = (!isHeightAuto && options.height != null) ? options.height : ''; |
|
var classNames = [ |
|
'fc', |
|
forPrint ? 'fc-media-print' : 'fc-media-screen', |
|
"fc-direction-" + options.direction, |
|
props.theme.getClass('root'), |
|
]; |
|
if (!getCanVGrowWithinCell()) { |
|
classNames.push('fc-liquid-hack'); |
|
} |
|
return props.children(classNames, height, isHeightAuto, forPrint); |
|
}; |
|
CalendarRoot.prototype.componentDidMount = function () { |
|
var emitter = this.props.emitter; |
|
emitter.on('_beforeprint', this.handleBeforePrint); |
|
emitter.on('_afterprint', this.handleAfterPrint); |
|
}; |
|
CalendarRoot.prototype.componentWillUnmount = function () { |
|
var emitter = this.props.emitter; |
|
emitter.off('_beforeprint', this.handleBeforePrint); |
|
emitter.off('_afterprint', this.handleAfterPrint); |
|
}; |
|
return CalendarRoot; |
|
}(BaseComponent)); |
|
|
|
// Computes a default column header formatting string if `colFormat` is not explicitly defined |
|
function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) { |
|
// if more than one week row, or if there are a lot of columns with not much space, |
|
// put just the day numbers will be in each cell |
|
if (!datesRepDistinctDays || dayCnt > 10) { |
|
return createFormatter({ weekday: 'short' }); // "Sat" |
|
} |
|
if (dayCnt > 1) { |
|
return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12" |
|
} |
|
return createFormatter({ weekday: 'long' }); // "Saturday" |
|
} |
|
|
|
var CLASS_NAME = 'fc-col-header-cell'; // do the cushion too? no |
|
function renderInner(hookProps) { |
|
return hookProps.text; |
|
} |
|
|
|
var TableDateCell = /** @class */ (function (_super) { |
|
__extends(TableDateCell, _super); |
|
function TableDateCell() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TableDateCell.prototype.render = function () { |
|
var _a = this.context, dateEnv = _a.dateEnv, options = _a.options, theme = _a.theme, viewApi = _a.viewApi; |
|
var props = this.props; |
|
var date = props.date, dateProfile = props.dateProfile; |
|
var dayMeta = getDateMeta(date, props.todayRange, null, dateProfile); |
|
var classNames = [CLASS_NAME].concat(getDayClassNames(dayMeta, theme)); |
|
var text = dateEnv.format(date, props.dayHeaderFormat); |
|
// if colCnt is 1, we are already in a day-view and don't need a navlink |
|
var navLinkAttrs = (options.navLinks && !dayMeta.isDisabled && props.colCnt > 1) |
|
? { 'data-navlink': buildNavLinkData(date), tabIndex: 0 } |
|
: {}; |
|
var hookProps = __assign(__assign(__assign({ date: dateEnv.toDate(date), view: viewApi }, props.extraHookProps), { text: text }), dayMeta); |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.dayHeaderClassNames, content: options.dayHeaderContent, defaultContent: renderInner, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("th", __assign({ ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-date": !dayMeta.isDisabled ? formatDayString(date) : undefined, colSpan: props.colSpan }, props.extraDataAttrs), |
|
createElement("div", { className: "fc-scrollgrid-sync-inner" }, !dayMeta.isDisabled && (createElement("a", __assign({ ref: innerElRef, className: [ |
|
'fc-col-header-cell-cushion', |
|
props.isSticky ? 'fc-sticky' : '', |
|
].join(' ') }, navLinkAttrs), innerContent))))); })); |
|
}; |
|
return TableDateCell; |
|
}(BaseComponent)); |
|
|
|
var TableDowCell = /** @class */ (function (_super) { |
|
__extends(TableDowCell, _super); |
|
function TableDowCell() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TableDowCell.prototype.render = function () { |
|
var props = this.props; |
|
var _a = this.context, dateEnv = _a.dateEnv, theme = _a.theme, viewApi = _a.viewApi, options = _a.options; |
|
var date = addDays(new Date(259200000), props.dow); // start with Sun, 04 Jan 1970 00:00:00 GMT |
|
var dateMeta = { |
|
dow: props.dow, |
|
isDisabled: false, |
|
isFuture: false, |
|
isPast: false, |
|
isToday: false, |
|
isOther: false, |
|
}; |
|
var classNames = [CLASS_NAME].concat(getDayClassNames(dateMeta, theme), props.extraClassNames || []); |
|
var text = dateEnv.format(date, props.dayHeaderFormat); |
|
var hookProps = __assign(__assign(__assign(__assign({ // TODO: make this public? |
|
date: date }, dateMeta), { view: viewApi }), props.extraHookProps), { text: text }); |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.dayHeaderClassNames, content: options.dayHeaderContent, defaultContent: renderInner, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("th", __assign({ ref: rootElRef, className: classNames.concat(customClassNames).join(' '), colSpan: props.colSpan }, props.extraDataAttrs), |
|
createElement("div", { className: "fc-scrollgrid-sync-inner" }, |
|
createElement("a", { className: [ |
|
'fc-col-header-cell-cushion', |
|
props.isSticky ? 'fc-sticky' : '', |
|
].join(' '), ref: innerElRef }, innerContent)))); })); |
|
}; |
|
return TableDowCell; |
|
}(BaseComponent)); |
|
|
|
var NowTimer = /** @class */ (function (_super) { |
|
__extends(NowTimer, _super); |
|
function NowTimer(props, context) { |
|
var _this = _super.call(this, props, context) || this; |
|
_this.initialNowDate = getNow(context.options.now, context.dateEnv); |
|
_this.initialNowQueriedMs = new Date().valueOf(); |
|
_this.state = _this.computeTiming().currentState; |
|
return _this; |
|
} |
|
NowTimer.prototype.render = function () { |
|
var _a = this, props = _a.props, state = _a.state; |
|
return props.children(state.nowDate, state.todayRange); |
|
}; |
|
NowTimer.prototype.componentDidMount = function () { |
|
this.setTimeout(); |
|
}; |
|
NowTimer.prototype.componentDidUpdate = function (prevProps) { |
|
if (prevProps.unit !== this.props.unit) { |
|
this.clearTimeout(); |
|
this.setTimeout(); |
|
} |
|
}; |
|
NowTimer.prototype.componentWillUnmount = function () { |
|
this.clearTimeout(); |
|
}; |
|
NowTimer.prototype.computeTiming = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var unroundedNow = addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs); |
|
var currentUnitStart = context.dateEnv.startOf(unroundedNow, props.unit); |
|
var nextUnitStart = context.dateEnv.add(currentUnitStart, createDuration(1, props.unit)); |
|
var waitMs = nextUnitStart.valueOf() - unroundedNow.valueOf(); |
|
// there is a max setTimeout ms value (https://stackoverflow.com/a/3468650/96342) |
|
// ensure no longer than a day |
|
waitMs = Math.min(1000 * 60 * 60 * 24, waitMs); |
|
return { |
|
currentState: { nowDate: currentUnitStart, todayRange: buildDayRange(currentUnitStart) }, |
|
nextState: { nowDate: nextUnitStart, todayRange: buildDayRange(nextUnitStart) }, |
|
waitMs: waitMs, |
|
}; |
|
}; |
|
NowTimer.prototype.setTimeout = function () { |
|
var _this = this; |
|
var _a = this.computeTiming(), nextState = _a.nextState, waitMs = _a.waitMs; |
|
this.timeoutId = setTimeout(function () { |
|
_this.setState(nextState, function () { |
|
_this.setTimeout(); |
|
}); |
|
}, waitMs); |
|
}; |
|
NowTimer.prototype.clearTimeout = function () { |
|
if (this.timeoutId) { |
|
clearTimeout(this.timeoutId); |
|
} |
|
}; |
|
NowTimer.contextType = ViewContextType; |
|
return NowTimer; |
|
}(Component)); |
|
function buildDayRange(date) { |
|
var start = startOfDay(date); |
|
var end = addDays(start, 1); |
|
return { start: start, end: end }; |
|
} |
|
|
|
var DayHeader = /** @class */ (function (_super) { |
|
__extends(DayHeader, _super); |
|
function DayHeader() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.createDayHeaderFormatter = memoize(createDayHeaderFormatter); |
|
return _this; |
|
} |
|
DayHeader.prototype.render = function () { |
|
var context = this.context; |
|
var _a = this.props, dates = _a.dates, dateProfile = _a.dateProfile, datesRepDistinctDays = _a.datesRepDistinctDays, renderIntro = _a.renderIntro; |
|
var dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, dates.length); |
|
return (createElement(NowTimer, { unit: "day" }, function (nowDate, todayRange) { return (createElement("tr", null, |
|
renderIntro && renderIntro('day'), |
|
dates.map(function (date) { return (datesRepDistinctDays ? (createElement(TableDateCell, { key: date.toISOString(), date: date, dateProfile: dateProfile, todayRange: todayRange, colCnt: dates.length, dayHeaderFormat: dayHeaderFormat })) : (createElement(TableDowCell, { key: date.getUTCDay(), dow: date.getUTCDay(), dayHeaderFormat: dayHeaderFormat }))); }))); })); |
|
}; |
|
return DayHeader; |
|
}(BaseComponent)); |
|
function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) { |
|
return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt); |
|
} |
|
|
|
var DaySeriesModel = /** @class */ (function () { |
|
function DaySeriesModel(range, dateProfileGenerator) { |
|
var date = range.start; |
|
var end = range.end; |
|
var indices = []; |
|
var dates = []; |
|
var dayIndex = -1; |
|
while (date < end) { // loop each day from start to end |
|
if (dateProfileGenerator.isHiddenDay(date)) { |
|
indices.push(dayIndex + 0.5); // mark that it's between indices |
|
} |
|
else { |
|
dayIndex += 1; |
|
indices.push(dayIndex); |
|
dates.push(date); |
|
} |
|
date = addDays(date, 1); |
|
} |
|
this.dates = dates; |
|
this.indices = indices; |
|
this.cnt = dates.length; |
|
} |
|
DaySeriesModel.prototype.sliceRange = function (range) { |
|
var firstIndex = this.getDateDayIndex(range.start); // inclusive first index |
|
var lastIndex = this.getDateDayIndex(addDays(range.end, -1)); // inclusive last index |
|
var clippedFirstIndex = Math.max(0, firstIndex); |
|
var clippedLastIndex = Math.min(this.cnt - 1, lastIndex); |
|
// deal with in-between indices |
|
clippedFirstIndex = Math.ceil(clippedFirstIndex); // in-between starts round to next cell |
|
clippedLastIndex = Math.floor(clippedLastIndex); // in-between ends round to prev cell |
|
if (clippedFirstIndex <= clippedLastIndex) { |
|
return { |
|
firstIndex: clippedFirstIndex, |
|
lastIndex: clippedLastIndex, |
|
isStart: firstIndex === clippedFirstIndex, |
|
isEnd: lastIndex === clippedLastIndex, |
|
}; |
|
} |
|
return null; |
|
}; |
|
// Given a date, returns its chronolocial cell-index from the first cell of the grid. |
|
// If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets. |
|
// If before the first offset, returns a negative number. |
|
// If after the last offset, returns an offset past the last cell offset. |
|
// Only works for *start* dates of cells. Will not work for exclusive end dates for cells. |
|
DaySeriesModel.prototype.getDateDayIndex = function (date) { |
|
var indices = this.indices; |
|
var dayOffset = Math.floor(diffDays(this.dates[0], date)); |
|
if (dayOffset < 0) { |
|
return indices[0] - 1; |
|
} |
|
if (dayOffset >= indices.length) { |
|
return indices[indices.length - 1] + 1; |
|
} |
|
return indices[dayOffset]; |
|
}; |
|
return DaySeriesModel; |
|
}()); |
|
|
|
var DayTableModel = /** @class */ (function () { |
|
function DayTableModel(daySeries, breakOnWeeks) { |
|
var dates = daySeries.dates; |
|
var daysPerRow; |
|
var firstDay; |
|
var rowCnt; |
|
if (breakOnWeeks) { |
|
// count columns until the day-of-week repeats |
|
firstDay = dates[0].getUTCDay(); |
|
for (daysPerRow = 1; daysPerRow < dates.length; daysPerRow += 1) { |
|
if (dates[daysPerRow].getUTCDay() === firstDay) { |
|
break; |
|
} |
|
} |
|
rowCnt = Math.ceil(dates.length / daysPerRow); |
|
} |
|
else { |
|
rowCnt = 1; |
|
daysPerRow = dates.length; |
|
} |
|
this.rowCnt = rowCnt; |
|
this.colCnt = daysPerRow; |
|
this.daySeries = daySeries; |
|
this.cells = this.buildCells(); |
|
this.headerDates = this.buildHeaderDates(); |
|
} |
|
DayTableModel.prototype.buildCells = function () { |
|
var rows = []; |
|
for (var row = 0; row < this.rowCnt; row += 1) { |
|
var cells = []; |
|
for (var col = 0; col < this.colCnt; col += 1) { |
|
cells.push(this.buildCell(row, col)); |
|
} |
|
rows.push(cells); |
|
} |
|
return rows; |
|
}; |
|
DayTableModel.prototype.buildCell = function (row, col) { |
|
var date = this.daySeries.dates[row * this.colCnt + col]; |
|
return { |
|
key: date.toISOString(), |
|
date: date, |
|
}; |
|
}; |
|
DayTableModel.prototype.buildHeaderDates = function () { |
|
var dates = []; |
|
for (var col = 0; col < this.colCnt; col += 1) { |
|
dates.push(this.cells[0][col].date); |
|
} |
|
return dates; |
|
}; |
|
DayTableModel.prototype.sliceRange = function (range) { |
|
var colCnt = this.colCnt; |
|
var seriesSeg = this.daySeries.sliceRange(range); |
|
var segs = []; |
|
if (seriesSeg) { |
|
var firstIndex = seriesSeg.firstIndex, lastIndex = seriesSeg.lastIndex; |
|
var index = firstIndex; |
|
while (index <= lastIndex) { |
|
var row = Math.floor(index / colCnt); |
|
var nextIndex = Math.min((row + 1) * colCnt, lastIndex + 1); |
|
segs.push({ |
|
row: row, |
|
firstCol: index % colCnt, |
|
lastCol: (nextIndex - 1) % colCnt, |
|
isStart: seriesSeg.isStart && index === firstIndex, |
|
isEnd: seriesSeg.isEnd && (nextIndex - 1) === lastIndex, |
|
}); |
|
index = nextIndex; |
|
} |
|
} |
|
return segs; |
|
}; |
|
return DayTableModel; |
|
}()); |
|
|
|
var Slicer = /** @class */ (function () { |
|
function Slicer() { |
|
this.sliceBusinessHours = memoize(this._sliceBusinessHours); |
|
this.sliceDateSelection = memoize(this._sliceDateSpan); |
|
this.sliceEventStore = memoize(this._sliceEventStore); |
|
this.sliceEventDrag = memoize(this._sliceInteraction); |
|
this.sliceEventResize = memoize(this._sliceInteraction); |
|
this.forceDayIfListItem = false; // hack |
|
} |
|
Slicer.prototype.sliceProps = function (props, dateProfile, nextDayThreshold, context) { |
|
var extraArgs = []; |
|
for (var _i = 4; _i < arguments.length; _i++) { |
|
extraArgs[_i - 4] = arguments[_i]; |
|
} |
|
var eventUiBases = props.eventUiBases; |
|
var eventSegs = this.sliceEventStore.apply(this, __spreadArrays([props.eventStore, eventUiBases, dateProfile, nextDayThreshold], extraArgs)); |
|
return { |
|
dateSelectionSegs: this.sliceDateSelection.apply(this, __spreadArrays([props.dateSelection, eventUiBases, context], extraArgs)), |
|
businessHourSegs: this.sliceBusinessHours.apply(this, __spreadArrays([props.businessHours, dateProfile, nextDayThreshold, context], extraArgs)), |
|
fgEventSegs: eventSegs.fg, |
|
bgEventSegs: eventSegs.bg, |
|
eventDrag: this.sliceEventDrag.apply(this, __spreadArrays([props.eventDrag, eventUiBases, dateProfile, nextDayThreshold], extraArgs)), |
|
eventResize: this.sliceEventResize.apply(this, __spreadArrays([props.eventResize, eventUiBases, dateProfile, nextDayThreshold], extraArgs)), |
|
eventSelection: props.eventSelection, |
|
}; // TODO: give interactionSegs? |
|
}; |
|
Slicer.prototype.sliceNowDate = function (// does not memoize |
|
date, context) { |
|
var extraArgs = []; |
|
for (var _i = 2; _i < arguments.length; _i++) { |
|
extraArgs[_i - 2] = arguments[_i]; |
|
} |
|
return this._sliceDateSpan.apply(this, __spreadArrays([{ range: { start: date, end: addMs(date, 1) }, allDay: false }, |
|
{}, |
|
context], extraArgs)); |
|
}; |
|
Slicer.prototype._sliceBusinessHours = function (businessHours, dateProfile, nextDayThreshold, context) { |
|
var extraArgs = []; |
|
for (var _i = 4; _i < arguments.length; _i++) { |
|
extraArgs[_i - 4] = arguments[_i]; |
|
} |
|
if (!businessHours) { |
|
return []; |
|
} |
|
return this._sliceEventStore.apply(this, __spreadArrays([expandRecurring(businessHours, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), context), |
|
{}, |
|
dateProfile, |
|
nextDayThreshold], extraArgs)).bg; |
|
}; |
|
Slicer.prototype._sliceEventStore = function (eventStore, eventUiBases, dateProfile, nextDayThreshold) { |
|
var extraArgs = []; |
|
for (var _i = 4; _i < arguments.length; _i++) { |
|
extraArgs[_i - 4] = arguments[_i]; |
|
} |
|
if (eventStore) { |
|
var rangeRes = sliceEventStore(eventStore, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold); |
|
return { |
|
bg: this.sliceEventRanges(rangeRes.bg, extraArgs), |
|
fg: this.sliceEventRanges(rangeRes.fg, extraArgs), |
|
}; |
|
} |
|
return { bg: [], fg: [] }; |
|
}; |
|
Slicer.prototype._sliceInteraction = function (interaction, eventUiBases, dateProfile, nextDayThreshold) { |
|
var extraArgs = []; |
|
for (var _i = 4; _i < arguments.length; _i++) { |
|
extraArgs[_i - 4] = arguments[_i]; |
|
} |
|
if (!interaction) { |
|
return null; |
|
} |
|
var rangeRes = sliceEventStore(interaction.mutatedEvents, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold); |
|
return { |
|
segs: this.sliceEventRanges(rangeRes.fg, extraArgs), |
|
affectedInstances: interaction.affectedEvents.instances, |
|
isEvent: interaction.isEvent, |
|
}; |
|
}; |
|
Slicer.prototype._sliceDateSpan = function (dateSpan, eventUiBases, context) { |
|
var extraArgs = []; |
|
for (var _i = 3; _i < arguments.length; _i++) { |
|
extraArgs[_i - 3] = arguments[_i]; |
|
} |
|
if (!dateSpan) { |
|
return []; |
|
} |
|
var eventRange = fabricateEventRange(dateSpan, eventUiBases, context); |
|
var segs = this.sliceRange.apply(this, __spreadArrays([dateSpan.range], extraArgs)); |
|
for (var _a = 0, segs_1 = segs; _a < segs_1.length; _a++) { |
|
var seg = segs_1[_a]; |
|
seg.eventRange = eventRange; |
|
} |
|
return segs; |
|
}; |
|
/* |
|
"complete" seg means it has component and eventRange |
|
*/ |
|
Slicer.prototype.sliceEventRanges = function (eventRanges, extraArgs) { |
|
var segs = []; |
|
for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) { |
|
var eventRange = eventRanges_1[_i]; |
|
segs.push.apply(segs, this.sliceEventRange(eventRange, extraArgs)); |
|
} |
|
return segs; |
|
}; |
|
/* |
|
"complete" seg means it has component and eventRange |
|
*/ |
|
Slicer.prototype.sliceEventRange = function (eventRange, extraArgs) { |
|
var dateRange = eventRange.range; |
|
// hack to make multi-day events that are being force-displayed as list-items to take up only one day |
|
if (this.forceDayIfListItem && eventRange.ui.display === 'list-item') { |
|
dateRange = { |
|
start: dateRange.start, |
|
end: addDays(dateRange.start, 1), |
|
}; |
|
} |
|
var segs = this.sliceRange.apply(this, __spreadArrays([dateRange], extraArgs)); |
|
for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { |
|
var seg = segs_2[_i]; |
|
seg.eventRange = eventRange; |
|
seg.isStart = eventRange.isStart && seg.isStart; |
|
seg.isEnd = eventRange.isEnd && seg.isEnd; |
|
} |
|
return segs; |
|
}; |
|
return Slicer; |
|
}()); |
|
/* |
|
for incorporating slotMinTime/slotMaxTime if appropriate |
|
TODO: should be part of DateProfile! |
|
TimelineDateProfile already does this btw |
|
*/ |
|
function computeActiveRange(dateProfile, isComponentAllDay) { |
|
var range = dateProfile.activeRange; |
|
if (isComponentAllDay) { |
|
return range; |
|
} |
|
return { |
|
start: addMs(range.start, dateProfile.slotMinTime.milliseconds), |
|
end: addMs(range.end, dateProfile.slotMaxTime.milliseconds - 864e5), |
|
}; |
|
} |
|
|
|
var VISIBLE_HIDDEN_RE = /^(visible|hidden)$/; |
|
var Scroller = /** @class */ (function (_super) { |
|
__extends(Scroller, _super); |
|
function Scroller() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.handleEl = function (el) { |
|
_this.el = el; |
|
setRef(_this.props.elRef, el); |
|
}; |
|
return _this; |
|
} |
|
Scroller.prototype.render = function () { |
|
var props = this.props; |
|
var liquid = props.liquid, liquidIsAbsolute = props.liquidIsAbsolute; |
|
var isAbsolute = liquid && liquidIsAbsolute; |
|
var className = ['fc-scroller']; |
|
if (liquid) { |
|
if (liquidIsAbsolute) { |
|
className.push('fc-scroller-liquid-absolute'); |
|
} |
|
else { |
|
className.push('fc-scroller-liquid'); |
|
} |
|
} |
|
return (createElement("div", { ref: this.handleEl, className: className.join(' '), style: { |
|
overflowX: props.overflowX, |
|
overflowY: props.overflowY, |
|
left: (isAbsolute && -(props.overcomeLeft || 0)) || '', |
|
right: (isAbsolute && -(props.overcomeRight || 0)) || '', |
|
bottom: (isAbsolute && -(props.overcomeBottom || 0)) || '', |
|
marginLeft: (!isAbsolute && -(props.overcomeLeft || 0)) || '', |
|
marginRight: (!isAbsolute && -(props.overcomeRight || 0)) || '', |
|
marginBottom: (!isAbsolute && -(props.overcomeBottom || 0)) || '', |
|
maxHeight: props.maxHeight || '', |
|
} }, props.children)); |
|
}; |
|
Scroller.prototype.needsXScrolling = function () { |
|
if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) { |
|
return false; |
|
} |
|
// testing scrollWidth>clientWidth is unreliable cross-browser when pixel heights aren't integers. |
|
// much more reliable to see if children are taller than the scroller, even tho doesn't account for |
|
// inner-child margins and absolute positioning |
|
var el = this.el; |
|
var realClientWidth = this.el.getBoundingClientRect().width - this.getYScrollbarWidth(); |
|
var children = el.children; |
|
for (var i = 0; i < children.length; i += 1) { |
|
var childEl = children[i]; |
|
if (childEl.getBoundingClientRect().width > realClientWidth) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
}; |
|
Scroller.prototype.needsYScrolling = function () { |
|
if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) { |
|
return false; |
|
} |
|
// testing scrollHeight>clientHeight is unreliable cross-browser when pixel heights aren't integers. |
|
// much more reliable to see if children are taller than the scroller, even tho doesn't account for |
|
// inner-child margins and absolute positioning |
|
var el = this.el; |
|
var realClientHeight = this.el.getBoundingClientRect().height - this.getXScrollbarWidth(); |
|
var children = el.children; |
|
for (var i = 0; i < children.length; i += 1) { |
|
var childEl = children[i]; |
|
if (childEl.getBoundingClientRect().height > realClientHeight) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
}; |
|
Scroller.prototype.getXScrollbarWidth = function () { |
|
if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) { |
|
return 0; |
|
} |
|
return this.el.offsetHeight - this.el.clientHeight; // only works because we guarantee no borders. TODO: add to CSS with important? |
|
}; |
|
Scroller.prototype.getYScrollbarWidth = function () { |
|
if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) { |
|
return 0; |
|
} |
|
return this.el.offsetWidth - this.el.clientWidth; // only works because we guarantee no borders. TODO: add to CSS with important? |
|
}; |
|
return Scroller; |
|
}(BaseComponent)); |
|
|
|
/* |
|
TODO: somehow infer OtherArgs from masterCallback? |
|
TODO: infer RefType from masterCallback if provided |
|
*/ |
|
var RefMap = /** @class */ (function () { |
|
function RefMap(masterCallback) { |
|
var _this = this; |
|
this.masterCallback = masterCallback; |
|
this.currentMap = {}; |
|
this.depths = {}; |
|
this.callbackMap = {}; |
|
this.handleValue = function (val, key) { |
|
var _a = _this, depths = _a.depths, currentMap = _a.currentMap; |
|
var removed = false; |
|
var added = false; |
|
if (val !== null) { |
|
// for bug... ACTUALLY: can probably do away with this now that callers don't share numeric indices anymore |
|
removed = (key in currentMap); |
|
currentMap[key] = val; |
|
depths[key] = (depths[key] || 0) + 1; |
|
added = true; |
|
} |
|
else { |
|
depths[key] -= 1; |
|
if (!depths[key]) { |
|
delete currentMap[key]; |
|
delete _this.callbackMap[key]; |
|
removed = true; |
|
} |
|
} |
|
if (_this.masterCallback) { |
|
if (removed) { |
|
_this.masterCallback(null, String(key)); |
|
} |
|
if (added) { |
|
_this.masterCallback(val, String(key)); |
|
} |
|
} |
|
}; |
|
} |
|
RefMap.prototype.createRef = function (key) { |
|
var _this = this; |
|
var refCallback = this.callbackMap[key]; |
|
if (!refCallback) { |
|
refCallback = this.callbackMap[key] = function (val) { |
|
_this.handleValue(val, String(key)); |
|
}; |
|
} |
|
return refCallback; |
|
}; |
|
// TODO: check callers that don't care about order. should use getAll instead |
|
// NOTE: this method has become less valuable now that we are encouraged to map order by some other index |
|
// TODO: provide ONE array-export function, buildArray, which fails on non-numeric indexes. caller can manipulate and "collect" |
|
RefMap.prototype.collect = function (startIndex, endIndex, step) { |
|
return collectFromHash(this.currentMap, startIndex, endIndex, step); |
|
}; |
|
RefMap.prototype.getAll = function () { |
|
return hashValuesToArray(this.currentMap); |
|
}; |
|
return RefMap; |
|
}()); |
|
|
|
function computeShrinkWidth(chunkEls) { |
|
var shrinkCells = findElements(chunkEls, '.fc-scrollgrid-shrink'); |
|
var largestWidth = 0; |
|
for (var _i = 0, shrinkCells_1 = shrinkCells; _i < shrinkCells_1.length; _i++) { |
|
var shrinkCell = shrinkCells_1[_i]; |
|
largestWidth = Math.max(largestWidth, computeSmallestCellWidth(shrinkCell)); |
|
} |
|
return Math.ceil(largestWidth); // <table> elements work best with integers. round up to ensure contents fits |
|
} |
|
function getSectionHasLiquidHeight(props, sectionConfig) { |
|
return props.liquid && sectionConfig.liquid; // does the section do liquid-height? (need to have whole scrollgrid liquid-height as well) |
|
} |
|
function getAllowYScrolling(props, sectionConfig) { |
|
return sectionConfig.maxHeight != null || // if its possible for the height to max out, we might need scrollbars |
|
getSectionHasLiquidHeight(props, sectionConfig); // if the section is liquid height, it might condense enough to require scrollbars |
|
} |
|
// TODO: ONLY use `arg`. force out internal function to use same API |
|
function renderChunkContent(sectionConfig, chunkConfig, arg) { |
|
var expandRows = arg.expandRows; |
|
var content = typeof chunkConfig.content === 'function' ? |
|
chunkConfig.content(arg) : |
|
createElement('table', { |
|
className: [ |
|
chunkConfig.tableClassName, |
|
sectionConfig.syncRowHeights ? 'fc-scrollgrid-sync-table' : '', |
|
].join(' '), |
|
style: { |
|
minWidth: arg.tableMinWidth, |
|
width: arg.clientWidth, |
|
height: expandRows ? arg.clientHeight : '', |
|
}, |
|
}, arg.tableColGroupNode, createElement('tbody', {}, typeof chunkConfig.rowContent === 'function' ? chunkConfig.rowContent(arg) : chunkConfig.rowContent)); |
|
return content; |
|
} |
|
function isColPropsEqual(cols0, cols1) { |
|
return isArraysEqual(cols0, cols1, isPropsEqual); |
|
} |
|
function renderMicroColGroup(cols, shrinkWidth) { |
|
var colNodes = []; |
|
/* |
|
for ColProps with spans, it would have been great to make a single <col span=""> |
|
HOWEVER, Chrome was getting messing up distributing the width to <td>/<th> elements with colspans. |
|
SOLUTION: making individual <col> elements makes Chrome behave. |
|
*/ |
|
for (var _i = 0, cols_1 = cols; _i < cols_1.length; _i++) { |
|
var colProps = cols_1[_i]; |
|
var span = colProps.span || 1; |
|
for (var i = 0; i < span; i += 1) { |
|
colNodes.push(createElement("col", { style: { |
|
width: colProps.width === 'shrink' ? sanitizeShrinkWidth(shrinkWidth) : (colProps.width || ''), |
|
minWidth: colProps.minWidth || '', |
|
} })); |
|
} |
|
} |
|
return createElement.apply(void 0, __spreadArrays(['colgroup', {}], colNodes)); |
|
} |
|
function sanitizeShrinkWidth(shrinkWidth) { |
|
/* why 4? if we do 0, it will kill any border, which are needed for computeSmallestCellWidth |
|
4 accounts for 2 2-pixel borders. TODO: better solution? */ |
|
return shrinkWidth == null ? 4 : shrinkWidth; |
|
} |
|
function hasShrinkWidth(cols) { |
|
for (var _i = 0, cols_2 = cols; _i < cols_2.length; _i++) { |
|
var col = cols_2[_i]; |
|
if (col.width === 'shrink') { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
function getScrollGridClassNames(liquid, context) { |
|
var classNames = [ |
|
'fc-scrollgrid', |
|
context.theme.getClass('table'), |
|
]; |
|
if (liquid) { |
|
classNames.push('fc-scrollgrid-liquid'); |
|
} |
|
return classNames; |
|
} |
|
function getSectionClassNames(sectionConfig, wholeTableVGrow) { |
|
var classNames = [ |
|
'fc-scrollgrid-section', |
|
"fc-scrollgrid-section-" + sectionConfig.type, |
|
sectionConfig.className, |
|
]; |
|
if (wholeTableVGrow && sectionConfig.liquid && sectionConfig.maxHeight == null) { |
|
classNames.push('fc-scrollgrid-section-liquid'); |
|
} |
|
if (sectionConfig.isSticky) { |
|
classNames.push('fc-scrollgrid-section-sticky'); |
|
} |
|
return classNames; |
|
} |
|
function renderScrollShim(arg) { |
|
return (createElement("div", { className: "fc-scrollgrid-sticky-shim", style: { |
|
width: arg.clientWidth, |
|
minWidth: arg.tableMinWidth, |
|
} })); |
|
} |
|
function getStickyHeaderDates(options) { |
|
var stickyHeaderDates = options.stickyHeaderDates; |
|
if (stickyHeaderDates == null || stickyHeaderDates === 'auto') { |
|
stickyHeaderDates = options.height === 'auto' || options.viewHeight === 'auto'; |
|
} |
|
return stickyHeaderDates; |
|
} |
|
function getStickyFooterScrollbar(options) { |
|
var stickyFooterScrollbar = options.stickyFooterScrollbar; |
|
if (stickyFooterScrollbar == null || stickyFooterScrollbar === 'auto') { |
|
stickyFooterScrollbar = options.height === 'auto' || options.viewHeight === 'auto'; |
|
} |
|
return stickyFooterScrollbar; |
|
} |
|
|
|
var SimpleScrollGrid = /** @class */ (function (_super) { |
|
__extends(SimpleScrollGrid, _super); |
|
function SimpleScrollGrid() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.processCols = memoize(function (a) { return a; }, isColPropsEqual); // so we get same `cols` props every time |
|
// yucky to memoize VNodes, but much more efficient for consumers |
|
_this.renderMicroColGroup = memoize(renderMicroColGroup); |
|
_this.scrollerRefs = new RefMap(); |
|
_this.scrollerElRefs = new RefMap(_this._handleScrollerEl.bind(_this)); |
|
_this.state = { |
|
shrinkWidth: null, |
|
forceYScrollbars: false, |
|
scrollerClientWidths: {}, |
|
scrollerClientHeights: {}, |
|
}; |
|
// TODO: can do a really simple print-view. dont need to join rows |
|
_this.handleSizing = function () { |
|
_this.setState(__assign({ shrinkWidth: _this.computeShrinkWidth() }, _this.computeScrollerDims())); |
|
}; |
|
return _this; |
|
} |
|
SimpleScrollGrid.prototype.render = function () { |
|
var _a = this, props = _a.props, state = _a.state, context = _a.context; |
|
var sectionConfigs = props.sections || []; |
|
var cols = this.processCols(props.cols); |
|
var microColGroupNode = this.renderMicroColGroup(cols, state.shrinkWidth); |
|
var classNames = getScrollGridClassNames(props.liquid, context); |
|
// TODO: make DRY |
|
var configCnt = sectionConfigs.length; |
|
var configI = 0; |
|
var currentConfig; |
|
var headSectionNodes = []; |
|
var bodySectionNodes = []; |
|
var footSectionNodes = []; |
|
while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'header') { |
|
headSectionNodes.push(this.renderSection(currentConfig, microColGroupNode)); |
|
configI += 1; |
|
} |
|
while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'body') { |
|
bodySectionNodes.push(this.renderSection(currentConfig, microColGroupNode)); |
|
configI += 1; |
|
} |
|
while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'footer') { |
|
footSectionNodes.push(this.renderSection(currentConfig, microColGroupNode)); |
|
configI += 1; |
|
} |
|
// firefox bug: when setting height on table and there is a thead or tfoot, |
|
// the necessary height:100% on the liquid-height body section forces the *whole* table to be taller. (bug #5524) |
|
// use getCanVGrowWithinCell as a way to detect table-stupid firefox. |
|
// if so, use a simpler dom structure, jam everything into a lone tbody. |
|
var isBuggy = !getCanVGrowWithinCell(); |
|
return createElement('table', { |
|
className: classNames.join(' '), |
|
style: { height: props.height }, |
|
}, Boolean(!isBuggy && headSectionNodes.length) && createElement.apply(void 0, __spreadArrays(['thead', {}], headSectionNodes)), Boolean(!isBuggy && bodySectionNodes.length) && createElement.apply(void 0, __spreadArrays(['tbody', {}], bodySectionNodes)), Boolean(!isBuggy && footSectionNodes.length) && createElement.apply(void 0, __spreadArrays(['tfoot', {}], footSectionNodes)), isBuggy && createElement.apply(void 0, __spreadArrays(['tbody', {}], headSectionNodes, bodySectionNodes, footSectionNodes))); |
|
}; |
|
SimpleScrollGrid.prototype.renderSection = function (sectionConfig, microColGroupNode) { |
|
if ('outerContent' in sectionConfig) { |
|
return (createElement(Fragment, { key: sectionConfig.key }, sectionConfig.outerContent)); |
|
} |
|
return (createElement("tr", { key: sectionConfig.key, className: getSectionClassNames(sectionConfig, this.props.liquid).join(' ') }, this.renderChunkTd(sectionConfig, microColGroupNode, sectionConfig.chunk))); |
|
}; |
|
SimpleScrollGrid.prototype.renderChunkTd = function (sectionConfig, microColGroupNode, chunkConfig) { |
|
if ('outerContent' in chunkConfig) { |
|
return chunkConfig.outerContent; |
|
} |
|
var props = this.props; |
|
var _a = this.state, forceYScrollbars = _a.forceYScrollbars, scrollerClientWidths = _a.scrollerClientWidths, scrollerClientHeights = _a.scrollerClientHeights; |
|
var needsYScrolling = getAllowYScrolling(props, sectionConfig); // TODO: do lazily. do in section config? |
|
var isLiquid = getSectionHasLiquidHeight(props, sectionConfig); |
|
// for `!props.liquid` - is WHOLE scrollgrid natural height? |
|
// TODO: do same thing in advanced scrollgrid? prolly not b/c always has horizontal scrollbars |
|
var overflowY = !props.liquid ? 'visible' : |
|
forceYScrollbars ? 'scroll' : |
|
!needsYScrolling ? 'hidden' : |
|
'auto'; |
|
var sectionKey = sectionConfig.key; |
|
var content = renderChunkContent(sectionConfig, chunkConfig, { |
|
tableColGroupNode: microColGroupNode, |
|
tableMinWidth: '', |
|
clientWidth: scrollerClientWidths[sectionKey] !== undefined ? scrollerClientWidths[sectionKey] : null, |
|
clientHeight: scrollerClientHeights[sectionKey] !== undefined ? scrollerClientHeights[sectionKey] : null, |
|
expandRows: sectionConfig.expandRows, |
|
syncRowHeights: false, |
|
rowSyncHeights: [], |
|
reportRowHeightChange: function () { }, |
|
}); |
|
return (createElement("td", { ref: chunkConfig.elRef }, |
|
createElement("div", { className: "fc-scroller-harness" + (isLiquid ? ' fc-scroller-harness-liquid' : '') }, |
|
createElement(Scroller, { ref: this.scrollerRefs.createRef(sectionKey), elRef: this.scrollerElRefs.createRef(sectionKey), overflowY: overflowY, overflowX: !props.liquid ? 'visible' : 'hidden' /* natural height? */, maxHeight: sectionConfig.maxHeight, liquid: isLiquid, liquidIsAbsolute // because its within a harness |
|
: true }, content)))); |
|
}; |
|
SimpleScrollGrid.prototype._handleScrollerEl = function (scrollerEl, key) { |
|
var section = getSectionByKey(this.props.sections, key); |
|
if (section) { |
|
setRef(section.chunk.scrollerElRef, scrollerEl); |
|
} |
|
}; |
|
SimpleScrollGrid.prototype.componentDidMount = function () { |
|
this.handleSizing(); |
|
this.context.addResizeHandler(this.handleSizing); |
|
}; |
|
SimpleScrollGrid.prototype.componentDidUpdate = function () { |
|
// TODO: need better solution when state contains non-sizing things |
|
this.handleSizing(); |
|
}; |
|
SimpleScrollGrid.prototype.componentWillUnmount = function () { |
|
this.context.removeResizeHandler(this.handleSizing); |
|
}; |
|
SimpleScrollGrid.prototype.computeShrinkWidth = function () { |
|
return hasShrinkWidth(this.props.cols) |
|
? computeShrinkWidth(this.scrollerElRefs.getAll()) |
|
: 0; |
|
}; |
|
SimpleScrollGrid.prototype.computeScrollerDims = function () { |
|
var scrollbarWidth = getScrollbarWidths(); |
|
var _a = this, scrollerRefs = _a.scrollerRefs, scrollerElRefs = _a.scrollerElRefs; |
|
var forceYScrollbars = false; |
|
var scrollerClientWidths = {}; |
|
var scrollerClientHeights = {}; |
|
for (var sectionKey in scrollerRefs.currentMap) { |
|
var scroller = scrollerRefs.currentMap[sectionKey]; |
|
if (scroller && scroller.needsYScrolling()) { |
|
forceYScrollbars = true; |
|
break; |
|
} |
|
} |
|
for (var _i = 0, _b = this.props.sections; _i < _b.length; _i++) { |
|
var section = _b[_i]; |
|
var sectionKey = section.key; |
|
var scrollerEl = scrollerElRefs.currentMap[sectionKey]; |
|
if (scrollerEl) { |
|
var harnessEl = scrollerEl.parentNode; // TODO: weird way to get this. need harness b/c doesn't include table borders |
|
scrollerClientWidths[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().width - (forceYScrollbars |
|
? scrollbarWidth.y // use global because scroller might not have scrollbars yet but will need them in future |
|
: 0)); |
|
scrollerClientHeights[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().height); |
|
} |
|
} |
|
return { forceYScrollbars: forceYScrollbars, scrollerClientWidths: scrollerClientWidths, scrollerClientHeights: scrollerClientHeights }; |
|
}; |
|
return SimpleScrollGrid; |
|
}(BaseComponent)); |
|
SimpleScrollGrid.addStateEquality({ |
|
scrollerClientWidths: isPropsEqual, |
|
scrollerClientHeights: isPropsEqual, |
|
}); |
|
function getSectionByKey(sections, key) { |
|
for (var _i = 0, sections_1 = sections; _i < sections_1.length; _i++) { |
|
var section = sections_1[_i]; |
|
if (section.key === key) { |
|
return section; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
var EventRoot = /** @class */ (function (_super) { |
|
__extends(EventRoot, _super); |
|
function EventRoot() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.elRef = createRef(); |
|
return _this; |
|
} |
|
EventRoot.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var options = context.options; |
|
var seg = props.seg; |
|
var eventRange = seg.eventRange; |
|
var ui = eventRange.ui; |
|
var hookProps = { |
|
event: new EventApi(context, eventRange.def, eventRange.instance), |
|
view: context.viewApi, |
|
timeText: props.timeText, |
|
textColor: ui.textColor, |
|
backgroundColor: ui.backgroundColor, |
|
borderColor: ui.borderColor, |
|
isDraggable: !props.disableDragging && computeSegDraggable(seg, context), |
|
isStartResizable: !props.disableResizing && computeSegStartResizable(seg, context), |
|
isEndResizable: !props.disableResizing && computeSegEndResizable(seg), |
|
isMirror: Boolean(props.isDragging || props.isResizing || props.isDateSelecting), |
|
isStart: Boolean(seg.isStart), |
|
isEnd: Boolean(seg.isEnd), |
|
isPast: Boolean(props.isPast), |
|
isFuture: Boolean(props.isFuture), |
|
isToday: Boolean(props.isToday), |
|
isSelected: Boolean(props.isSelected), |
|
isDragging: Boolean(props.isDragging), |
|
isResizing: Boolean(props.isResizing), |
|
}; |
|
var standardClassNames = getEventClassNames(hookProps).concat(ui.classNames); |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.eventClassNames, content: options.eventContent, defaultContent: props.defaultContent, didMount: options.eventDidMount, willUnmount: options.eventWillUnmount, elRef: this.elRef }, function (rootElRef, customClassNames, innerElRef, innerContent) { return props.children(rootElRef, standardClassNames.concat(customClassNames), innerElRef, innerContent, hookProps); })); |
|
}; |
|
EventRoot.prototype.componentDidMount = function () { |
|
setElSeg(this.elRef.current, this.props.seg); |
|
}; |
|
/* |
|
need to re-assign seg to the element if seg changes, even if the element is the same |
|
*/ |
|
EventRoot.prototype.componentDidUpdate = function (prevProps) { |
|
var seg = this.props.seg; |
|
if (seg !== prevProps.seg) { |
|
setElSeg(this.elRef.current, seg); |
|
} |
|
}; |
|
return EventRoot; |
|
}(BaseComponent)); |
|
|
|
// should not be a purecomponent |
|
var StandardEvent = /** @class */ (function (_super) { |
|
__extends(StandardEvent, _super); |
|
function StandardEvent() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
StandardEvent.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var seg = props.seg; |
|
var timeFormat = context.options.eventTimeFormat || props.defaultTimeFormat; |
|
var timeText = buildSegTimeText(seg, timeFormat, context, props.defaultDisplayEventTime, props.defaultDisplayEventEnd); |
|
return (createElement(EventRoot, { seg: seg, timeText: timeText, disableDragging: props.disableDragging, disableResizing: props.disableResizing, defaultContent: props.defaultContent || renderInnerContent, isDragging: props.isDragging, isResizing: props.isResizing, isDateSelecting: props.isDateSelecting, isSelected: props.isSelected, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent, hookProps) { return (createElement("a", __assign({ className: props.extraClassNames.concat(classNames).join(' '), style: { |
|
borderColor: hookProps.borderColor, |
|
backgroundColor: hookProps.backgroundColor, |
|
}, ref: rootElRef }, getSegAnchorAttrs(seg)), |
|
createElement("div", { className: "fc-event-main", ref: innerElRef, style: { color: hookProps.textColor } }, innerContent), |
|
hookProps.isStartResizable && |
|
createElement("div", { className: "fc-event-resizer fc-event-resizer-start" }), |
|
hookProps.isEndResizable && |
|
createElement("div", { className: "fc-event-resizer fc-event-resizer-end" }))); })); |
|
}; |
|
return StandardEvent; |
|
}(BaseComponent)); |
|
function renderInnerContent(innerProps) { |
|
return (createElement("div", { className: "fc-event-main-frame" }, |
|
innerProps.timeText && (createElement("div", { className: "fc-event-time" }, innerProps.timeText)), |
|
createElement("div", { className: "fc-event-title-container" }, |
|
createElement("div", { className: "fc-event-title fc-sticky" }, innerProps.event.title || createElement(Fragment, null, "\u00A0"))))); |
|
} |
|
function getSegAnchorAttrs(seg) { |
|
var url = seg.eventRange.def.url; |
|
return url ? { href: url } : {}; |
|
} |
|
|
|
var NowIndicatorRoot = function (props) { return (createElement(ViewContextType.Consumer, null, function (context) { |
|
var options = context.options; |
|
var hookProps = { |
|
isAxis: props.isAxis, |
|
date: context.dateEnv.toDate(props.date), |
|
view: context.viewApi, |
|
}; |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.nowIndicatorClassNames, content: options.nowIndicatorContent, didMount: options.nowIndicatorDidMount, willUnmount: options.nowIndicatorWillUnmount }, props.children)); |
|
})); }; |
|
|
|
var DAY_NUM_FORMAT = createFormatter({ day: 'numeric' }); |
|
var DayCellContent = /** @class */ (function (_super) { |
|
__extends(DayCellContent, _super); |
|
function DayCellContent() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
DayCellContent.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var options = context.options; |
|
var hookProps = refineDayCellHookProps({ |
|
date: props.date, |
|
dateProfile: props.dateProfile, |
|
todayRange: props.todayRange, |
|
showDayNumber: props.showDayNumber, |
|
extraProps: props.extraHookProps, |
|
viewApi: context.viewApi, |
|
dateEnv: context.dateEnv, |
|
}); |
|
return (createElement(ContentHook, { hookProps: hookProps, content: options.dayCellContent, defaultContent: props.defaultContent }, props.children)); |
|
}; |
|
return DayCellContent; |
|
}(BaseComponent)); |
|
function refineDayCellHookProps(raw) { |
|
var date = raw.date, dateEnv = raw.dateEnv; |
|
var dayMeta = getDateMeta(date, raw.todayRange, null, raw.dateProfile); |
|
return __assign(__assign(__assign({ date: dateEnv.toDate(date), view: raw.viewApi }, dayMeta), { dayNumberText: raw.showDayNumber ? dateEnv.format(date, DAY_NUM_FORMAT) : '' }), raw.extraProps); |
|
} |
|
|
|
var DayCellRoot = /** @class */ (function (_super) { |
|
__extends(DayCellRoot, _super); |
|
function DayCellRoot() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.refineHookProps = memoizeObjArg(refineDayCellHookProps); |
|
_this.normalizeClassNames = buildClassNameNormalizer(); |
|
return _this; |
|
} |
|
DayCellRoot.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var options = context.options; |
|
var hookProps = this.refineHookProps({ |
|
date: props.date, |
|
dateProfile: props.dateProfile, |
|
todayRange: props.todayRange, |
|
showDayNumber: props.showDayNumber, |
|
extraProps: props.extraHookProps, |
|
viewApi: context.viewApi, |
|
dateEnv: context.dateEnv, |
|
}); |
|
var classNames = getDayClassNames(hookProps, context.theme).concat(hookProps.isDisabled |
|
? [] // don't use custom classNames if disabled |
|
: this.normalizeClassNames(options.dayCellClassNames, hookProps)); |
|
var dataAttrs = hookProps.isDisabled ? {} : { |
|
'data-date': formatDayString(props.date), |
|
}; |
|
return (createElement(MountHook, { hookProps: hookProps, didMount: options.dayCellDidMount, willUnmount: options.dayCellWillUnmount, elRef: props.elRef }, function (rootElRef) { return props.children(rootElRef, classNames, dataAttrs, hookProps.isDisabled); })); |
|
}; |
|
return DayCellRoot; |
|
}(BaseComponent)); |
|
|
|
function renderFill(fillType) { |
|
return (createElement("div", { className: "fc-" + fillType })); |
|
} |
|
var BgEvent = function (props) { return (createElement(EventRoot, { defaultContent: renderInnerContent$1, seg: props.seg /* uselesss i think */, timeText: "", disableDragging: true, disableResizing: true, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent, hookProps) { return (createElement("div", { ref: rootElRef, className: ['fc-bg-event'].concat(classNames).join(' '), style: { |
|
backgroundColor: hookProps.backgroundColor, |
|
} }, innerContent)); })); }; |
|
function renderInnerContent$1(props) { |
|
var title = props.event.title; |
|
return title && (createElement("div", { className: "fc-event-title" }, props.event.title)); |
|
} |
|
|
|
var WeekNumberRoot = function (props) { return (createElement(ViewContextType.Consumer, null, function (context) { |
|
var dateEnv = context.dateEnv, options = context.options; |
|
var date = props.date; |
|
var format = options.weekNumberFormat || props.defaultFormat; |
|
var num = dateEnv.computeWeekNumber(date); // TODO: somehow use for formatting as well? |
|
var text = dateEnv.format(date, format); |
|
var hookProps = { num: num, text: text, date: date }; |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.weekNumberClassNames, content: options.weekNumberContent, defaultContent: renderInner$1, didMount: options.weekNumberDidMount, willUnmount: options.weekNumberWillUnmount }, props.children)); |
|
})); }; |
|
function renderInner$1(innerProps) { |
|
return innerProps.text; |
|
} |
|
|
|
// exports |
|
// -------------------------------------------------------------------------------------------------- |
|
var version = '5.5.1'; // important to type it, so .d.ts has generic string |
|
|
|
var Calendar = /** @class */ (function (_super) { |
|
__extends(Calendar, _super); |
|
function Calendar(el, optionOverrides) { |
|
if (optionOverrides === void 0) { optionOverrides = {}; } |
|
var _this = _super.call(this) || this; |
|
_this.isRendering = false; |
|
_this.isRendered = false; |
|
_this.currentClassNames = []; |
|
_this.customContentRenderId = 0; // will affect custom generated classNames? |
|
_this.handleAction = function (action) { |
|
// actions we know we want to render immediately |
|
switch (action.type) { |
|
case 'SET_EVENT_DRAG': |
|
case 'SET_EVENT_RESIZE': |
|
_this.renderRunner.tryDrain(); |
|
} |
|
}; |
|
_this.handleData = function (data) { |
|
_this.currentData = data; |
|
_this.renderRunner.request(data.calendarOptions.rerenderDelay); |
|
}; |
|
_this.handleRenderRequest = function () { |
|
if (_this.isRendering) { |
|
_this.isRendered = true; |
|
var currentData_1 = _this.currentData; |
|
render(createElement(CalendarRoot, { options: currentData_1.calendarOptions, theme: currentData_1.theme, emitter: currentData_1.emitter }, function (classNames, height, isHeightAuto, forPrint) { |
|
_this.setClassNames(classNames); |
|
_this.setHeight(height); |
|
return (createElement(CustomContentRenderContext.Provider, { value: _this.customContentRenderId }, |
|
createElement(CalendarContent, __assign({ isHeightAuto: isHeightAuto, forPrint: forPrint }, currentData_1)))); |
|
}), _this.el); |
|
} |
|
else if (_this.isRendered) { |
|
_this.isRendered = false; |
|
unmountComponentAtNode$1(_this.el); |
|
_this.setClassNames([]); |
|
_this.setHeight(''); |
|
} |
|
flushToDom$1(); |
|
}; |
|
_this.el = el; |
|
_this.renderRunner = new DelayedRunner(_this.handleRenderRequest); |
|
new CalendarDataManager({ |
|
optionOverrides: optionOverrides, |
|
calendarApi: _this, |
|
onAction: _this.handleAction, |
|
onData: _this.handleData, |
|
}); |
|
return _this; |
|
} |
|
Object.defineProperty(Calendar.prototype, "view", { |
|
get: function () { return this.currentData.viewApi; } // for public API |
|
, |
|
enumerable: false, |
|
configurable: true |
|
}); |
|
Calendar.prototype.render = function () { |
|
var wasRendering = this.isRendering; |
|
if (!wasRendering) { |
|
this.isRendering = true; |
|
} |
|
else { |
|
this.customContentRenderId += 1; |
|
} |
|
this.renderRunner.request(); |
|
if (wasRendering) { |
|
this.updateSize(); |
|
} |
|
}; |
|
Calendar.prototype.destroy = function () { |
|
if (this.isRendering) { |
|
this.isRendering = false; |
|
this.renderRunner.request(); |
|
} |
|
}; |
|
Calendar.prototype.updateSize = function () { |
|
_super.prototype.updateSize.call(this); |
|
flushToDom$1(); |
|
}; |
|
Calendar.prototype.batchRendering = function (func) { |
|
this.renderRunner.pause('batchRendering'); |
|
func(); |
|
this.renderRunner.resume('batchRendering'); |
|
}; |
|
Calendar.prototype.pauseRendering = function () { |
|
this.renderRunner.pause('pauseRendering'); |
|
}; |
|
Calendar.prototype.resumeRendering = function () { |
|
this.renderRunner.resume('pauseRendering', true); |
|
}; |
|
Calendar.prototype.resetOptions = function (optionOverrides, append) { |
|
this.currentDataManager.resetOptions(optionOverrides, append); |
|
}; |
|
Calendar.prototype.setClassNames = function (classNames) { |
|
if (!isArraysEqual(classNames, this.currentClassNames)) { |
|
var classList = this.el.classList; |
|
for (var _i = 0, _a = this.currentClassNames; _i < _a.length; _i++) { |
|
var className = _a[_i]; |
|
classList.remove(className); |
|
} |
|
for (var _b = 0, classNames_1 = classNames; _b < classNames_1.length; _b++) { |
|
var className = classNames_1[_b]; |
|
classList.add(className); |
|
} |
|
this.currentClassNames = classNames; |
|
} |
|
}; |
|
Calendar.prototype.setHeight = function (height) { |
|
applyStyleProp(this.el, 'height', height); |
|
}; |
|
return Calendar; |
|
}(CalendarApi)); |
|
|
|
config.touchMouseIgnoreWait = 500; |
|
var ignoreMouseDepth = 0; |
|
var listenerCnt = 0; |
|
var isWindowTouchMoveCancelled = false; |
|
/* |
|
Uses a "pointer" abstraction, which monitors UI events for both mouse and touch. |
|
Tracks when the pointer "drags" on a certain element, meaning down+move+up. |
|
|
|
Also, tracks if there was touch-scrolling. |
|
Also, can prevent touch-scrolling from happening. |
|
Also, can fire pointermove events when scrolling happens underneath, even when no real pointer movement. |
|
|
|
emits: |
|
- pointerdown |
|
- pointermove |
|
- pointerup |
|
*/ |
|
var PointerDragging = /** @class */ (function () { |
|
function PointerDragging(containerEl) { |
|
var _this = this; |
|
this.subjectEl = null; |
|
// options that can be directly assigned by caller |
|
this.selector = ''; // will cause subjectEl in all emitted events to be this element |
|
this.handleSelector = ''; |
|
this.shouldIgnoreMove = false; |
|
this.shouldWatchScroll = true; // for simulating pointermove on scroll |
|
// internal states |
|
this.isDragging = false; |
|
this.isTouchDragging = false; |
|
this.wasTouchScroll = false; |
|
// Mouse |
|
// ---------------------------------------------------------------------------------------------------- |
|
this.handleMouseDown = function (ev) { |
|
if (!_this.shouldIgnoreMouse() && |
|
isPrimaryMouseButton(ev) && |
|
_this.tryStart(ev)) { |
|
var pev = _this.createEventFromMouse(ev, true); |
|
_this.emitter.trigger('pointerdown', pev); |
|
_this.initScrollWatch(pev); |
|
if (!_this.shouldIgnoreMove) { |
|
document.addEventListener('mousemove', _this.handleMouseMove); |
|
} |
|
document.addEventListener('mouseup', _this.handleMouseUp); |
|
} |
|
}; |
|
this.handleMouseMove = function (ev) { |
|
var pev = _this.createEventFromMouse(ev); |
|
_this.recordCoords(pev); |
|
_this.emitter.trigger('pointermove', pev); |
|
}; |
|
this.handleMouseUp = function (ev) { |
|
document.removeEventListener('mousemove', _this.handleMouseMove); |
|
document.removeEventListener('mouseup', _this.handleMouseUp); |
|
_this.emitter.trigger('pointerup', _this.createEventFromMouse(ev)); |
|
_this.cleanup(); // call last so that pointerup has access to props |
|
}; |
|
// Touch |
|
// ---------------------------------------------------------------------------------------------------- |
|
this.handleTouchStart = function (ev) { |
|
if (_this.tryStart(ev)) { |
|
_this.isTouchDragging = true; |
|
var pev = _this.createEventFromTouch(ev, true); |
|
_this.emitter.trigger('pointerdown', pev); |
|
_this.initScrollWatch(pev); |
|
// unlike mouse, need to attach to target, not document |
|
// https://stackoverflow.com/a/45760014 |
|
var targetEl = ev.target; |
|
if (!_this.shouldIgnoreMove) { |
|
targetEl.addEventListener('touchmove', _this.handleTouchMove); |
|
} |
|
targetEl.addEventListener('touchend', _this.handleTouchEnd); |
|
targetEl.addEventListener('touchcancel', _this.handleTouchEnd); // treat it as a touch end |
|
// attach a handler to get called when ANY scroll action happens on the page. |
|
// this was impossible to do with normal on/off because 'scroll' doesn't bubble. |
|
// http://stackoverflow.com/a/32954565/96342 |
|
window.addEventListener('scroll', _this.handleTouchScroll, true); |
|
} |
|
}; |
|
this.handleTouchMove = function (ev) { |
|
var pev = _this.createEventFromTouch(ev); |
|
_this.recordCoords(pev); |
|
_this.emitter.trigger('pointermove', pev); |
|
}; |
|
this.handleTouchEnd = function (ev) { |
|
if (_this.isDragging) { // done to guard against touchend followed by touchcancel |
|
var targetEl = ev.target; |
|
targetEl.removeEventListener('touchmove', _this.handleTouchMove); |
|
targetEl.removeEventListener('touchend', _this.handleTouchEnd); |
|
targetEl.removeEventListener('touchcancel', _this.handleTouchEnd); |
|
window.removeEventListener('scroll', _this.handleTouchScroll, true); // useCaptured=true |
|
_this.emitter.trigger('pointerup', _this.createEventFromTouch(ev)); |
|
_this.cleanup(); // call last so that pointerup has access to props |
|
_this.isTouchDragging = false; |
|
startIgnoringMouse(); |
|
} |
|
}; |
|
this.handleTouchScroll = function () { |
|
_this.wasTouchScroll = true; |
|
}; |
|
this.handleScroll = function (ev) { |
|
if (!_this.shouldIgnoreMove) { |
|
var pageX = (window.pageXOffset - _this.prevScrollX) + _this.prevPageX; |
|
var pageY = (window.pageYOffset - _this.prevScrollY) + _this.prevPageY; |
|
_this.emitter.trigger('pointermove', { |
|
origEvent: ev, |
|
isTouch: _this.isTouchDragging, |
|
subjectEl: _this.subjectEl, |
|
pageX: pageX, |
|
pageY: pageY, |
|
deltaX: pageX - _this.origPageX, |
|
deltaY: pageY - _this.origPageY, |
|
}); |
|
} |
|
}; |
|
this.containerEl = containerEl; |
|
this.emitter = new Emitter(); |
|
containerEl.addEventListener('mousedown', this.handleMouseDown); |
|
containerEl.addEventListener('touchstart', this.handleTouchStart, { passive: true }); |
|
listenerCreated(); |
|
} |
|
PointerDragging.prototype.destroy = function () { |
|
this.containerEl.removeEventListener('mousedown', this.handleMouseDown); |
|
this.containerEl.removeEventListener('touchstart', this.handleTouchStart, { passive: true }); |
|
listenerDestroyed(); |
|
}; |
|
PointerDragging.prototype.tryStart = function (ev) { |
|
var subjectEl = this.querySubjectEl(ev); |
|
var downEl = ev.target; |
|
if (subjectEl && |
|
(!this.handleSelector || elementClosest(downEl, this.handleSelector))) { |
|
this.subjectEl = subjectEl; |
|
this.isDragging = true; // do this first so cancelTouchScroll will work |
|
this.wasTouchScroll = false; |
|
return true; |
|
} |
|
return false; |
|
}; |
|
PointerDragging.prototype.cleanup = function () { |
|
isWindowTouchMoveCancelled = false; |
|
this.isDragging = false; |
|
this.subjectEl = null; |
|
// keep wasTouchScroll around for later access |
|
this.destroyScrollWatch(); |
|
}; |
|
PointerDragging.prototype.querySubjectEl = function (ev) { |
|
if (this.selector) { |
|
return elementClosest(ev.target, this.selector); |
|
} |
|
return this.containerEl; |
|
}; |
|
PointerDragging.prototype.shouldIgnoreMouse = function () { |
|
return ignoreMouseDepth || this.isTouchDragging; |
|
}; |
|
// can be called by user of this class, to cancel touch-based scrolling for the current drag |
|
PointerDragging.prototype.cancelTouchScroll = function () { |
|
if (this.isDragging) { |
|
isWindowTouchMoveCancelled = true; |
|
} |
|
}; |
|
// Scrolling that simulates pointermoves |
|
// ---------------------------------------------------------------------------------------------------- |
|
PointerDragging.prototype.initScrollWatch = function (ev) { |
|
if (this.shouldWatchScroll) { |
|
this.recordCoords(ev); |
|
window.addEventListener('scroll', this.handleScroll, true); // useCapture=true |
|
} |
|
}; |
|
PointerDragging.prototype.recordCoords = function (ev) { |
|
if (this.shouldWatchScroll) { |
|
this.prevPageX = ev.pageX; |
|
this.prevPageY = ev.pageY; |
|
this.prevScrollX = window.pageXOffset; |
|
this.prevScrollY = window.pageYOffset; |
|
} |
|
}; |
|
PointerDragging.prototype.destroyScrollWatch = function () { |
|
if (this.shouldWatchScroll) { |
|
window.removeEventListener('scroll', this.handleScroll, true); // useCaptured=true |
|
} |
|
}; |
|
// Event Normalization |
|
// ---------------------------------------------------------------------------------------------------- |
|
PointerDragging.prototype.createEventFromMouse = function (ev, isFirst) { |
|
var deltaX = 0; |
|
var deltaY = 0; |
|
// TODO: repeat code |
|
if (isFirst) { |
|
this.origPageX = ev.pageX; |
|
this.origPageY = ev.pageY; |
|
} |
|
else { |
|
deltaX = ev.pageX - this.origPageX; |
|
deltaY = ev.pageY - this.origPageY; |
|
} |
|
return { |
|
origEvent: ev, |
|
isTouch: false, |
|
subjectEl: this.subjectEl, |
|
pageX: ev.pageX, |
|
pageY: ev.pageY, |
|
deltaX: deltaX, |
|
deltaY: deltaY, |
|
}; |
|
}; |
|
PointerDragging.prototype.createEventFromTouch = function (ev, isFirst) { |
|
var touches = ev.touches; |
|
var pageX; |
|
var pageY; |
|
var deltaX = 0; |
|
var deltaY = 0; |
|
// if touch coords available, prefer, |
|
// because FF would give bad ev.pageX ev.pageY |
|
if (touches && touches.length) { |
|
pageX = touches[0].pageX; |
|
pageY = touches[0].pageY; |
|
} |
|
else { |
|
pageX = ev.pageX; |
|
pageY = ev.pageY; |
|
} |
|
// TODO: repeat code |
|
if (isFirst) { |
|
this.origPageX = pageX; |
|
this.origPageY = pageY; |
|
} |
|
else { |
|
deltaX = pageX - this.origPageX; |
|
deltaY = pageY - this.origPageY; |
|
} |
|
return { |
|
origEvent: ev, |
|
isTouch: true, |
|
subjectEl: this.subjectEl, |
|
pageX: pageX, |
|
pageY: pageY, |
|
deltaX: deltaX, |
|
deltaY: deltaY, |
|
}; |
|
}; |
|
return PointerDragging; |
|
}()); |
|
// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac) |
|
function isPrimaryMouseButton(ev) { |
|
return ev.button === 0 && !ev.ctrlKey; |
|
} |
|
// Ignoring fake mouse events generated by touch |
|
// ---------------------------------------------------------------------------------------------------- |
|
function startIgnoringMouse() { |
|
ignoreMouseDepth += 1; |
|
setTimeout(function () { |
|
ignoreMouseDepth -= 1; |
|
}, config.touchMouseIgnoreWait); |
|
} |
|
// We want to attach touchmove as early as possible for Safari |
|
// ---------------------------------------------------------------------------------------------------- |
|
function listenerCreated() { |
|
listenerCnt += 1; |
|
if (listenerCnt === 1) { |
|
window.addEventListener('touchmove', onWindowTouchMove, { passive: false }); |
|
} |
|
} |
|
function listenerDestroyed() { |
|
listenerCnt -= 1; |
|
if (!listenerCnt) { |
|
window.removeEventListener('touchmove', onWindowTouchMove, { passive: false }); |
|
} |
|
} |
|
function onWindowTouchMove(ev) { |
|
if (isWindowTouchMoveCancelled) { |
|
ev.preventDefault(); |
|
} |
|
} |
|
|
|
/* |
|
An effect in which an element follows the movement of a pointer across the screen. |
|
The moving element is a clone of some other element. |
|
Must call start + handleMove + stop. |
|
*/ |
|
var ElementMirror = /** @class */ (function () { |
|
function ElementMirror() { |
|
this.isVisible = false; // must be explicitly enabled |
|
this.sourceEl = null; |
|
this.mirrorEl = null; |
|
this.sourceElRect = null; // screen coords relative to viewport |
|
// options that can be set directly by caller |
|
this.parentNode = document.body; |
|
this.zIndex = 9999; |
|
this.revertDuration = 0; |
|
} |
|
ElementMirror.prototype.start = function (sourceEl, pageX, pageY) { |
|
this.sourceEl = sourceEl; |
|
this.sourceElRect = this.sourceEl.getBoundingClientRect(); |
|
this.origScreenX = pageX - window.pageXOffset; |
|
this.origScreenY = pageY - window.pageYOffset; |
|
this.deltaX = 0; |
|
this.deltaY = 0; |
|
this.updateElPosition(); |
|
}; |
|
ElementMirror.prototype.handleMove = function (pageX, pageY) { |
|
this.deltaX = (pageX - window.pageXOffset) - this.origScreenX; |
|
this.deltaY = (pageY - window.pageYOffset) - this.origScreenY; |
|
this.updateElPosition(); |
|
}; |
|
// can be called before start |
|
ElementMirror.prototype.setIsVisible = function (bool) { |
|
if (bool) { |
|
if (!this.isVisible) { |
|
if (this.mirrorEl) { |
|
this.mirrorEl.style.display = ''; |
|
} |
|
this.isVisible = bool; // needs to happen before updateElPosition |
|
this.updateElPosition(); // because was not updating the position while invisible |
|
} |
|
} |
|
else if (this.isVisible) { |
|
if (this.mirrorEl) { |
|
this.mirrorEl.style.display = 'none'; |
|
} |
|
this.isVisible = bool; |
|
} |
|
}; |
|
// always async |
|
ElementMirror.prototype.stop = function (needsRevertAnimation, callback) { |
|
var _this = this; |
|
var done = function () { |
|
_this.cleanup(); |
|
callback(); |
|
}; |
|
if (needsRevertAnimation && |
|
this.mirrorEl && |
|
this.isVisible && |
|
this.revertDuration && // if 0, transition won't work |
|
(this.deltaX || this.deltaY) // if same coords, transition won't work |
|
) { |
|
this.doRevertAnimation(done, this.revertDuration); |
|
} |
|
else { |
|
setTimeout(done, 0); |
|
} |
|
}; |
|
ElementMirror.prototype.doRevertAnimation = function (callback, revertDuration) { |
|
var mirrorEl = this.mirrorEl; |
|
var finalSourceElRect = this.sourceEl.getBoundingClientRect(); // because autoscrolling might have happened |
|
mirrorEl.style.transition = |
|
'top ' + revertDuration + 'ms,' + |
|
'left ' + revertDuration + 'ms'; |
|
applyStyle(mirrorEl, { |
|
left: finalSourceElRect.left, |
|
top: finalSourceElRect.top, |
|
}); |
|
whenTransitionDone(mirrorEl, function () { |
|
mirrorEl.style.transition = ''; |
|
callback(); |
|
}); |
|
}; |
|
ElementMirror.prototype.cleanup = function () { |
|
if (this.mirrorEl) { |
|
removeElement(this.mirrorEl); |
|
this.mirrorEl = null; |
|
} |
|
this.sourceEl = null; |
|
}; |
|
ElementMirror.prototype.updateElPosition = function () { |
|
if (this.sourceEl && this.isVisible) { |
|
applyStyle(this.getMirrorEl(), { |
|
left: this.sourceElRect.left + this.deltaX, |
|
top: this.sourceElRect.top + this.deltaY, |
|
}); |
|
} |
|
}; |
|
ElementMirror.prototype.getMirrorEl = function () { |
|
var sourceElRect = this.sourceElRect; |
|
var mirrorEl = this.mirrorEl; |
|
if (!mirrorEl) { |
|
mirrorEl = this.mirrorEl = this.sourceEl.cloneNode(true); // cloneChildren=true |
|
// we don't want long taps or any mouse interaction causing selection/menus. |
|
// would use preventSelection(), but that prevents selectstart, causing problems. |
|
mirrorEl.classList.add('fc-unselectable'); |
|
mirrorEl.classList.add('fc-event-dragging'); |
|
applyStyle(mirrorEl, { |
|
position: 'fixed', |
|
zIndex: this.zIndex, |
|
visibility: '', |
|
boxSizing: 'border-box', |
|
width: sourceElRect.right - sourceElRect.left, |
|
height: sourceElRect.bottom - sourceElRect.top, |
|
right: 'auto', |
|
bottom: 'auto', |
|
margin: 0, |
|
}); |
|
this.parentNode.appendChild(mirrorEl); |
|
} |
|
return mirrorEl; |
|
}; |
|
return ElementMirror; |
|
}()); |
|
|
|
/* |
|
Is a cache for a given element's scroll information (all the info that ScrollController stores) |
|
in addition the "client rectangle" of the element.. the area within the scrollbars. |
|
|
|
The cache can be in one of two modes: |
|
- doesListening:false - ignores when the container is scrolled by someone else |
|
- doesListening:true - watch for scrolling and update the cache |
|
*/ |
|
var ScrollGeomCache = /** @class */ (function (_super) { |
|
__extends(ScrollGeomCache, _super); |
|
function ScrollGeomCache(scrollController, doesListening) { |
|
var _this = _super.call(this) || this; |
|
_this.handleScroll = function () { |
|
_this.scrollTop = _this.scrollController.getScrollTop(); |
|
_this.scrollLeft = _this.scrollController.getScrollLeft(); |
|
_this.handleScrollChange(); |
|
}; |
|
_this.scrollController = scrollController; |
|
_this.doesListening = doesListening; |
|
_this.scrollTop = _this.origScrollTop = scrollController.getScrollTop(); |
|
_this.scrollLeft = _this.origScrollLeft = scrollController.getScrollLeft(); |
|
_this.scrollWidth = scrollController.getScrollWidth(); |
|
_this.scrollHeight = scrollController.getScrollHeight(); |
|
_this.clientWidth = scrollController.getClientWidth(); |
|
_this.clientHeight = scrollController.getClientHeight(); |
|
_this.clientRect = _this.computeClientRect(); // do last in case it needs cached values |
|
if (_this.doesListening) { |
|
_this.getEventTarget().addEventListener('scroll', _this.handleScroll); |
|
} |
|
return _this; |
|
} |
|
ScrollGeomCache.prototype.destroy = function () { |
|
if (this.doesListening) { |
|
this.getEventTarget().removeEventListener('scroll', this.handleScroll); |
|
} |
|
}; |
|
ScrollGeomCache.prototype.getScrollTop = function () { |
|
return this.scrollTop; |
|
}; |
|
ScrollGeomCache.prototype.getScrollLeft = function () { |
|
return this.scrollLeft; |
|
}; |
|
ScrollGeomCache.prototype.setScrollTop = function (top) { |
|
this.scrollController.setScrollTop(top); |
|
if (!this.doesListening) { |
|
// we are not relying on the element to normalize out-of-bounds scroll values |
|
// so we need to sanitize ourselves |
|
this.scrollTop = Math.max(Math.min(top, this.getMaxScrollTop()), 0); |
|
this.handleScrollChange(); |
|
} |
|
}; |
|
ScrollGeomCache.prototype.setScrollLeft = function (top) { |
|
this.scrollController.setScrollLeft(top); |
|
if (!this.doesListening) { |
|
// we are not relying on the element to normalize out-of-bounds scroll values |
|
// so we need to sanitize ourselves |
|
this.scrollLeft = Math.max(Math.min(top, this.getMaxScrollLeft()), 0); |
|
this.handleScrollChange(); |
|
} |
|
}; |
|
ScrollGeomCache.prototype.getClientWidth = function () { |
|
return this.clientWidth; |
|
}; |
|
ScrollGeomCache.prototype.getClientHeight = function () { |
|
return this.clientHeight; |
|
}; |
|
ScrollGeomCache.prototype.getScrollWidth = function () { |
|
return this.scrollWidth; |
|
}; |
|
ScrollGeomCache.prototype.getScrollHeight = function () { |
|
return this.scrollHeight; |
|
}; |
|
ScrollGeomCache.prototype.handleScrollChange = function () { |
|
}; |
|
return ScrollGeomCache; |
|
}(ScrollController)); |
|
|
|
var ElementScrollGeomCache = /** @class */ (function (_super) { |
|
__extends(ElementScrollGeomCache, _super); |
|
function ElementScrollGeomCache(el, doesListening) { |
|
return _super.call(this, new ElementScrollController(el), doesListening) || this; |
|
} |
|
ElementScrollGeomCache.prototype.getEventTarget = function () { |
|
return this.scrollController.el; |
|
}; |
|
ElementScrollGeomCache.prototype.computeClientRect = function () { |
|
return computeInnerRect(this.scrollController.el); |
|
}; |
|
return ElementScrollGeomCache; |
|
}(ScrollGeomCache)); |
|
|
|
var WindowScrollGeomCache = /** @class */ (function (_super) { |
|
__extends(WindowScrollGeomCache, _super); |
|
function WindowScrollGeomCache(doesListening) { |
|
return _super.call(this, new WindowScrollController(), doesListening) || this; |
|
} |
|
WindowScrollGeomCache.prototype.getEventTarget = function () { |
|
return window; |
|
}; |
|
WindowScrollGeomCache.prototype.computeClientRect = function () { |
|
return { |
|
left: this.scrollLeft, |
|
right: this.scrollLeft + this.clientWidth, |
|
top: this.scrollTop, |
|
bottom: this.scrollTop + this.clientHeight, |
|
}; |
|
}; |
|
// the window is the only scroll object that changes it's rectangle relative |
|
// to the document's topleft as it scrolls |
|
WindowScrollGeomCache.prototype.handleScrollChange = function () { |
|
this.clientRect = this.computeClientRect(); |
|
}; |
|
return WindowScrollGeomCache; |
|
}(ScrollGeomCache)); |
|
|
|
// If available we are using native "performance" API instead of "Date" |
|
// Read more about it on MDN: |
|
// https://developer.mozilla.org/en-US/docs/Web/API/Performance |
|
var getTime = typeof performance === 'function' ? performance.now : Date.now; |
|
/* |
|
For a pointer interaction, automatically scrolls certain scroll containers when the pointer |
|
approaches the edge. |
|
|
|
The caller must call start + handleMove + stop. |
|
*/ |
|
var AutoScroller = /** @class */ (function () { |
|
function AutoScroller() { |
|
var _this = this; |
|
// options that can be set by caller |
|
this.isEnabled = true; |
|
this.scrollQuery = [window, '.fc-scroller']; |
|
this.edgeThreshold = 50; // pixels |
|
this.maxVelocity = 300; // pixels per second |
|
// internal state |
|
this.pointerScreenX = null; |
|
this.pointerScreenY = null; |
|
this.isAnimating = false; |
|
this.scrollCaches = null; |
|
// protect against the initial pointerdown being too close to an edge and starting the scroll |
|
this.everMovedUp = false; |
|
this.everMovedDown = false; |
|
this.everMovedLeft = false; |
|
this.everMovedRight = false; |
|
this.animate = function () { |
|
if (_this.isAnimating) { // wasn't cancelled between animation calls |
|
var edge = _this.computeBestEdge(_this.pointerScreenX + window.pageXOffset, _this.pointerScreenY + window.pageYOffset); |
|
if (edge) { |
|
var now = getTime(); |
|
_this.handleSide(edge, (now - _this.msSinceRequest) / 1000); |
|
_this.requestAnimation(now); |
|
} |
|
else { |
|
_this.isAnimating = false; // will stop animation |
|
} |
|
} |
|
}; |
|
} |
|
AutoScroller.prototype.start = function (pageX, pageY) { |
|
if (this.isEnabled) { |
|
this.scrollCaches = this.buildCaches(); |
|
this.pointerScreenX = null; |
|
this.pointerScreenY = null; |
|
this.everMovedUp = false; |
|
this.everMovedDown = false; |
|
this.everMovedLeft = false; |
|
this.everMovedRight = false; |
|
this.handleMove(pageX, pageY); |
|
} |
|
}; |
|
AutoScroller.prototype.handleMove = function (pageX, pageY) { |
|
if (this.isEnabled) { |
|
var pointerScreenX = pageX - window.pageXOffset; |
|
var pointerScreenY = pageY - window.pageYOffset; |
|
var yDelta = this.pointerScreenY === null ? 0 : pointerScreenY - this.pointerScreenY; |
|
var xDelta = this.pointerScreenX === null ? 0 : pointerScreenX - this.pointerScreenX; |
|
if (yDelta < 0) { |
|
this.everMovedUp = true; |
|
} |
|
else if (yDelta > 0) { |
|
this.everMovedDown = true; |
|
} |
|
if (xDelta < 0) { |
|
this.everMovedLeft = true; |
|
} |
|
else if (xDelta > 0) { |
|
this.everMovedRight = true; |
|
} |
|
this.pointerScreenX = pointerScreenX; |
|
this.pointerScreenY = pointerScreenY; |
|
if (!this.isAnimating) { |
|
this.isAnimating = true; |
|
this.requestAnimation(getTime()); |
|
} |
|
} |
|
}; |
|
AutoScroller.prototype.stop = function () { |
|
if (this.isEnabled) { |
|
this.isAnimating = false; // will stop animation |
|
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { |
|
var scrollCache = _a[_i]; |
|
scrollCache.destroy(); |
|
} |
|
this.scrollCaches = null; |
|
} |
|
}; |
|
AutoScroller.prototype.requestAnimation = function (now) { |
|
this.msSinceRequest = now; |
|
requestAnimationFrame(this.animate); |
|
}; |
|
AutoScroller.prototype.handleSide = function (edge, seconds) { |
|
var scrollCache = edge.scrollCache; |
|
var edgeThreshold = this.edgeThreshold; |
|
var invDistance = edgeThreshold - edge.distance; |
|
var velocity = // the closer to the edge, the faster we scroll |
|
((invDistance * invDistance) / (edgeThreshold * edgeThreshold)) * // quadratic |
|
this.maxVelocity * seconds; |
|
var sign = 1; |
|
switch (edge.name) { |
|
case 'left': |
|
sign = -1; |
|
// falls through |
|
case 'right': |
|
scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign); |
|
break; |
|
case 'top': |
|
sign = -1; |
|
// falls through |
|
case 'bottom': |
|
scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign); |
|
break; |
|
} |
|
}; |
|
// left/top are relative to document topleft |
|
AutoScroller.prototype.computeBestEdge = function (left, top) { |
|
var edgeThreshold = this.edgeThreshold; |
|
var bestSide = null; |
|
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { |
|
var scrollCache = _a[_i]; |
|
var rect = scrollCache.clientRect; |
|
var leftDist = left - rect.left; |
|
var rightDist = rect.right - left; |
|
var topDist = top - rect.top; |
|
var bottomDist = rect.bottom - top; |
|
// completely within the rect? |
|
if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) { |
|
if (topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() && |
|
(!bestSide || bestSide.distance > topDist)) { |
|
bestSide = { scrollCache: scrollCache, name: 'top', distance: topDist }; |
|
} |
|
if (bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() && |
|
(!bestSide || bestSide.distance > bottomDist)) { |
|
bestSide = { scrollCache: scrollCache, name: 'bottom', distance: bottomDist }; |
|
} |
|
if (leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() && |
|
(!bestSide || bestSide.distance > leftDist)) { |
|
bestSide = { scrollCache: scrollCache, name: 'left', distance: leftDist }; |
|
} |
|
if (rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() && |
|
(!bestSide || bestSide.distance > rightDist)) { |
|
bestSide = { scrollCache: scrollCache, name: 'right', distance: rightDist }; |
|
} |
|
} |
|
} |
|
return bestSide; |
|
}; |
|
AutoScroller.prototype.buildCaches = function () { |
|
return this.queryScrollEls().map(function (el) { |
|
if (el === window) { |
|
return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls |
|
} |
|
return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls |
|
}); |
|
}; |
|
AutoScroller.prototype.queryScrollEls = function () { |
|
var els = []; |
|
for (var _i = 0, _a = this.scrollQuery; _i < _a.length; _i++) { |
|
var query = _a[_i]; |
|
if (typeof query === 'object') { |
|
els.push(query); |
|
} |
|
else { |
|
els.push.apply(els, Array.prototype.slice.call(document.querySelectorAll(query))); |
|
} |
|
} |
|
return els; |
|
}; |
|
return AutoScroller; |
|
}()); |
|
|
|
/* |
|
Monitors dragging on an element. Has a number of high-level features: |
|
- minimum distance required before dragging |
|
- minimum wait time ("delay") before dragging |
|
- a mirror element that follows the pointer |
|
*/ |
|
var FeaturefulElementDragging = /** @class */ (function (_super) { |
|
__extends(FeaturefulElementDragging, _super); |
|
function FeaturefulElementDragging(containerEl, selector) { |
|
var _this = _super.call(this, containerEl) || this; |
|
// options that can be directly set by caller |
|
// the caller can also set the PointerDragging's options as well |
|
_this.delay = null; |
|
_this.minDistance = 0; |
|
_this.touchScrollAllowed = true; // prevents drag from starting and blocks scrolling during drag |
|
_this.mirrorNeedsRevert = false; |
|
_this.isInteracting = false; // is the user validly moving the pointer? lasts until pointerup |
|
_this.isDragging = false; // is it INTENTFULLY dragging? lasts until after revert animation |
|
_this.isDelayEnded = false; |
|
_this.isDistanceSurpassed = false; |
|
_this.delayTimeoutId = null; |
|
_this.onPointerDown = function (ev) { |
|
if (!_this.isDragging) { // so new drag doesn't happen while revert animation is going |
|
_this.isInteracting = true; |
|
_this.isDelayEnded = false; |
|
_this.isDistanceSurpassed = false; |
|
preventSelection(document.body); |
|
preventContextMenu(document.body); |
|
// prevent links from being visited if there's an eventual drag. |
|
// also prevents selection in older browsers (maybe?). |
|
// not necessary for touch, besides, browser would complain about passiveness. |
|
if (!ev.isTouch) { |
|
ev.origEvent.preventDefault(); |
|
} |
|
_this.emitter.trigger('pointerdown', ev); |
|
if (_this.isInteracting && // not destroyed via pointerdown handler |
|
!_this.pointer.shouldIgnoreMove) { |
|
// actions related to initiating dragstart+dragmove+dragend... |
|
_this.mirror.setIsVisible(false); // reset. caller must set-visible |
|
_this.mirror.start(ev.subjectEl, ev.pageX, ev.pageY); // must happen on first pointer down |
|
_this.startDelay(ev); |
|
if (!_this.minDistance) { |
|
_this.handleDistanceSurpassed(ev); |
|
} |
|
} |
|
} |
|
}; |
|
_this.onPointerMove = function (ev) { |
|
if (_this.isInteracting) { |
|
_this.emitter.trigger('pointermove', ev); |
|
if (!_this.isDistanceSurpassed) { |
|
var minDistance = _this.minDistance; |
|
var distanceSq = void 0; // current distance from the origin, squared |
|
var deltaX = ev.deltaX, deltaY = ev.deltaY; |
|
distanceSq = deltaX * deltaX + deltaY * deltaY; |
|
if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem |
|
_this.handleDistanceSurpassed(ev); |
|
} |
|
} |
|
if (_this.isDragging) { |
|
// a real pointer move? (not one simulated by scrolling) |
|
if (ev.origEvent.type !== 'scroll') { |
|
_this.mirror.handleMove(ev.pageX, ev.pageY); |
|
_this.autoScroller.handleMove(ev.pageX, ev.pageY); |
|
} |
|
_this.emitter.trigger('dragmove', ev); |
|
} |
|
} |
|
}; |
|
_this.onPointerUp = function (ev) { |
|
if (_this.isInteracting) { |
|
_this.isInteracting = false; |
|
allowSelection(document.body); |
|
allowContextMenu(document.body); |
|
_this.emitter.trigger('pointerup', ev); // can potentially set mirrorNeedsRevert |
|
if (_this.isDragging) { |
|
_this.autoScroller.stop(); |
|
_this.tryStopDrag(ev); // which will stop the mirror |
|
} |
|
if (_this.delayTimeoutId) { |
|
clearTimeout(_this.delayTimeoutId); |
|
_this.delayTimeoutId = null; |
|
} |
|
} |
|
}; |
|
var pointer = _this.pointer = new PointerDragging(containerEl); |
|
pointer.emitter.on('pointerdown', _this.onPointerDown); |
|
pointer.emitter.on('pointermove', _this.onPointerMove); |
|
pointer.emitter.on('pointerup', _this.onPointerUp); |
|
if (selector) { |
|
pointer.selector = selector; |
|
} |
|
_this.mirror = new ElementMirror(); |
|
_this.autoScroller = new AutoScroller(); |
|
return _this; |
|
} |
|
FeaturefulElementDragging.prototype.destroy = function () { |
|
this.pointer.destroy(); |
|
// HACK: simulate a pointer-up to end the current drag |
|
// TODO: fire 'dragend' directly and stop interaction. discourage use of pointerup event (b/c might not fire) |
|
this.onPointerUp({}); |
|
}; |
|
FeaturefulElementDragging.prototype.startDelay = function (ev) { |
|
var _this = this; |
|
if (typeof this.delay === 'number') { |
|
this.delayTimeoutId = setTimeout(function () { |
|
_this.delayTimeoutId = null; |
|
_this.handleDelayEnd(ev); |
|
}, this.delay); // not assignable to number! |
|
} |
|
else { |
|
this.handleDelayEnd(ev); |
|
} |
|
}; |
|
FeaturefulElementDragging.prototype.handleDelayEnd = function (ev) { |
|
this.isDelayEnded = true; |
|
this.tryStartDrag(ev); |
|
}; |
|
FeaturefulElementDragging.prototype.handleDistanceSurpassed = function (ev) { |
|
this.isDistanceSurpassed = true; |
|
this.tryStartDrag(ev); |
|
}; |
|
FeaturefulElementDragging.prototype.tryStartDrag = function (ev) { |
|
if (this.isDelayEnded && this.isDistanceSurpassed) { |
|
if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) { |
|
this.isDragging = true; |
|
this.mirrorNeedsRevert = false; |
|
this.autoScroller.start(ev.pageX, ev.pageY); |
|
this.emitter.trigger('dragstart', ev); |
|
if (this.touchScrollAllowed === false) { |
|
this.pointer.cancelTouchScroll(); |
|
} |
|
} |
|
} |
|
}; |
|
FeaturefulElementDragging.prototype.tryStopDrag = function (ev) { |
|
// .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events |
|
// that come from the document to fire beforehand. much more convenient this way. |
|
this.mirror.stop(this.mirrorNeedsRevert, this.stopDrag.bind(this, ev)); |
|
}; |
|
FeaturefulElementDragging.prototype.stopDrag = function (ev) { |
|
this.isDragging = false; |
|
this.emitter.trigger('dragend', ev); |
|
}; |
|
// fill in the implementations... |
|
FeaturefulElementDragging.prototype.setIgnoreMove = function (bool) { |
|
this.pointer.shouldIgnoreMove = bool; |
|
}; |
|
FeaturefulElementDragging.prototype.setMirrorIsVisible = function (bool) { |
|
this.mirror.setIsVisible(bool); |
|
}; |
|
FeaturefulElementDragging.prototype.setMirrorNeedsRevert = function (bool) { |
|
this.mirrorNeedsRevert = bool; |
|
}; |
|
FeaturefulElementDragging.prototype.setAutoScrollEnabled = function (bool) { |
|
this.autoScroller.isEnabled = bool; |
|
}; |
|
return FeaturefulElementDragging; |
|
}(ElementDragging)); |
|
|
|
/* |
|
When this class is instantiated, it records the offset of an element (relative to the document topleft), |
|
and continues to monitor scrolling, updating the cached coordinates if it needs to. |
|
Does not access the DOM after instantiation, so highly performant. |
|
|
|
Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element |
|
and an determine if a given point is inside the combined clipping rectangle. |
|
*/ |
|
var OffsetTracker = /** @class */ (function () { |
|
function OffsetTracker(el) { |
|
this.origRect = computeRect(el); |
|
// will work fine for divs that have overflow:hidden |
|
this.scrollCaches = getClippingParents(el).map(function (scrollEl) { return new ElementScrollGeomCache(scrollEl, true); }); |
|
} |
|
OffsetTracker.prototype.destroy = function () { |
|
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { |
|
var scrollCache = _a[_i]; |
|
scrollCache.destroy(); |
|
} |
|
}; |
|
OffsetTracker.prototype.computeLeft = function () { |
|
var left = this.origRect.left; |
|
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { |
|
var scrollCache = _a[_i]; |
|
left += scrollCache.origScrollLeft - scrollCache.getScrollLeft(); |
|
} |
|
return left; |
|
}; |
|
OffsetTracker.prototype.computeTop = function () { |
|
var top = this.origRect.top; |
|
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { |
|
var scrollCache = _a[_i]; |
|
top += scrollCache.origScrollTop - scrollCache.getScrollTop(); |
|
} |
|
return top; |
|
}; |
|
OffsetTracker.prototype.isWithinClipping = function (pageX, pageY) { |
|
var point = { left: pageX, top: pageY }; |
|
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) { |
|
var scrollCache = _a[_i]; |
|
if (!isIgnoredClipping(scrollCache.getEventTarget()) && |
|
!pointInsideRect(point, scrollCache.clientRect)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
}; |
|
return OffsetTracker; |
|
}()); |
|
// certain clipping containers should never constrain interactions, like <html> and <body> |
|
// https://github.com/fullcalendar/fullcalendar/issues/3615 |
|
function isIgnoredClipping(node) { |
|
var tagName = node.tagName; |
|
return tagName === 'HTML' || tagName === 'BODY'; |
|
} |
|
|
|
/* |
|
Tracks movement over multiple droppable areas (aka "hits") |
|
that exist in one or more DateComponents. |
|
Relies on an existing draggable. |
|
|
|
emits: |
|
- pointerdown |
|
- dragstart |
|
- hitchange - fires initially, even if not over a hit |
|
- pointerup |
|
- (hitchange - again, to null, if ended over a hit) |
|
- dragend |
|
*/ |
|
var HitDragging = /** @class */ (function () { |
|
function HitDragging(dragging, droppableStore) { |
|
var _this = this; |
|
// options that can be set by caller |
|
this.useSubjectCenter = false; |
|
this.requireInitial = true; // if doesn't start out on a hit, won't emit any events |
|
this.initialHit = null; |
|
this.movingHit = null; |
|
this.finalHit = null; // won't ever be populated if shouldIgnoreMove |
|
this.handlePointerDown = function (ev) { |
|
var dragging = _this.dragging; |
|
_this.initialHit = null; |
|
_this.movingHit = null; |
|
_this.finalHit = null; |
|
_this.prepareHits(); |
|
_this.processFirstCoord(ev); |
|
if (_this.initialHit || !_this.requireInitial) { |
|
dragging.setIgnoreMove(false); |
|
// TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :( |
|
_this.emitter.trigger('pointerdown', ev); |
|
} |
|
else { |
|
dragging.setIgnoreMove(true); |
|
} |
|
}; |
|
this.handleDragStart = function (ev) { |
|
_this.emitter.trigger('dragstart', ev); |
|
_this.handleMove(ev, true); // force = fire even if initially null |
|
}; |
|
this.handleDragMove = function (ev) { |
|
_this.emitter.trigger('dragmove', ev); |
|
_this.handleMove(ev); |
|
}; |
|
this.handlePointerUp = function (ev) { |
|
_this.releaseHits(); |
|
_this.emitter.trigger('pointerup', ev); |
|
}; |
|
this.handleDragEnd = function (ev) { |
|
if (_this.movingHit) { |
|
_this.emitter.trigger('hitupdate', null, true, ev); |
|
} |
|
_this.finalHit = _this.movingHit; |
|
_this.movingHit = null; |
|
_this.emitter.trigger('dragend', ev); |
|
}; |
|
this.droppableStore = droppableStore; |
|
dragging.emitter.on('pointerdown', this.handlePointerDown); |
|
dragging.emitter.on('dragstart', this.handleDragStart); |
|
dragging.emitter.on('dragmove', this.handleDragMove); |
|
dragging.emitter.on('pointerup', this.handlePointerUp); |
|
dragging.emitter.on('dragend', this.handleDragEnd); |
|
this.dragging = dragging; |
|
this.emitter = new Emitter(); |
|
} |
|
// sets initialHit |
|
// sets coordAdjust |
|
HitDragging.prototype.processFirstCoord = function (ev) { |
|
var origPoint = { left: ev.pageX, top: ev.pageY }; |
|
var adjustedPoint = origPoint; |
|
var subjectEl = ev.subjectEl; |
|
var subjectRect; |
|
if (subjectEl !== document) { |
|
subjectRect = computeRect(subjectEl); |
|
adjustedPoint = constrainPoint(adjustedPoint, subjectRect); |
|
} |
|
var initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top); |
|
if (initialHit) { |
|
if (this.useSubjectCenter && subjectRect) { |
|
var slicedSubjectRect = intersectRects(subjectRect, initialHit.rect); |
|
if (slicedSubjectRect) { |
|
adjustedPoint = getRectCenter(slicedSubjectRect); |
|
} |
|
} |
|
this.coordAdjust = diffPoints(adjustedPoint, origPoint); |
|
} |
|
else { |
|
this.coordAdjust = { left: 0, top: 0 }; |
|
} |
|
}; |
|
HitDragging.prototype.handleMove = function (ev, forceHandle) { |
|
var hit = this.queryHitForOffset(ev.pageX + this.coordAdjust.left, ev.pageY + this.coordAdjust.top); |
|
if (forceHandle || !isHitsEqual(this.movingHit, hit)) { |
|
this.movingHit = hit; |
|
this.emitter.trigger('hitupdate', hit, false, ev); |
|
} |
|
}; |
|
HitDragging.prototype.prepareHits = function () { |
|
this.offsetTrackers = mapHash(this.droppableStore, function (interactionSettings) { |
|
interactionSettings.component.prepareHits(); |
|
return new OffsetTracker(interactionSettings.el); |
|
}); |
|
}; |
|
HitDragging.prototype.releaseHits = function () { |
|
var offsetTrackers = this.offsetTrackers; |
|
for (var id in offsetTrackers) { |
|
offsetTrackers[id].destroy(); |
|
} |
|
this.offsetTrackers = {}; |
|
}; |
|
HitDragging.prototype.queryHitForOffset = function (offsetLeft, offsetTop) { |
|
var _a = this, droppableStore = _a.droppableStore, offsetTrackers = _a.offsetTrackers; |
|
var bestHit = null; |
|
for (var id in droppableStore) { |
|
var component = droppableStore[id].component; |
|
var offsetTracker = offsetTrackers[id]; |
|
if (offsetTracker && // wasn't destroyed mid-drag |
|
offsetTracker.isWithinClipping(offsetLeft, offsetTop)) { |
|
var originLeft = offsetTracker.computeLeft(); |
|
var originTop = offsetTracker.computeTop(); |
|
var positionLeft = offsetLeft - originLeft; |
|
var positionTop = offsetTop - originTop; |
|
var origRect = offsetTracker.origRect; |
|
var width = origRect.right - origRect.left; |
|
var height = origRect.bottom - origRect.top; |
|
if ( |
|
// must be within the element's bounds |
|
positionLeft >= 0 && positionLeft < width && |
|
positionTop >= 0 && positionTop < height) { |
|
var hit = component.queryHit(positionLeft, positionTop, width, height); |
|
var dateProfile = component.context.getCurrentData().dateProfile; |
|
if (hit && |
|
( |
|
// make sure the hit is within activeRange, meaning it's not a deal cell |
|
rangeContainsRange(dateProfile.activeRange, hit.dateSpan.range)) && |
|
(!bestHit || hit.layer > bestHit.layer)) { |
|
// TODO: better way to re-orient rectangle |
|
hit.rect.left += originLeft; |
|
hit.rect.right += originLeft; |
|
hit.rect.top += originTop; |
|
hit.rect.bottom += originTop; |
|
bestHit = hit; |
|
} |
|
} |
|
} |
|
} |
|
return bestHit; |
|
}; |
|
return HitDragging; |
|
}()); |
|
function isHitsEqual(hit0, hit1) { |
|
if (!hit0 && !hit1) { |
|
return true; |
|
} |
|
if (Boolean(hit0) !== Boolean(hit1)) { |
|
return false; |
|
} |
|
return isDateSpansEqual(hit0.dateSpan, hit1.dateSpan); |
|
} |
|
|
|
function buildDatePointApiWithContext(dateSpan, context) { |
|
var props = {}; |
|
for (var _i = 0, _a = context.pluginHooks.datePointTransforms; _i < _a.length; _i++) { |
|
var transform = _a[_i]; |
|
__assign(props, transform(dateSpan, context)); |
|
} |
|
__assign(props, buildDatePointApi(dateSpan, context.dateEnv)); |
|
return props; |
|
} |
|
function buildDatePointApi(span, dateEnv) { |
|
return { |
|
date: dateEnv.toDate(span.range.start), |
|
dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }), |
|
allDay: span.allDay, |
|
}; |
|
} |
|
|
|
/* |
|
Monitors when the user clicks on a specific date/time of a component. |
|
A pointerdown+pointerup on the same "hit" constitutes a click. |
|
*/ |
|
var DateClicking = /** @class */ (function (_super) { |
|
__extends(DateClicking, _super); |
|
function DateClicking(settings) { |
|
var _this = _super.call(this, settings) || this; |
|
_this.handlePointerDown = function (pev) { |
|
var dragging = _this.dragging; |
|
var downEl = pev.origEvent.target; |
|
// do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired |
|
dragging.setIgnoreMove(!_this.component.isValidDateDownEl(downEl)); |
|
}; |
|
// won't even fire if moving was ignored |
|
_this.handleDragEnd = function (ev) { |
|
var component = _this.component; |
|
var pointer = _this.dragging.pointer; |
|
if (!pointer.wasTouchScroll) { |
|
var _a = _this.hitDragging, initialHit = _a.initialHit, finalHit = _a.finalHit; |
|
if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) { |
|
var context = component.context; |
|
var arg = __assign(__assign({}, buildDatePointApiWithContext(initialHit.dateSpan, context)), { dayEl: initialHit.dayEl, jsEvent: ev.origEvent, view: context.viewApi || context.calendarApi.view }); |
|
context.emitter.trigger('dateClick', arg); |
|
} |
|
} |
|
}; |
|
// we DO want to watch pointer moves because otherwise finalHit won't get populated |
|
_this.dragging = new FeaturefulElementDragging(settings.el); |
|
_this.dragging.autoScroller.isEnabled = false; |
|
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings)); |
|
hitDragging.emitter.on('pointerdown', _this.handlePointerDown); |
|
hitDragging.emitter.on('dragend', _this.handleDragEnd); |
|
return _this; |
|
} |
|
DateClicking.prototype.destroy = function () { |
|
this.dragging.destroy(); |
|
}; |
|
return DateClicking; |
|
}(Interaction)); |
|
|
|
/* |
|
Tracks when the user selects a portion of time of a component, |
|
constituted by a drag over date cells, with a possible delay at the beginning of the drag. |
|
*/ |
|
var DateSelecting = /** @class */ (function (_super) { |
|
__extends(DateSelecting, _super); |
|
function DateSelecting(settings) { |
|
var _this = _super.call(this, settings) || this; |
|
_this.dragSelection = null; |
|
_this.handlePointerDown = function (ev) { |
|
var _a = _this, component = _a.component, dragging = _a.dragging; |
|
var options = component.context.options; |
|
var canSelect = options.selectable && |
|
component.isValidDateDownEl(ev.origEvent.target); |
|
// don't bother to watch expensive moves if component won't do selection |
|
dragging.setIgnoreMove(!canSelect); |
|
// if touch, require user to hold down |
|
dragging.delay = ev.isTouch ? getComponentTouchDelay(component) : null; |
|
}; |
|
_this.handleDragStart = function (ev) { |
|
_this.component.context.calendarApi.unselect(ev); // unselect previous selections |
|
}; |
|
_this.handleHitUpdate = function (hit, isFinal) { |
|
var context = _this.component.context; |
|
var dragSelection = null; |
|
var isInvalid = false; |
|
if (hit) { |
|
dragSelection = joinHitsIntoSelection(_this.hitDragging.initialHit, hit, context.pluginHooks.dateSelectionTransformers); |
|
if (!dragSelection || !_this.component.isDateSelectionValid(dragSelection)) { |
|
isInvalid = true; |
|
dragSelection = null; |
|
} |
|
} |
|
if (dragSelection) { |
|
context.dispatch({ type: 'SELECT_DATES', selection: dragSelection }); |
|
} |
|
else if (!isFinal) { // only unselect if moved away while dragging |
|
context.dispatch({ type: 'UNSELECT_DATES' }); |
|
} |
|
if (!isInvalid) { |
|
enableCursor(); |
|
} |
|
else { |
|
disableCursor(); |
|
} |
|
if (!isFinal) { |
|
_this.dragSelection = dragSelection; // only clear if moved away from all hits while dragging |
|
} |
|
}; |
|
_this.handlePointerUp = function (pev) { |
|
if (_this.dragSelection) { |
|
// selection is already rendered, so just need to report selection |
|
triggerDateSelect(_this.dragSelection, pev, _this.component.context); |
|
_this.dragSelection = null; |
|
} |
|
}; |
|
var component = settings.component; |
|
var options = component.context.options; |
|
var dragging = _this.dragging = new FeaturefulElementDragging(settings.el); |
|
dragging.touchScrollAllowed = false; |
|
dragging.minDistance = options.selectMinDistance || 0; |
|
dragging.autoScroller.isEnabled = options.dragScroll; |
|
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings)); |
|
hitDragging.emitter.on('pointerdown', _this.handlePointerDown); |
|
hitDragging.emitter.on('dragstart', _this.handleDragStart); |
|
hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); |
|
hitDragging.emitter.on('pointerup', _this.handlePointerUp); |
|
return _this; |
|
} |
|
DateSelecting.prototype.destroy = function () { |
|
this.dragging.destroy(); |
|
}; |
|
return DateSelecting; |
|
}(Interaction)); |
|
function getComponentTouchDelay(component) { |
|
var options = component.context.options; |
|
var delay = options.selectLongPressDelay; |
|
if (delay == null) { |
|
delay = options.longPressDelay; |
|
} |
|
return delay; |
|
} |
|
function joinHitsIntoSelection(hit0, hit1, dateSelectionTransformers) { |
|
var dateSpan0 = hit0.dateSpan; |
|
var dateSpan1 = hit1.dateSpan; |
|
var ms = [ |
|
dateSpan0.range.start, |
|
dateSpan0.range.end, |
|
dateSpan1.range.start, |
|
dateSpan1.range.end, |
|
]; |
|
ms.sort(compareNumbers); |
|
var props = {}; |
|
for (var _i = 0, dateSelectionTransformers_1 = dateSelectionTransformers; _i < dateSelectionTransformers_1.length; _i++) { |
|
var transformer = dateSelectionTransformers_1[_i]; |
|
var res = transformer(hit0, hit1); |
|
if (res === false) { |
|
return null; |
|
} |
|
if (res) { |
|
__assign(props, res); |
|
} |
|
} |
|
props.range = { start: ms[0], end: ms[3] }; |
|
props.allDay = dateSpan0.allDay; |
|
return props; |
|
} |
|
|
|
var EventDragging = /** @class */ (function (_super) { |
|
__extends(EventDragging, _super); |
|
function EventDragging(settings) { |
|
var _this = _super.call(this, settings) || this; |
|
// internal state |
|
_this.subjectEl = null; |
|
_this.subjectSeg = null; // the seg being selected/dragged |
|
_this.isDragging = false; |
|
_this.eventRange = null; |
|
_this.relevantEvents = null; // the events being dragged |
|
_this.receivingContext = null; |
|
_this.validMutation = null; |
|
_this.mutatedRelevantEvents = null; |
|
_this.handlePointerDown = function (ev) { |
|
var origTarget = ev.origEvent.target; |
|
var _a = _this, component = _a.component, dragging = _a.dragging; |
|
var mirror = dragging.mirror; |
|
var options = component.context.options; |
|
var initialContext = component.context; |
|
_this.subjectEl = ev.subjectEl; |
|
var subjectSeg = _this.subjectSeg = getElSeg(ev.subjectEl); |
|
var eventRange = _this.eventRange = subjectSeg.eventRange; |
|
var eventInstanceId = eventRange.instance.instanceId; |
|
_this.relevantEvents = getRelevantEvents(initialContext.getCurrentData().eventStore, eventInstanceId); |
|
dragging.minDistance = ev.isTouch ? 0 : options.eventDragMinDistance; |
|
dragging.delay = |
|
// only do a touch delay if touch and this event hasn't been selected yet |
|
(ev.isTouch && eventInstanceId !== component.props.eventSelection) ? |
|
getComponentTouchDelay$1(component) : |
|
null; |
|
if (options.fixedMirrorParent) { |
|
mirror.parentNode = options.fixedMirrorParent; |
|
} |
|
else { |
|
mirror.parentNode = elementClosest(origTarget, '.fc'); |
|
} |
|
mirror.revertDuration = options.dragRevertDuration; |
|
var isValid = component.isValidSegDownEl(origTarget) && |
|
!elementClosest(origTarget, '.fc-event-resizer'); // NOT on a resizer |
|
dragging.setIgnoreMove(!isValid); |
|
// disable dragging for elements that are resizable (ie, selectable) |
|
// but are not draggable |
|
_this.isDragging = isValid && |
|
ev.subjectEl.classList.contains('fc-event-draggable'); |
|
}; |
|
_this.handleDragStart = function (ev) { |
|
var initialContext = _this.component.context; |
|
var eventRange = _this.eventRange; |
|
var eventInstanceId = eventRange.instance.instanceId; |
|
if (ev.isTouch) { |
|
// need to select a different event? |
|
if (eventInstanceId !== _this.component.props.eventSelection) { |
|
initialContext.dispatch({ type: 'SELECT_EVENT', eventInstanceId: eventInstanceId }); |
|
} |
|
} |
|
else { |
|
// if now using mouse, but was previous touch interaction, clear selected event |
|
initialContext.dispatch({ type: 'UNSELECT_EVENT' }); |
|
} |
|
if (_this.isDragging) { |
|
initialContext.calendarApi.unselect(ev); // unselect *date* selection |
|
initialContext.emitter.trigger('eventDragStart', { |
|
el: _this.subjectEl, |
|
event: new EventApi(initialContext, eventRange.def, eventRange.instance), |
|
jsEvent: ev.origEvent, |
|
view: initialContext.viewApi, |
|
}); |
|
} |
|
}; |
|
_this.handleHitUpdate = function (hit, isFinal) { |
|
if (!_this.isDragging) { |
|
return; |
|
} |
|
var relevantEvents = _this.relevantEvents; |
|
var initialHit = _this.hitDragging.initialHit; |
|
var initialContext = _this.component.context; |
|
// states based on new hit |
|
var receivingContext = null; |
|
var mutation = null; |
|
var mutatedRelevantEvents = null; |
|
var isInvalid = false; |
|
var interaction = { |
|
affectedEvents: relevantEvents, |
|
mutatedEvents: createEmptyEventStore(), |
|
isEvent: true, |
|
}; |
|
if (hit) { |
|
var receivingComponent = hit.component; |
|
receivingContext = receivingComponent.context; |
|
var receivingOptions = receivingContext.options; |
|
if (initialContext === receivingContext || |
|
(receivingOptions.editable && receivingOptions.droppable)) { |
|
mutation = computeEventMutation(initialHit, hit, receivingContext.getCurrentData().pluginHooks.eventDragMutationMassagers); |
|
if (mutation) { |
|
mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, receivingContext.getCurrentData().eventUiBases, mutation, receivingContext); |
|
interaction.mutatedEvents = mutatedRelevantEvents; |
|
if (!receivingComponent.isInteractionValid(interaction)) { |
|
isInvalid = true; |
|
mutation = null; |
|
mutatedRelevantEvents = null; |
|
interaction.mutatedEvents = createEmptyEventStore(); |
|
} |
|
} |
|
} |
|
else { |
|
receivingContext = null; |
|
} |
|
} |
|
_this.displayDrag(receivingContext, interaction); |
|
if (!isInvalid) { |
|
enableCursor(); |
|
} |
|
else { |
|
disableCursor(); |
|
} |
|
if (!isFinal) { |
|
if (initialContext === receivingContext && // TODO: write test for this |
|
isHitsEqual(initialHit, hit)) { |
|
mutation = null; |
|
} |
|
_this.dragging.setMirrorNeedsRevert(!mutation); |
|
// render the mirror if no already-rendered mirror |
|
// TODO: wish we could somehow wait for dispatch to guarantee render |
|
_this.dragging.setMirrorIsVisible(!hit || !document.querySelector('.fc-event-mirror')); |
|
// assign states based on new hit |
|
_this.receivingContext = receivingContext; |
|
_this.validMutation = mutation; |
|
_this.mutatedRelevantEvents = mutatedRelevantEvents; |
|
} |
|
}; |
|
_this.handlePointerUp = function () { |
|
if (!_this.isDragging) { |
|
_this.cleanup(); // because handleDragEnd won't fire |
|
} |
|
}; |
|
_this.handleDragEnd = function (ev) { |
|
if (_this.isDragging) { |
|
var initialContext_1 = _this.component.context; |
|
var initialView = initialContext_1.viewApi; |
|
var _a = _this, receivingContext_1 = _a.receivingContext, validMutation = _a.validMutation; |
|
var eventDef = _this.eventRange.def; |
|
var eventInstance = _this.eventRange.instance; |
|
var eventApi = new EventApi(initialContext_1, eventDef, eventInstance); |
|
var relevantEvents_1 = _this.relevantEvents; |
|
var mutatedRelevantEvents_1 = _this.mutatedRelevantEvents; |
|
var finalHit = _this.hitDragging.finalHit; |
|
_this.clearDrag(); // must happen after revert animation |
|
initialContext_1.emitter.trigger('eventDragStop', { |
|
el: _this.subjectEl, |
|
event: eventApi, |
|
jsEvent: ev.origEvent, |
|
view: initialView, |
|
}); |
|
if (validMutation) { |
|
// dropped within same calendar |
|
if (receivingContext_1 === initialContext_1) { |
|
var updatedEventApi = new EventApi(initialContext_1, mutatedRelevantEvents_1.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents_1.instances[eventInstance.instanceId] : null); |
|
initialContext_1.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: mutatedRelevantEvents_1, |
|
}); |
|
var eventChangeArg = { |
|
oldEvent: eventApi, |
|
event: updatedEventApi, |
|
relatedEvents: buildEventApis(mutatedRelevantEvents_1, initialContext_1, eventInstance), |
|
revert: function () { |
|
initialContext_1.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: relevantEvents_1, |
|
}); |
|
}, |
|
}; |
|
var transformed = {}; |
|
for (var _i = 0, _b = initialContext_1.getCurrentData().pluginHooks.eventDropTransformers; _i < _b.length; _i++) { |
|
var transformer = _b[_i]; |
|
__assign(transformed, transformer(validMutation, initialContext_1)); |
|
} |
|
initialContext_1.emitter.trigger('eventDrop', __assign(__assign(__assign({}, eventChangeArg), transformed), { el: ev.subjectEl, delta: validMutation.datesDelta, jsEvent: ev.origEvent, view: initialView })); |
|
initialContext_1.emitter.trigger('eventChange', eventChangeArg); |
|
// dropped in different calendar |
|
} |
|
else if (receivingContext_1) { |
|
var eventRemoveArg = { |
|
event: eventApi, |
|
relatedEvents: buildEventApis(relevantEvents_1, initialContext_1, eventInstance), |
|
revert: function () { |
|
initialContext_1.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: relevantEvents_1, |
|
}); |
|
}, |
|
}; |
|
initialContext_1.emitter.trigger('eventLeave', __assign(__assign({}, eventRemoveArg), { draggedEl: ev.subjectEl, view: initialView })); |
|
initialContext_1.dispatch({ |
|
type: 'REMOVE_EVENTS', |
|
eventStore: relevantEvents_1, |
|
}); |
|
initialContext_1.emitter.trigger('eventRemove', eventRemoveArg); |
|
var addedEventDef = mutatedRelevantEvents_1.defs[eventDef.defId]; |
|
var addedEventInstance = mutatedRelevantEvents_1.instances[eventInstance.instanceId]; |
|
var addedEventApi = new EventApi(receivingContext_1, addedEventDef, addedEventInstance); |
|
receivingContext_1.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: mutatedRelevantEvents_1, |
|
}); |
|
var eventAddArg = { |
|
event: addedEventApi, |
|
relatedEvents: buildEventApis(mutatedRelevantEvents_1, receivingContext_1, addedEventInstance), |
|
revert: function () { |
|
receivingContext_1.dispatch({ |
|
type: 'REMOVE_EVENTS', |
|
eventStore: mutatedRelevantEvents_1, |
|
}); |
|
}, |
|
}; |
|
receivingContext_1.emitter.trigger('eventAdd', eventAddArg); |
|
if (ev.isTouch) { |
|
receivingContext_1.dispatch({ |
|
type: 'SELECT_EVENT', |
|
eventInstanceId: eventInstance.instanceId, |
|
}); |
|
} |
|
receivingContext_1.emitter.trigger('drop', __assign(__assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext_1)), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.component.context.viewApi })); |
|
receivingContext_1.emitter.trigger('eventReceive', __assign(__assign({}, eventAddArg), { draggedEl: ev.subjectEl, view: finalHit.component.context.viewApi })); |
|
} |
|
} |
|
else { |
|
initialContext_1.emitter.trigger('_noEventDrop'); |
|
} |
|
} |
|
_this.cleanup(); |
|
}; |
|
var component = _this.component; |
|
var options = component.context.options; |
|
var dragging = _this.dragging = new FeaturefulElementDragging(settings.el); |
|
dragging.pointer.selector = EventDragging.SELECTOR; |
|
dragging.touchScrollAllowed = false; |
|
dragging.autoScroller.isEnabled = options.dragScroll; |
|
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsStore); |
|
hitDragging.useSubjectCenter = settings.useEventCenter; |
|
hitDragging.emitter.on('pointerdown', _this.handlePointerDown); |
|
hitDragging.emitter.on('dragstart', _this.handleDragStart); |
|
hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); |
|
hitDragging.emitter.on('pointerup', _this.handlePointerUp); |
|
hitDragging.emitter.on('dragend', _this.handleDragEnd); |
|
return _this; |
|
} |
|
EventDragging.prototype.destroy = function () { |
|
this.dragging.destroy(); |
|
}; |
|
// render a drag state on the next receivingCalendar |
|
EventDragging.prototype.displayDrag = function (nextContext, state) { |
|
var initialContext = this.component.context; |
|
var prevContext = this.receivingContext; |
|
// does the previous calendar need to be cleared? |
|
if (prevContext && prevContext !== nextContext) { |
|
// does the initial calendar need to be cleared? |
|
// if so, don't clear all the way. we still need to to hide the affectedEvents |
|
if (prevContext === initialContext) { |
|
prevContext.dispatch({ |
|
type: 'SET_EVENT_DRAG', |
|
state: { |
|
affectedEvents: state.affectedEvents, |
|
mutatedEvents: createEmptyEventStore(), |
|
isEvent: true, |
|
}, |
|
}); |
|
// completely clear the old calendar if it wasn't the initial |
|
} |
|
else { |
|
prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); |
|
} |
|
} |
|
if (nextContext) { |
|
nextContext.dispatch({ type: 'SET_EVENT_DRAG', state: state }); |
|
} |
|
}; |
|
EventDragging.prototype.clearDrag = function () { |
|
var initialCalendar = this.component.context; |
|
var receivingContext = this.receivingContext; |
|
if (receivingContext) { |
|
receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); |
|
} |
|
// the initial calendar might have an dummy drag state from displayDrag |
|
if (initialCalendar !== receivingContext) { |
|
initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' }); |
|
} |
|
}; |
|
EventDragging.prototype.cleanup = function () { |
|
this.subjectSeg = null; |
|
this.isDragging = false; |
|
this.eventRange = null; |
|
this.relevantEvents = null; |
|
this.receivingContext = null; |
|
this.validMutation = null; |
|
this.mutatedRelevantEvents = null; |
|
}; |
|
// TODO: test this in IE11 |
|
// QUESTION: why do we need it on the resizable??? |
|
EventDragging.SELECTOR = '.fc-event-draggable, .fc-event-resizable'; |
|
return EventDragging; |
|
}(Interaction)); |
|
function computeEventMutation(hit0, hit1, massagers) { |
|
var dateSpan0 = hit0.dateSpan; |
|
var dateSpan1 = hit1.dateSpan; |
|
var date0 = dateSpan0.range.start; |
|
var date1 = dateSpan1.range.start; |
|
var standardProps = {}; |
|
if (dateSpan0.allDay !== dateSpan1.allDay) { |
|
standardProps.allDay = dateSpan1.allDay; |
|
standardProps.hasEnd = hit1.component.context.options.allDayMaintainDuration; |
|
if (dateSpan1.allDay) { |
|
// means date1 is already start-of-day, |
|
// but date0 needs to be converted |
|
date0 = startOfDay(date0); |
|
} |
|
} |
|
var delta = diffDates(date0, date1, hit0.component.context.dateEnv, hit0.component === hit1.component ? |
|
hit0.component.largeUnit : |
|
null); |
|
if (delta.milliseconds) { // has hours/minutes/seconds |
|
standardProps.allDay = false; |
|
} |
|
var mutation = { |
|
datesDelta: delta, |
|
standardProps: standardProps, |
|
}; |
|
for (var _i = 0, massagers_1 = massagers; _i < massagers_1.length; _i++) { |
|
var massager = massagers_1[_i]; |
|
massager(mutation, hit0, hit1); |
|
} |
|
return mutation; |
|
} |
|
function getComponentTouchDelay$1(component) { |
|
var options = component.context.options; |
|
var delay = options.eventLongPressDelay; |
|
if (delay == null) { |
|
delay = options.longPressDelay; |
|
} |
|
return delay; |
|
} |
|
|
|
var EventResizing = /** @class */ (function (_super) { |
|
__extends(EventResizing, _super); |
|
function EventResizing(settings) { |
|
var _this = _super.call(this, settings) || this; |
|
// internal state |
|
_this.draggingSegEl = null; |
|
_this.draggingSeg = null; // TODO: rename to resizingSeg? subjectSeg? |
|
_this.eventRange = null; |
|
_this.relevantEvents = null; |
|
_this.validMutation = null; |
|
_this.mutatedRelevantEvents = null; |
|
_this.handlePointerDown = function (ev) { |
|
var component = _this.component; |
|
var segEl = _this.querySegEl(ev); |
|
var seg = getElSeg(segEl); |
|
var eventRange = _this.eventRange = seg.eventRange; |
|
_this.dragging.minDistance = component.context.options.eventDragMinDistance; |
|
// if touch, need to be working with a selected event |
|
_this.dragging.setIgnoreMove(!_this.component.isValidSegDownEl(ev.origEvent.target) || |
|
(ev.isTouch && _this.component.props.eventSelection !== eventRange.instance.instanceId)); |
|
}; |
|
_this.handleDragStart = function (ev) { |
|
var context = _this.component.context; |
|
var eventRange = _this.eventRange; |
|
_this.relevantEvents = getRelevantEvents(context.getCurrentData().eventStore, _this.eventRange.instance.instanceId); |
|
var segEl = _this.querySegEl(ev); |
|
_this.draggingSegEl = segEl; |
|
_this.draggingSeg = getElSeg(segEl); |
|
context.calendarApi.unselect(); |
|
context.emitter.trigger('eventResizeStart', { |
|
el: segEl, |
|
event: new EventApi(context, eventRange.def, eventRange.instance), |
|
jsEvent: ev.origEvent, |
|
view: context.viewApi, |
|
}); |
|
}; |
|
_this.handleHitUpdate = function (hit, isFinal, ev) { |
|
var context = _this.component.context; |
|
var relevantEvents = _this.relevantEvents; |
|
var initialHit = _this.hitDragging.initialHit; |
|
var eventInstance = _this.eventRange.instance; |
|
var mutation = null; |
|
var mutatedRelevantEvents = null; |
|
var isInvalid = false; |
|
var interaction = { |
|
affectedEvents: relevantEvents, |
|
mutatedEvents: createEmptyEventStore(), |
|
isEvent: true, |
|
}; |
|
if (hit) { |
|
mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-event-resizer-start'), eventInstance.range, context.pluginHooks.eventResizeJoinTransforms); |
|
} |
|
if (mutation) { |
|
mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, context.getCurrentData().eventUiBases, mutation, context); |
|
interaction.mutatedEvents = mutatedRelevantEvents; |
|
if (!_this.component.isInteractionValid(interaction)) { |
|
isInvalid = true; |
|
mutation = null; |
|
mutatedRelevantEvents = null; |
|
interaction.mutatedEvents = null; |
|
} |
|
} |
|
if (mutatedRelevantEvents) { |
|
context.dispatch({ |
|
type: 'SET_EVENT_RESIZE', |
|
state: interaction, |
|
}); |
|
} |
|
else { |
|
context.dispatch({ type: 'UNSET_EVENT_RESIZE' }); |
|
} |
|
if (!isInvalid) { |
|
enableCursor(); |
|
} |
|
else { |
|
disableCursor(); |
|
} |
|
if (!isFinal) { |
|
if (mutation && isHitsEqual(initialHit, hit)) { |
|
mutation = null; |
|
} |
|
_this.validMutation = mutation; |
|
_this.mutatedRelevantEvents = mutatedRelevantEvents; |
|
} |
|
}; |
|
_this.handleDragEnd = function (ev) { |
|
var context = _this.component.context; |
|
var eventDef = _this.eventRange.def; |
|
var eventInstance = _this.eventRange.instance; |
|
var eventApi = new EventApi(context, eventDef, eventInstance); |
|
var relevantEvents = _this.relevantEvents; |
|
var mutatedRelevantEvents = _this.mutatedRelevantEvents; |
|
context.emitter.trigger('eventResizeStop', { |
|
el: _this.draggingSegEl, |
|
event: eventApi, |
|
jsEvent: ev.origEvent, |
|
view: context.viewApi, |
|
}); |
|
if (_this.validMutation) { |
|
var updatedEventApi = new EventApi(context, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null); |
|
context.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: mutatedRelevantEvents, |
|
}); |
|
var eventChangeArg = { |
|
oldEvent: eventApi, |
|
event: updatedEventApi, |
|
relatedEvents: buildEventApis(mutatedRelevantEvents, context, eventInstance), |
|
revert: function () { |
|
context.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: relevantEvents, |
|
}); |
|
}, |
|
}; |
|
context.emitter.trigger('eventResize', __assign(__assign({}, eventChangeArg), { el: _this.draggingSegEl, startDelta: _this.validMutation.startDelta || createDuration(0), endDelta: _this.validMutation.endDelta || createDuration(0), jsEvent: ev.origEvent, view: context.viewApi })); |
|
context.emitter.trigger('eventChange', eventChangeArg); |
|
} |
|
else { |
|
context.emitter.trigger('_noEventResize'); |
|
} |
|
// reset all internal state |
|
_this.draggingSeg = null; |
|
_this.relevantEvents = null; |
|
_this.validMutation = null; |
|
// okay to keep eventInstance around. useful to set it in handlePointerDown |
|
}; |
|
var component = settings.component; |
|
var dragging = _this.dragging = new FeaturefulElementDragging(settings.el); |
|
dragging.pointer.selector = '.fc-event-resizer'; |
|
dragging.touchScrollAllowed = false; |
|
dragging.autoScroller.isEnabled = component.context.options.dragScroll; |
|
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings)); |
|
hitDragging.emitter.on('pointerdown', _this.handlePointerDown); |
|
hitDragging.emitter.on('dragstart', _this.handleDragStart); |
|
hitDragging.emitter.on('hitupdate', _this.handleHitUpdate); |
|
hitDragging.emitter.on('dragend', _this.handleDragEnd); |
|
return _this; |
|
} |
|
EventResizing.prototype.destroy = function () { |
|
this.dragging.destroy(); |
|
}; |
|
EventResizing.prototype.querySegEl = function (ev) { |
|
return elementClosest(ev.subjectEl, '.fc-event'); |
|
}; |
|
return EventResizing; |
|
}(Interaction)); |
|
function computeMutation(hit0, hit1, isFromStart, instanceRange, transforms) { |
|
var dateEnv = hit0.component.context.dateEnv; |
|
var date0 = hit0.dateSpan.range.start; |
|
var date1 = hit1.dateSpan.range.start; |
|
var delta = diffDates(date0, date1, dateEnv, hit0.component.largeUnit); |
|
var props = {}; |
|
for (var _i = 0, transforms_1 = transforms; _i < transforms_1.length; _i++) { |
|
var transform = transforms_1[_i]; |
|
var res = transform(hit0, hit1); |
|
if (res === false) { |
|
return null; |
|
} |
|
if (res) { |
|
__assign(props, res); |
|
} |
|
} |
|
if (isFromStart) { |
|
if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) { |
|
props.startDelta = delta; |
|
return props; |
|
} |
|
} |
|
else if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) { |
|
props.endDelta = delta; |
|
return props; |
|
} |
|
return null; |
|
} |
|
|
|
var UnselectAuto = /** @class */ (function () { |
|
function UnselectAuto(context) { |
|
var _this = this; |
|
this.context = context; |
|
this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system |
|
this.matchesCancel = false; |
|
this.matchesEvent = false; |
|
this.onSelect = function (selectInfo) { |
|
if (selectInfo.jsEvent) { |
|
_this.isRecentPointerDateSelect = true; |
|
} |
|
}; |
|
this.onDocumentPointerDown = function (pev) { |
|
var unselectCancel = _this.context.options.unselectCancel; |
|
var downEl = pev.origEvent.target; |
|
_this.matchesCancel = !!elementClosest(downEl, unselectCancel); |
|
_this.matchesEvent = !!elementClosest(downEl, EventDragging.SELECTOR); // interaction started on an event? |
|
}; |
|
this.onDocumentPointerUp = function (pev) { |
|
var context = _this.context; |
|
var documentPointer = _this.documentPointer; |
|
var calendarState = context.getCurrentData(); |
|
// touch-scrolling should never unfocus any type of selection |
|
if (!documentPointer.wasTouchScroll) { |
|
if (calendarState.dateSelection && // an existing date selection? |
|
!_this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp? |
|
) { |
|
var unselectAuto = context.options.unselectAuto; |
|
if (unselectAuto && (!unselectAuto || !_this.matchesCancel)) { |
|
context.calendarApi.unselect(pev); |
|
} |
|
} |
|
if (calendarState.eventSelection && // an existing event selected? |
|
!_this.matchesEvent // interaction DIDN'T start on an event |
|
) { |
|
context.dispatch({ type: 'UNSELECT_EVENT' }); |
|
} |
|
} |
|
_this.isRecentPointerDateSelect = false; |
|
}; |
|
var documentPointer = this.documentPointer = new PointerDragging(document); |
|
documentPointer.shouldIgnoreMove = true; |
|
documentPointer.shouldWatchScroll = false; |
|
documentPointer.emitter.on('pointerdown', this.onDocumentPointerDown); |
|
documentPointer.emitter.on('pointerup', this.onDocumentPointerUp); |
|
/* |
|
TODO: better way to know about whether there was a selection with the pointer |
|
*/ |
|
context.emitter.on('select', this.onSelect); |
|
} |
|
UnselectAuto.prototype.destroy = function () { |
|
this.context.emitter.off('select', this.onSelect); |
|
this.documentPointer.destroy(); |
|
}; |
|
return UnselectAuto; |
|
}()); |
|
|
|
var OPTION_REFINERS = { |
|
fixedMirrorParent: identity, |
|
}; |
|
var LISTENER_REFINERS = { |
|
dateClick: identity, |
|
eventDragStart: identity, |
|
eventDragStop: identity, |
|
eventDrop: identity, |
|
eventResizeStart: identity, |
|
eventResizeStop: identity, |
|
eventResize: identity, |
|
drop: identity, |
|
eventReceive: identity, |
|
eventLeave: identity, |
|
}; |
|
|
|
/* |
|
Given an already instantiated draggable object for one-or-more elements, |
|
Interprets any dragging as an attempt to drag an events that lives outside |
|
of a calendar onto a calendar. |
|
*/ |
|
var ExternalElementDragging = /** @class */ (function () { |
|
function ExternalElementDragging(dragging, suppliedDragMeta) { |
|
var _this = this; |
|
this.receivingContext = null; |
|
this.droppableEvent = null; // will exist for all drags, even if create:false |
|
this.suppliedDragMeta = null; |
|
this.dragMeta = null; |
|
this.handleDragStart = function (ev) { |
|
_this.dragMeta = _this.buildDragMeta(ev.subjectEl); |
|
}; |
|
this.handleHitUpdate = function (hit, isFinal, ev) { |
|
var dragging = _this.hitDragging.dragging; |
|
var receivingContext = null; |
|
var droppableEvent = null; |
|
var isInvalid = false; |
|
var interaction = { |
|
affectedEvents: createEmptyEventStore(), |
|
mutatedEvents: createEmptyEventStore(), |
|
isEvent: _this.dragMeta.create, |
|
}; |
|
if (hit) { |
|
receivingContext = hit.component.context; |
|
if (_this.canDropElOnCalendar(ev.subjectEl, receivingContext)) { |
|
droppableEvent = computeEventForDateSpan(hit.dateSpan, _this.dragMeta, receivingContext); |
|
interaction.mutatedEvents = eventTupleToStore(droppableEvent); |
|
isInvalid = !isInteractionValid(interaction, receivingContext); |
|
if (isInvalid) { |
|
interaction.mutatedEvents = createEmptyEventStore(); |
|
droppableEvent = null; |
|
} |
|
} |
|
} |
|
_this.displayDrag(receivingContext, interaction); |
|
// show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?) |
|
// TODO: wish we could somehow wait for dispatch to guarantee render |
|
dragging.setMirrorIsVisible(isFinal || !droppableEvent || !document.querySelector('.fc-event-mirror')); |
|
if (!isInvalid) { |
|
enableCursor(); |
|
} |
|
else { |
|
disableCursor(); |
|
} |
|
if (!isFinal) { |
|
dragging.setMirrorNeedsRevert(!droppableEvent); |
|
_this.receivingContext = receivingContext; |
|
_this.droppableEvent = droppableEvent; |
|
} |
|
}; |
|
this.handleDragEnd = function (pev) { |
|
var _a = _this, receivingContext = _a.receivingContext, droppableEvent = _a.droppableEvent; |
|
_this.clearDrag(); |
|
if (receivingContext && droppableEvent) { |
|
var finalHit = _this.hitDragging.finalHit; |
|
var finalView = finalHit.component.context.viewApi; |
|
var dragMeta = _this.dragMeta; |
|
receivingContext.emitter.trigger('drop', __assign(__assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext)), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView })); |
|
if (dragMeta.create) { |
|
var addingEvents_1 = eventTupleToStore(droppableEvent); |
|
receivingContext.dispatch({ |
|
type: 'MERGE_EVENTS', |
|
eventStore: addingEvents_1, |
|
}); |
|
if (pev.isTouch) { |
|
receivingContext.dispatch({ |
|
type: 'SELECT_EVENT', |
|
eventInstanceId: droppableEvent.instance.instanceId, |
|
}); |
|
} |
|
// signal that an external event landed |
|
receivingContext.emitter.trigger('eventReceive', { |
|
event: new EventApi(receivingContext, droppableEvent.def, droppableEvent.instance), |
|
relatedEvents: [], |
|
revert: function () { |
|
receivingContext.dispatch({ |
|
type: 'REMOVE_EVENTS', |
|
eventStore: addingEvents_1, |
|
}); |
|
}, |
|
draggedEl: pev.subjectEl, |
|
view: finalView, |
|
}); |
|
} |
|
} |
|
_this.receivingContext = null; |
|
_this.droppableEvent = null; |
|
}; |
|
var hitDragging = this.hitDragging = new HitDragging(dragging, interactionSettingsStore); |
|
hitDragging.requireInitial = false; // will start outside of a component |
|
hitDragging.emitter.on('dragstart', this.handleDragStart); |
|
hitDragging.emitter.on('hitupdate', this.handleHitUpdate); |
|
hitDragging.emitter.on('dragend', this.handleDragEnd); |
|
this.suppliedDragMeta = suppliedDragMeta; |
|
} |
|
ExternalElementDragging.prototype.buildDragMeta = function (subjectEl) { |
|
if (typeof this.suppliedDragMeta === 'object') { |
|
return parseDragMeta(this.suppliedDragMeta); |
|
} |
|
if (typeof this.suppliedDragMeta === 'function') { |
|
return parseDragMeta(this.suppliedDragMeta(subjectEl)); |
|
} |
|
return getDragMetaFromEl(subjectEl); |
|
}; |
|
ExternalElementDragging.prototype.displayDrag = function (nextContext, state) { |
|
var prevContext = this.receivingContext; |
|
if (prevContext && prevContext !== nextContext) { |
|
prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); |
|
} |
|
if (nextContext) { |
|
nextContext.dispatch({ type: 'SET_EVENT_DRAG', state: state }); |
|
} |
|
}; |
|
ExternalElementDragging.prototype.clearDrag = function () { |
|
if (this.receivingContext) { |
|
this.receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); |
|
} |
|
}; |
|
ExternalElementDragging.prototype.canDropElOnCalendar = function (el, receivingContext) { |
|
var dropAccept = receivingContext.options.dropAccept; |
|
if (typeof dropAccept === 'function') { |
|
return dropAccept.call(receivingContext.calendarApi, el); |
|
} |
|
if (typeof dropAccept === 'string' && dropAccept) { |
|
return Boolean(elementMatches(el, dropAccept)); |
|
} |
|
return true; |
|
}; |
|
return ExternalElementDragging; |
|
}()); |
|
// Utils for computing event store from the DragMeta |
|
// ---------------------------------------------------------------------------------------------------- |
|
function computeEventForDateSpan(dateSpan, dragMeta, context) { |
|
var defProps = __assign({}, dragMeta.leftoverProps); |
|
for (var _i = 0, _a = context.pluginHooks.externalDefTransforms; _i < _a.length; _i++) { |
|
var transform = _a[_i]; |
|
__assign(defProps, transform(dateSpan, dragMeta)); |
|
} |
|
var _b = refineEventDef(defProps, context), refined = _b.refined, extra = _b.extra; |
|
var def = parseEventDef(refined, extra, dragMeta.sourceId, dateSpan.allDay, context.options.forceEventDuration || Boolean(dragMeta.duration), // hasEnd |
|
context); |
|
var start = dateSpan.range.start; |
|
// only rely on time info if drop zone is all-day, |
|
// otherwise, we already know the time |
|
if (dateSpan.allDay && dragMeta.startTime) { |
|
start = context.dateEnv.add(start, dragMeta.startTime); |
|
} |
|
var end = dragMeta.duration ? |
|
context.dateEnv.add(start, dragMeta.duration) : |
|
getDefaultEventEnd(dateSpan.allDay, start, context); |
|
var instance = createEventInstance(def.defId, { start: start, end: end }); |
|
return { def: def, instance: instance }; |
|
} |
|
// Utils for extracting data from element |
|
// ---------------------------------------------------------------------------------------------------- |
|
function getDragMetaFromEl(el) { |
|
var str = getEmbeddedElData(el, 'event'); |
|
var obj = str ? |
|
JSON.parse(str) : |
|
{ create: false }; // if no embedded data, assume no event creation |
|
return parseDragMeta(obj); |
|
} |
|
config.dataAttrPrefix = ''; |
|
function getEmbeddedElData(el, name) { |
|
var prefix = config.dataAttrPrefix; |
|
var prefixedName = (prefix ? prefix + '-' : '') + name; |
|
return el.getAttribute('data-' + prefixedName) || ''; |
|
} |
|
|
|
/* |
|
Makes an element (that is *external* to any calendar) draggable. |
|
Can pass in data that determines how an event will be created when dropped onto a calendar. |
|
Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system. |
|
*/ |
|
var ExternalDraggable = /** @class */ (function () { |
|
function ExternalDraggable(el, settings) { |
|
var _this = this; |
|
if (settings === void 0) { settings = {}; } |
|
this.handlePointerDown = function (ev) { |
|
var dragging = _this.dragging; |
|
var _a = _this.settings, minDistance = _a.minDistance, longPressDelay = _a.longPressDelay; |
|
dragging.minDistance = |
|
minDistance != null ? |
|
minDistance : |
|
(ev.isTouch ? 0 : BASE_OPTION_DEFAULTS.eventDragMinDistance); |
|
dragging.delay = |
|
ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv |
|
(longPressDelay != null ? longPressDelay : BASE_OPTION_DEFAULTS.longPressDelay) : |
|
0; |
|
}; |
|
this.handleDragStart = function (ev) { |
|
if (ev.isTouch && |
|
_this.dragging.delay && |
|
ev.subjectEl.classList.contains('fc-event')) { |
|
_this.dragging.mirror.getMirrorEl().classList.add('fc-event-selected'); |
|
} |
|
}; |
|
this.settings = settings; |
|
var dragging = this.dragging = new FeaturefulElementDragging(el); |
|
dragging.touchScrollAllowed = false; |
|
if (settings.itemSelector != null) { |
|
dragging.pointer.selector = settings.itemSelector; |
|
} |
|
if (settings.appendTo != null) { |
|
dragging.mirror.parentNode = settings.appendTo; // TODO: write tests |
|
} |
|
dragging.emitter.on('pointerdown', this.handlePointerDown); |
|
dragging.emitter.on('dragstart', this.handleDragStart); |
|
new ExternalElementDragging(dragging, settings.eventData); // eslint-disable-line no-new |
|
} |
|
ExternalDraggable.prototype.destroy = function () { |
|
this.dragging.destroy(); |
|
}; |
|
return ExternalDraggable; |
|
}()); |
|
|
|
/* |
|
Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements. |
|
The third-party system is responsible for drawing the visuals effects of the drag. |
|
This class simply monitors for pointer movements and fires events. |
|
It also has the ability to hide the moving element (the "mirror") during the drag. |
|
*/ |
|
var InferredElementDragging = /** @class */ (function (_super) { |
|
__extends(InferredElementDragging, _super); |
|
function InferredElementDragging(containerEl) { |
|
var _this = _super.call(this, containerEl) || this; |
|
_this.shouldIgnoreMove = false; |
|
_this.mirrorSelector = ''; |
|
_this.currentMirrorEl = null; |
|
_this.handlePointerDown = function (ev) { |
|
_this.emitter.trigger('pointerdown', ev); |
|
if (!_this.shouldIgnoreMove) { |
|
// fire dragstart right away. does not support delay or min-distance |
|
_this.emitter.trigger('dragstart', ev); |
|
} |
|
}; |
|
_this.handlePointerMove = function (ev) { |
|
if (!_this.shouldIgnoreMove) { |
|
_this.emitter.trigger('dragmove', ev); |
|
} |
|
}; |
|
_this.handlePointerUp = function (ev) { |
|
_this.emitter.trigger('pointerup', ev); |
|
if (!_this.shouldIgnoreMove) { |
|
// fire dragend right away. does not support a revert animation |
|
_this.emitter.trigger('dragend', ev); |
|
} |
|
}; |
|
var pointer = _this.pointer = new PointerDragging(containerEl); |
|
pointer.emitter.on('pointerdown', _this.handlePointerDown); |
|
pointer.emitter.on('pointermove', _this.handlePointerMove); |
|
pointer.emitter.on('pointerup', _this.handlePointerUp); |
|
return _this; |
|
} |
|
InferredElementDragging.prototype.destroy = function () { |
|
this.pointer.destroy(); |
|
}; |
|
InferredElementDragging.prototype.setIgnoreMove = function (bool) { |
|
this.shouldIgnoreMove = bool; |
|
}; |
|
InferredElementDragging.prototype.setMirrorIsVisible = function (bool) { |
|
if (bool) { |
|
// restore a previously hidden element. |
|
// use the reference in case the selector class has already been removed. |
|
if (this.currentMirrorEl) { |
|
this.currentMirrorEl.style.visibility = ''; |
|
this.currentMirrorEl = null; |
|
} |
|
} |
|
else { |
|
var mirrorEl = this.mirrorSelector ? |
|
document.querySelector(this.mirrorSelector) : |
|
null; |
|
if (mirrorEl) { |
|
this.currentMirrorEl = mirrorEl; |
|
mirrorEl.style.visibility = 'hidden'; |
|
} |
|
} |
|
}; |
|
return InferredElementDragging; |
|
}(ElementDragging)); |
|
|
|
/* |
|
Bridges third-party drag-n-drop systems with FullCalendar. |
|
Must be instantiated and destroyed by caller. |
|
*/ |
|
var ThirdPartyDraggable = /** @class */ (function () { |
|
function ThirdPartyDraggable(containerOrSettings, settings) { |
|
var containerEl = document; |
|
if ( |
|
// wish we could just test instanceof EventTarget, but doesn't work in IE11 |
|
containerOrSettings === document || |
|
containerOrSettings instanceof Element) { |
|
containerEl = containerOrSettings; |
|
settings = settings || {}; |
|
} |
|
else { |
|
settings = (containerOrSettings || {}); |
|
} |
|
var dragging = this.dragging = new InferredElementDragging(containerEl); |
|
if (typeof settings.itemSelector === 'string') { |
|
dragging.pointer.selector = settings.itemSelector; |
|
} |
|
else if (containerEl === document) { |
|
dragging.pointer.selector = '[data-event]'; |
|
} |
|
if (typeof settings.mirrorSelector === 'string') { |
|
dragging.mirrorSelector = settings.mirrorSelector; |
|
} |
|
new ExternalElementDragging(dragging, settings.eventData); // eslint-disable-line no-new |
|
} |
|
ThirdPartyDraggable.prototype.destroy = function () { |
|
this.dragging.destroy(); |
|
}; |
|
return ThirdPartyDraggable; |
|
}()); |
|
|
|
var interactionPlugin = createPlugin({ |
|
componentInteractions: [DateClicking, DateSelecting, EventDragging, EventResizing], |
|
calendarInteractions: [UnselectAuto], |
|
elementDraggingImpl: FeaturefulElementDragging, |
|
optionRefiners: OPTION_REFINERS, |
|
listenerRefiners: LISTENER_REFINERS, |
|
}); |
|
|
|
/* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells. |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
// It is a manager for a Table subcomponent, which does most of the heavy lifting. |
|
// It is responsible for managing width/height. |
|
var TableView = /** @class */ (function (_super) { |
|
__extends(TableView, _super); |
|
function TableView() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.headerElRef = createRef(); |
|
return _this; |
|
} |
|
TableView.prototype.renderSimpleLayout = function (headerRowContent, bodyContent) { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var sections = []; |
|
var stickyHeaderDates = getStickyHeaderDates(context.options); |
|
if (headerRowContent) { |
|
sections.push({ |
|
type: 'header', |
|
key: 'header', |
|
isSticky: stickyHeaderDates, |
|
chunk: { |
|
elRef: this.headerElRef, |
|
tableClassName: 'fc-col-header', |
|
rowContent: headerRowContent, |
|
}, |
|
}); |
|
} |
|
sections.push({ |
|
type: 'body', |
|
key: 'body', |
|
liquid: true, |
|
chunk: { content: bodyContent }, |
|
}); |
|
return (createElement(ViewRoot, { viewSpec: context.viewSpec }, function (rootElRef, classNames) { return (createElement("div", { ref: rootElRef, className: ['fc-daygrid'].concat(classNames).join(' ') }, |
|
createElement(SimpleScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, cols: [] /* TODO: make optional? */, sections: sections }))); })); |
|
}; |
|
TableView.prototype.renderHScrollLayout = function (headerRowContent, bodyContent, colCnt, dayMinWidth) { |
|
var ScrollGrid = this.context.pluginHooks.scrollGridImpl; |
|
if (!ScrollGrid) { |
|
throw new Error('No ScrollGrid implementation'); |
|
} |
|
var _a = this, props = _a.props, context = _a.context; |
|
var stickyHeaderDates = !props.forPrint && getStickyHeaderDates(context.options); |
|
var stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(context.options); |
|
var sections = []; |
|
if (headerRowContent) { |
|
sections.push({ |
|
type: 'header', |
|
key: 'header', |
|
isSticky: stickyHeaderDates, |
|
chunks: [{ |
|
key: 'main', |
|
elRef: this.headerElRef, |
|
tableClassName: 'fc-col-header', |
|
rowContent: headerRowContent, |
|
}], |
|
}); |
|
} |
|
sections.push({ |
|
type: 'body', |
|
key: 'body', |
|
liquid: true, |
|
chunks: [{ |
|
key: 'main', |
|
content: bodyContent, |
|
}], |
|
}); |
|
if (stickyFooterScrollbar) { |
|
sections.push({ |
|
type: 'footer', |
|
key: 'footer', |
|
isSticky: true, |
|
chunks: [{ |
|
key: 'main', |
|
content: renderScrollShim, |
|
}], |
|
}); |
|
} |
|
return (createElement(ViewRoot, { viewSpec: context.viewSpec }, function (rootElRef, classNames) { return (createElement("div", { ref: rootElRef, className: ['fc-daygrid'].concat(classNames).join(' ') }, |
|
createElement(ScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, colGroups: [{ cols: [{ span: colCnt, minWidth: dayMinWidth }] }], sections: sections }))); })); |
|
}; |
|
return TableView; |
|
}(DateComponent)); |
|
|
|
function splitSegsByRow(segs, rowCnt) { |
|
var byRow = []; |
|
for (var i = 0; i < rowCnt; i += 1) { |
|
byRow[i] = []; |
|
} |
|
for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { |
|
var seg = segs_1[_i]; |
|
byRow[seg.row].push(seg); |
|
} |
|
return byRow; |
|
} |
|
function splitSegsByFirstCol(segs, colCnt) { |
|
var byCol = []; |
|
for (var i = 0; i < colCnt; i += 1) { |
|
byCol[i] = []; |
|
} |
|
for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { |
|
var seg = segs_2[_i]; |
|
byCol[seg.firstCol].push(seg); |
|
} |
|
return byCol; |
|
} |
|
function splitInteractionByRow(ui, rowCnt) { |
|
var byRow = []; |
|
if (!ui) { |
|
for (var i = 0; i < rowCnt; i += 1) { |
|
byRow[i] = null; |
|
} |
|
} |
|
else { |
|
for (var i = 0; i < rowCnt; i += 1) { |
|
byRow[i] = { |
|
affectedInstances: ui.affectedInstances, |
|
isEvent: ui.isEvent, |
|
segs: [], |
|
}; |
|
} |
|
for (var _i = 0, _a = ui.segs; _i < _a.length; _i++) { |
|
var seg = _a[_i]; |
|
byRow[seg.row].segs.push(seg); |
|
} |
|
} |
|
return byRow; |
|
} |
|
|
|
var TableCellTop = /** @class */ (function (_super) { |
|
__extends(TableCellTop, _super); |
|
function TableCellTop() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TableCellTop.prototype.render = function () { |
|
var props = this.props; |
|
var navLinkAttrs = this.context.options.navLinks |
|
? { 'data-navlink': buildNavLinkData(props.date), tabIndex: 0 } |
|
: {}; |
|
return (createElement(DayCellContent, { date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, extraHookProps: props.extraHookProps, defaultContent: renderTopInner }, function (innerElRef, innerContent) { return ((innerContent || props.forceDayTop) && (createElement("div", { className: "fc-daygrid-day-top", ref: innerElRef }, |
|
createElement("a", __assign({ className: "fc-daygrid-day-number" }, navLinkAttrs), innerContent || createElement(Fragment, null, "\u00A0"))))); })); |
|
}; |
|
return TableCellTop; |
|
}(BaseComponent)); |
|
function renderTopInner(props) { |
|
return props.dayNumberText; |
|
} |
|
|
|
var DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'narrow' }); |
|
var TableCell = /** @class */ (function (_super) { |
|
__extends(TableCell, _super); |
|
function TableCell() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.handleRootEl = function (el) { |
|
_this.rootEl = el; |
|
setRef(_this.props.elRef, el); |
|
}; |
|
_this.handleMoreLinkClick = function (ev) { |
|
var props = _this.props; |
|
if (props.onMoreClick) { |
|
var allSegs = props.segsByEachCol; |
|
var hiddenSegs = allSegs.filter(function (seg) { return props.segIsHidden[seg.eventRange.instance.instanceId]; }); |
|
props.onMoreClick({ |
|
date: props.date, |
|
allSegs: allSegs, |
|
hiddenSegs: hiddenSegs, |
|
moreCnt: props.moreCnt, |
|
dayEl: _this.rootEl, |
|
ev: ev, |
|
}); |
|
} |
|
}; |
|
return _this; |
|
} |
|
TableCell.prototype.render = function () { |
|
var _this = this; |
|
var _a = this.context, options = _a.options, viewApi = _a.viewApi; |
|
var props = this.props; |
|
var date = props.date, dateProfile = props.dateProfile; |
|
var hookProps = { |
|
num: props.moreCnt, |
|
text: props.buildMoreLinkText(props.moreCnt), |
|
view: viewApi, |
|
}; |
|
var navLinkAttrs = options.navLinks |
|
? { 'data-navlink': buildNavLinkData(date, 'week'), tabIndex: 0 } |
|
: {}; |
|
return (createElement(DayCellRoot, { date: date, dateProfile: dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, extraHookProps: props.extraHookProps, elRef: this.handleRootEl }, function (dayElRef, dayClassNames, rootDataAttrs, isDisabled) { return (createElement("td", __assign({ ref: dayElRef, className: ['fc-daygrid-day'].concat(dayClassNames, props.extraClassNames || []).join(' ') }, rootDataAttrs, props.extraDataAttrs), |
|
createElement("div", { className: "fc-daygrid-day-frame fc-scrollgrid-sync-inner", ref: props.innerElRef /* different from hook system! RENAME */ }, |
|
props.showWeekNumber && (createElement(WeekNumberRoot, { date: date, defaultFormat: DEFAULT_WEEK_NUM_FORMAT }, function (weekElRef, weekClassNames, innerElRef, innerContent) { return (createElement("a", __assign({ ref: weekElRef, className: ['fc-daygrid-week-number'].concat(weekClassNames).join(' ') }, navLinkAttrs), innerContent)); })), |
|
!isDisabled && (createElement(TableCellTop, { date: date, dateProfile: dateProfile, showDayNumber: props.showDayNumber, forceDayTop: props.forceDayTop, todayRange: props.todayRange, extraHookProps: props.extraHookProps })), |
|
createElement("div", { className: "fc-daygrid-day-events", ref: props.fgContentElRef, style: { paddingBottom: props.fgPaddingBottom } }, |
|
props.fgContent, |
|
Boolean(props.moreCnt) && (createElement("div", { className: "fc-daygrid-day-bottom", style: { marginTop: props.moreMarginTop } }, |
|
createElement(RenderHook, { hookProps: hookProps, classNames: options.moreLinkClassNames, content: options.moreLinkContent, defaultContent: renderMoreLinkInner, didMount: options.moreLinkDidMount, willUnmount: options.moreLinkWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("a", { ref: rootElRef, className: ['fc-daygrid-more-link'].concat(classNames).join(' '), onClick: _this.handleMoreLinkClick }, innerContent)); })))), |
|
createElement("div", { className: "fc-daygrid-day-bg" }, props.bgContent)))); })); |
|
}; |
|
return TableCell; |
|
}(DateComponent)); |
|
TableCell.addPropsEquality({ |
|
onMoreClick: true, |
|
}); |
|
function renderMoreLinkInner(props) { |
|
return props.text; |
|
} |
|
|
|
var DEFAULT_TABLE_EVENT_TIME_FORMAT = createFormatter({ |
|
hour: 'numeric', |
|
minute: '2-digit', |
|
omitZeroMinute: true, |
|
meridiem: 'narrow', |
|
}); |
|
function hasListItemDisplay(seg) { |
|
var display = seg.eventRange.ui.display; |
|
return display === 'list-item' || (display === 'auto' && |
|
!seg.eventRange.def.allDay && |
|
seg.firstCol === seg.lastCol && // can't be multi-day |
|
seg.isStart && // " |
|
seg.isEnd // " |
|
); |
|
} |
|
|
|
var TableListItemEvent = /** @class */ (function (_super) { |
|
__extends(TableListItemEvent, _super); |
|
function TableListItemEvent() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TableListItemEvent.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var timeFormat = context.options.eventTimeFormat || DEFAULT_TABLE_EVENT_TIME_FORMAT; |
|
var timeText = buildSegTimeText(props.seg, timeFormat, context, true, props.defaultDisplayEventEnd); |
|
return (createElement(EventRoot, { seg: props.seg, timeText: timeText, defaultContent: renderInnerContent$2, isDragging: props.isDragging, isResizing: false, isDateSelecting: false, isSelected: props.isSelected, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent) { return ( // we don't use styles! |
|
createElement("a", __assign({ className: ['fc-daygrid-event', 'fc-daygrid-dot-event'].concat(classNames).join(' '), ref: rootElRef }, getSegAnchorAttrs$1(props.seg)), innerContent)); })); |
|
}; |
|
return TableListItemEvent; |
|
}(BaseComponent)); |
|
function renderInnerContent$2(innerProps) { |
|
return (createElement(Fragment, null, |
|
createElement("div", { className: "fc-daygrid-event-dot", style: { borderColor: innerProps.borderColor || innerProps.backgroundColor } }), |
|
innerProps.timeText && (createElement("div", { className: "fc-event-time" }, innerProps.timeText)), |
|
createElement("div", { className: "fc-event-title" }, innerProps.event.title || createElement(Fragment, null, "\u00A0")))); |
|
} |
|
function getSegAnchorAttrs$1(seg) { |
|
var url = seg.eventRange.def.url; |
|
return url ? { href: url } : {}; |
|
} |
|
|
|
var TableBlockEvent = /** @class */ (function (_super) { |
|
__extends(TableBlockEvent, _super); |
|
function TableBlockEvent() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TableBlockEvent.prototype.render = function () { |
|
var props = this.props; |
|
return (createElement(StandardEvent, __assign({}, props, { extraClassNames: ['fc-daygrid-event', 'fc-daygrid-block-event', 'fc-h-event'], defaultTimeFormat: DEFAULT_TABLE_EVENT_TIME_FORMAT, defaultDisplayEventEnd: props.defaultDisplayEventEnd, disableResizing: !props.seg.eventRange.def.allDay }))); |
|
}; |
|
return TableBlockEvent; |
|
}(BaseComponent)); |
|
|
|
function computeFgSegPlacement(// for one row. TODO: print mode? |
|
cellModels, segs, dayMaxEvents, dayMaxEventRows, eventHeights, maxContentHeight, colCnt, eventOrderSpecs) { |
|
var colPlacements = []; // if event spans multiple cols, its present in each col |
|
var moreCnts = []; // by-col |
|
var segIsHidden = {}; |
|
var segTops = {}; // always populated for each seg |
|
var segMarginTops = {}; // simetimes populated for each seg |
|
var moreTops = {}; |
|
var paddingBottoms = {}; // for each cell's inner-wrapper div |
|
for (var i = 0; i < colCnt; i += 1) { |
|
colPlacements.push([]); |
|
moreCnts.push(0); |
|
} |
|
segs = sortEventSegs(segs, eventOrderSpecs); |
|
for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { |
|
var seg = segs_1[_i]; |
|
var instanceId = seg.eventRange.instance.instanceId; |
|
var eventHeight = eventHeights[instanceId + ':' + seg.firstCol]; |
|
placeSeg(seg, eventHeight || 0); // will keep colPlacements sorted by top |
|
} |
|
if (dayMaxEvents === true || dayMaxEventRows === true) { |
|
limitByMaxHeight(moreCnts, segIsHidden, colPlacements, maxContentHeight); // populates moreCnts/segIsHidden |
|
} |
|
else if (typeof dayMaxEvents === 'number') { |
|
limitByMaxEvents(moreCnts, segIsHidden, colPlacements, dayMaxEvents); // populates moreCnts/segIsHidden |
|
} |
|
else if (typeof dayMaxEventRows === 'number') { |
|
limitByMaxRows(moreCnts, segIsHidden, colPlacements, dayMaxEventRows); // populates moreCnts/segIsHidden |
|
} |
|
// computes segTops/segMarginTops/moreTops/paddingBottoms |
|
for (var col = 0; col < colCnt; col += 1) { |
|
var placements = colPlacements[col]; |
|
var currentNonAbsBottom = 0; |
|
var currentAbsHeight = 0; |
|
for (var _a = 0, placements_1 = placements; _a < placements_1.length; _a++) { |
|
var placement = placements_1[_a]; |
|
var seg = placement.seg; |
|
if (!segIsHidden[seg.eventRange.instance.instanceId]) { |
|
segTops[seg.eventRange.instance.instanceId] = placement.top; // from top of container |
|
if (seg.firstCol === seg.lastCol && seg.isStart && seg.isEnd) { // TODO: simpler way? NOT DRY |
|
segMarginTops[seg.eventRange.instance.instanceId] = |
|
placement.top - currentNonAbsBottom; // from previous seg bottom |
|
currentAbsHeight = 0; |
|
currentNonAbsBottom = placement.bottom; |
|
} |
|
else { // multi-col event, abs positioned |
|
currentAbsHeight = placement.bottom - currentNonAbsBottom; |
|
} |
|
} |
|
} |
|
if (currentAbsHeight) { |
|
if (moreCnts[col]) { |
|
moreTops[col] = currentAbsHeight; |
|
} |
|
else { |
|
paddingBottoms[col] = currentAbsHeight; |
|
} |
|
} |
|
} |
|
function placeSeg(seg, segHeight) { |
|
if (!tryPlaceSegAt(seg, segHeight, 0)) { |
|
for (var col = seg.firstCol; col <= seg.lastCol; col += 1) { |
|
for (var _i = 0, _a = colPlacements[col]; _i < _a.length; _i++) { // will repeat multi-day segs!!!!!!! bad!!!!!! |
|
var placement = _a[_i]; |
|
if (tryPlaceSegAt(seg, segHeight, placement.bottom)) { |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
function tryPlaceSegAt(seg, segHeight, top) { |
|
if (canPlaceSegAt(seg, segHeight, top)) { |
|
for (var col = seg.firstCol; col <= seg.lastCol; col += 1) { |
|
var placements = colPlacements[col]; |
|
var insertionIndex = 0; |
|
while (insertionIndex < placements.length && |
|
top >= placements[insertionIndex].top) { |
|
insertionIndex += 1; |
|
} |
|
placements.splice(insertionIndex, 0, { |
|
seg: seg, |
|
top: top, |
|
bottom: top + segHeight, |
|
}); |
|
} |
|
return true; |
|
} |
|
return false; |
|
} |
|
function canPlaceSegAt(seg, segHeight, top) { |
|
for (var col = seg.firstCol; col <= seg.lastCol; col += 1) { |
|
for (var _i = 0, _a = colPlacements[col]; _i < _a.length; _i++) { |
|
var placement = _a[_i]; |
|
if (top < placement.bottom && top + segHeight > placement.top) { // collide? |
|
return false; |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
// what does this do!? |
|
for (var instanceIdAndFirstCol in eventHeights) { |
|
if (!eventHeights[instanceIdAndFirstCol]) { |
|
segIsHidden[instanceIdAndFirstCol.split(':')[0]] = true; |
|
} |
|
} |
|
var segsByFirstCol = colPlacements.map(extractFirstColSegs); // operates on the sorted cols |
|
var segsByEachCol = colPlacements.map(function (placements, col) { |
|
var segsForCols = extractAllColSegs(placements); |
|
segsForCols = resliceDaySegs(segsForCols, cellModels[col].date, col); |
|
return segsForCols; |
|
}); |
|
return { |
|
segsByFirstCol: segsByFirstCol, |
|
segsByEachCol: segsByEachCol, |
|
segIsHidden: segIsHidden, |
|
segTops: segTops, |
|
segMarginTops: segMarginTops, |
|
moreCnts: moreCnts, |
|
moreTops: moreTops, |
|
paddingBottoms: paddingBottoms, |
|
}; |
|
} |
|
function extractFirstColSegs(oneColPlacements, col) { |
|
var segs = []; |
|
for (var _i = 0, oneColPlacements_1 = oneColPlacements; _i < oneColPlacements_1.length; _i++) { |
|
var placement = oneColPlacements_1[_i]; |
|
if (placement.seg.firstCol === col) { |
|
segs.push(placement.seg); |
|
} |
|
} |
|
return segs; |
|
} |
|
function extractAllColSegs(oneColPlacements) { |
|
var segs = []; |
|
for (var _i = 0, oneColPlacements_2 = oneColPlacements; _i < oneColPlacements_2.length; _i++) { |
|
var placement = oneColPlacements_2[_i]; |
|
segs.push(placement.seg); |
|
} |
|
return segs; |
|
} |
|
function limitByMaxHeight(hiddenCnts, segIsHidden, colPlacements, maxContentHeight) { |
|
limitEvents(hiddenCnts, segIsHidden, colPlacements, true, function (placement) { return placement.bottom <= maxContentHeight; }); |
|
} |
|
function limitByMaxEvents(hiddenCnts, segIsHidden, colPlacements, dayMaxEvents) { |
|
limitEvents(hiddenCnts, segIsHidden, colPlacements, false, function (placement, levelIndex) { return levelIndex < dayMaxEvents; }); |
|
} |
|
function limitByMaxRows(hiddenCnts, segIsHidden, colPlacements, dayMaxEventRows) { |
|
limitEvents(hiddenCnts, segIsHidden, colPlacements, true, function (placement, levelIndex) { return levelIndex < dayMaxEventRows; }); |
|
} |
|
/* |
|
populates the given hiddenCnts/segIsHidden, which are supplied empty. |
|
TODO: return them instead |
|
*/ |
|
function limitEvents(hiddenCnts, segIsHidden, colPlacements, _moreLinkConsumesLevel, isPlacementInBounds) { |
|
var colCnt = hiddenCnts.length; |
|
var segIsVisible = {}; // TODO: instead, use segIsHidden with true/false? |
|
var visibleColPlacements = []; // will mirror colPlacements |
|
for (var col = 0; col < colCnt; col += 1) { |
|
visibleColPlacements.push([]); |
|
} |
|
for (var col = 0; col < colCnt; col += 1) { |
|
var placements = colPlacements[col]; |
|
var level = 0; |
|
for (var _i = 0, placements_2 = placements; _i < placements_2.length; _i++) { |
|
var placement = placements_2[_i]; |
|
if (isPlacementInBounds(placement, level)) { |
|
recordVisible(placement); |
|
} |
|
else { |
|
recordHidden(placement, level, _moreLinkConsumesLevel); |
|
} |
|
// only considered a level if the seg had height |
|
if (placement.top !== placement.bottom) { |
|
level += 1; |
|
} |
|
} |
|
} |
|
function recordVisible(placement) { |
|
var seg = placement.seg; |
|
var instanceId = seg.eventRange.instance.instanceId; |
|
if (!segIsVisible[instanceId]) { |
|
segIsVisible[instanceId] = true; |
|
for (var col = seg.firstCol; col <= seg.lastCol; col += 1) { |
|
var destPlacements = visibleColPlacements[col]; |
|
var newPosition = 0; |
|
// insert while keeping top sorted in each column |
|
while (newPosition < destPlacements.length && |
|
placement.top >= destPlacements[newPosition].top) { |
|
newPosition += 1; |
|
} |
|
destPlacements.splice(newPosition, 0, placement); |
|
} |
|
} |
|
} |
|
function recordHidden(placement, currentLevel, moreLinkConsumesLevel) { |
|
var seg = placement.seg; |
|
var instanceId = seg.eventRange.instance.instanceId; |
|
if (!segIsHidden[instanceId]) { |
|
segIsHidden[instanceId] = true; |
|
for (var col = seg.firstCol; col <= seg.lastCol; col += 1) { |
|
hiddenCnts[col] += 1; |
|
var hiddenCnt = hiddenCnts[col]; |
|
if (moreLinkConsumesLevel && hiddenCnt === 1 && currentLevel > 0) { |
|
var doomedLevel = currentLevel - 1; |
|
while (visibleColPlacements[col].length > doomedLevel) { |
|
recordHidden(visibleColPlacements[col].pop(), // removes |
|
visibleColPlacements[col].length, // will execute after the pop. will be the index of the removed placement |
|
false); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
// Given the events within an array of segment objects, reslice them to be in a single day |
|
function resliceDaySegs(segs, dayDate, colIndex) { |
|
var dayStart = dayDate; |
|
var dayEnd = addDays(dayStart, 1); |
|
var dayRange = { start: dayStart, end: dayEnd }; |
|
var newSegs = []; |
|
for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { |
|
var seg = segs_2[_i]; |
|
var eventRange = seg.eventRange; |
|
var origRange = eventRange.range; |
|
var slicedRange = intersectRanges(origRange, dayRange); |
|
if (slicedRange) { |
|
newSegs.push(__assign(__assign({}, seg), { firstCol: colIndex, lastCol: colIndex, eventRange: { |
|
def: eventRange.def, |
|
ui: __assign(__assign({}, eventRange.ui), { durationEditable: false }), |
|
instance: eventRange.instance, |
|
range: slicedRange, |
|
}, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() })); |
|
} |
|
} |
|
return newSegs; |
|
} |
|
|
|
var TableRow = /** @class */ (function (_super) { |
|
__extends(TableRow, _super); |
|
function TableRow() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.cellElRefs = new RefMap(); // the <td> |
|
_this.frameElRefs = new RefMap(); // the fc-daygrid-day-frame |
|
_this.fgElRefs = new RefMap(); // the fc-daygrid-day-events |
|
_this.segHarnessRefs = new RefMap(); // indexed by "instanceId:firstCol" |
|
_this.rootElRef = createRef(); |
|
_this.state = { |
|
framePositions: null, |
|
maxContentHeight: null, |
|
segHeights: {}, |
|
}; |
|
return _this; |
|
} |
|
TableRow.prototype.render = function () { |
|
var _this = this; |
|
var _a = this, props = _a.props, state = _a.state, context = _a.context; |
|
var colCnt = props.cells.length; |
|
var businessHoursByCol = splitSegsByFirstCol(props.businessHourSegs, colCnt); |
|
var bgEventSegsByCol = splitSegsByFirstCol(props.bgEventSegs, colCnt); |
|
var highlightSegsByCol = splitSegsByFirstCol(this.getHighlightSegs(), colCnt); |
|
var mirrorSegsByCol = splitSegsByFirstCol(this.getMirrorSegs(), colCnt); |
|
var _b = computeFgSegPlacement(props.cells, props.fgEventSegs, props.dayMaxEvents, props.dayMaxEventRows, state.segHeights, state.maxContentHeight, colCnt, context.options.eventOrder), paddingBottoms = _b.paddingBottoms, segsByFirstCol = _b.segsByFirstCol, segsByEachCol = _b.segsByEachCol, segIsHidden = _b.segIsHidden, segTops = _b.segTops, segMarginTops = _b.segMarginTops, moreCnts = _b.moreCnts, moreTops = _b.moreTops; |
|
var selectedInstanceHash = // TODO: messy way to compute this |
|
(props.eventDrag && props.eventDrag.affectedInstances) || |
|
(props.eventResize && props.eventResize.affectedInstances) || |
|
{}; |
|
return (createElement("tr", { ref: this.rootElRef }, |
|
props.renderIntro && props.renderIntro(), |
|
props.cells.map(function (cell, col) { |
|
var normalFgNodes = _this.renderFgSegs(segsByFirstCol[col], segIsHidden, segTops, segMarginTops, selectedInstanceHash, props.todayRange); |
|
var mirrorFgNodes = _this.renderFgSegs(mirrorSegsByCol[col], {}, segTops, // use same tops as real rendering |
|
{}, {}, props.todayRange, Boolean(props.eventDrag), Boolean(props.eventResize), false); |
|
return (createElement(TableCell, { key: cell.key, elRef: _this.cellElRefs.createRef(cell.key), innerElRef: _this.frameElRefs.createRef(cell.key) /* FF <td> problem, but okay to use for left/right. TODO: rename prop */, dateProfile: props.dateProfile, date: cell.date, showDayNumber: props.showDayNumbers, showWeekNumber: props.showWeekNumbers && col === 0, forceDayTop: props.showWeekNumbers /* even displaying weeknum for row, not necessarily day */, todayRange: props.todayRange, extraHookProps: cell.extraHookProps, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames, moreCnt: moreCnts[col], buildMoreLinkText: props.buildMoreLinkText, onMoreClick: function (arg) { |
|
props.onMoreClick(__assign(__assign({}, arg), { fromCol: col })); |
|
}, segIsHidden: segIsHidden, moreMarginTop: moreTops[col] /* rename */, segsByEachCol: segsByEachCol[col], fgPaddingBottom: paddingBottoms[col], fgContentElRef: _this.fgElRefs.createRef(cell.key), fgContent: ( // Fragment scopes the keys |
|
createElement(Fragment, null, |
|
createElement(Fragment, null, normalFgNodes), |
|
createElement(Fragment, null, mirrorFgNodes))), bgContent: ( // Fragment scopes the keys |
|
createElement(Fragment, null, |
|
_this.renderFillSegs(highlightSegsByCol[col], 'highlight'), |
|
_this.renderFillSegs(businessHoursByCol[col], 'non-business'), |
|
_this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))) })); |
|
}))); |
|
}; |
|
TableRow.prototype.componentDidMount = function () { |
|
this.updateSizing(true); |
|
}; |
|
TableRow.prototype.componentDidUpdate = function (prevProps, prevState) { |
|
var currentProps = this.props; |
|
this.updateSizing(!isPropsEqual(prevProps, currentProps)); |
|
}; |
|
TableRow.prototype.getHighlightSegs = function () { |
|
var props = this.props; |
|
if (props.eventDrag && props.eventDrag.segs.length) { // messy check |
|
return props.eventDrag.segs; |
|
} |
|
if (props.eventResize && props.eventResize.segs.length) { // messy check |
|
return props.eventResize.segs; |
|
} |
|
return props.dateSelectionSegs; |
|
}; |
|
TableRow.prototype.getMirrorSegs = function () { |
|
var props = this.props; |
|
if (props.eventResize && props.eventResize.segs.length) { // messy check |
|
return props.eventResize.segs; |
|
} |
|
return []; |
|
}; |
|
TableRow.prototype.renderFgSegs = function (segs, segIsHidden, // does NOT mean display:hidden |
|
segTops, segMarginTops, selectedInstanceHash, todayRange, isDragging, isResizing, isDateSelecting) { |
|
var context = this.context; |
|
var eventSelection = this.props.eventSelection; |
|
var framePositions = this.state.framePositions; |
|
var defaultDisplayEventEnd = this.props.cells.length === 1; // colCnt === 1 |
|
var nodes = []; |
|
if (framePositions) { |
|
for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { |
|
var seg = segs_1[_i]; |
|
var instanceId = seg.eventRange.instance.instanceId; |
|
var isMirror = isDragging || isResizing || isDateSelecting; |
|
var isSelected = selectedInstanceHash[instanceId]; |
|
var isInvisible = segIsHidden[instanceId] || isSelected; |
|
// TODO: simpler way? NOT DRY |
|
var isAbsolute = segIsHidden[instanceId] || isMirror || seg.firstCol !== seg.lastCol || !seg.isStart || !seg.isEnd; |
|
var marginTop = void 0; |
|
var top_1 = void 0; |
|
var left = void 0; |
|
var right = void 0; |
|
if (isAbsolute) { |
|
top_1 = segTops[instanceId]; |
|
if (context.isRtl) { |
|
right = 0; |
|
left = framePositions.lefts[seg.lastCol] - framePositions.lefts[seg.firstCol]; |
|
} |
|
else { |
|
left = 0; |
|
right = framePositions.rights[seg.firstCol] - framePositions.rights[seg.lastCol]; |
|
} |
|
} |
|
else { |
|
marginTop = segMarginTops[instanceId]; |
|
} |
|
/* |
|
known bug: events that are force to be list-item but span multiple days still take up space in later columns |
|
*/ |
|
nodes.push(createElement("div", { className: 'fc-daygrid-event-harness' + (isAbsolute ? ' fc-daygrid-event-harness-abs' : ''), key: instanceId, |
|
// in print mode when in mult cols, could collide |
|
ref: isMirror ? null : this.segHarnessRefs.createRef(instanceId + ':' + seg.firstCol), style: { |
|
visibility: isInvisible ? 'hidden' : '', |
|
marginTop: marginTop || '', |
|
top: top_1 || '', |
|
left: left || '', |
|
right: right || '', |
|
} }, hasListItemDisplay(seg) ? (createElement(TableListItemEvent, __assign({ seg: seg, isDragging: isDragging, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange)))) : (createElement(TableBlockEvent, __assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange)))))); |
|
} |
|
} |
|
return nodes; |
|
}; |
|
TableRow.prototype.renderFillSegs = function (segs, fillType) { |
|
var isRtl = this.context.isRtl; |
|
var todayRange = this.props.todayRange; |
|
var framePositions = this.state.framePositions; |
|
var nodes = []; |
|
if (framePositions) { |
|
for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { |
|
var seg = segs_2[_i]; |
|
var leftRightCss = isRtl ? { |
|
right: 0, |
|
left: framePositions.lefts[seg.lastCol] - framePositions.lefts[seg.firstCol], |
|
} : { |
|
left: 0, |
|
right: framePositions.rights[seg.firstCol] - framePositions.rights[seg.lastCol], |
|
}; |
|
nodes.push(createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-daygrid-bg-harness", style: leftRightCss }, fillType === 'bg-event' ? |
|
createElement(BgEvent, __assign({ seg: seg }, getSegMeta(seg, todayRange))) : |
|
renderFill(fillType))); |
|
} |
|
} |
|
return createElement.apply(void 0, __spreadArrays([Fragment, {}], nodes)); |
|
}; |
|
TableRow.prototype.updateSizing = function (isExternalSizingChange) { |
|
var _a = this, props = _a.props, frameElRefs = _a.frameElRefs; |
|
if (props.clientWidth !== null) { // positioning ready? |
|
if (isExternalSizingChange) { |
|
var frameEls = props.cells.map(function (cell) { return frameElRefs.currentMap[cell.key]; }); |
|
if (frameEls.length) { |
|
var originEl = this.rootElRef.current; |
|
this.setState({ |
|
framePositions: new PositionCache(originEl, frameEls, true, // isHorizontal |
|
false), |
|
}); |
|
} |
|
} |
|
var limitByContentHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true; |
|
this.setState({ |
|
segHeights: this.computeSegHeights(), |
|
maxContentHeight: limitByContentHeight ? this.computeMaxContentHeight() : null, |
|
}); |
|
} |
|
}; |
|
TableRow.prototype.computeSegHeights = function () { |
|
return mapHash(this.segHarnessRefs.currentMap, function (eventHarnessEl) { return (eventHarnessEl.getBoundingClientRect().height); }); |
|
}; |
|
TableRow.prototype.computeMaxContentHeight = function () { |
|
var firstKey = this.props.cells[0].key; |
|
var cellEl = this.cellElRefs.currentMap[firstKey]; |
|
var fcContainerEl = this.fgElRefs.currentMap[firstKey]; |
|
return cellEl.getBoundingClientRect().bottom - fcContainerEl.getBoundingClientRect().top; |
|
}; |
|
TableRow.prototype.getCellEls = function () { |
|
var elMap = this.cellElRefs.currentMap; |
|
return this.props.cells.map(function (cell) { return elMap[cell.key]; }); |
|
}; |
|
return TableRow; |
|
}(DateComponent)); |
|
TableRow.addPropsEquality({ |
|
onMoreClick: true, |
|
}); |
|
TableRow.addStateEquality({ |
|
segHeights: isPropsEqual, |
|
}); |
|
|
|
var PADDING_FROM_VIEWPORT = 10; |
|
var SCROLL_DEBOUNCE = 10; |
|
var Popover = /** @class */ (function (_super) { |
|
__extends(Popover, _super); |
|
function Popover() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.repositioner = new DelayedRunner(_this.updateSize.bind(_this)); |
|
_this.handleRootEl = function (el) { |
|
_this.rootEl = el; |
|
if (_this.props.elRef) { |
|
setRef(_this.props.elRef, el); |
|
} |
|
}; |
|
// Triggered when the user clicks *anywhere* in the document, for the autoHide feature |
|
_this.handleDocumentMousedown = function (ev) { |
|
var onClose = _this.props.onClose; |
|
// only hide the popover if the click happened outside the popover |
|
if (onClose && !_this.rootEl.contains(ev.target)) { |
|
onClose(); |
|
} |
|
}; |
|
_this.handleDocumentScroll = function () { |
|
_this.repositioner.request(SCROLL_DEBOUNCE); |
|
}; |
|
_this.handleCloseClick = function () { |
|
var onClose = _this.props.onClose; |
|
if (onClose) { |
|
onClose(); |
|
} |
|
}; |
|
return _this; |
|
} |
|
Popover.prototype.render = function () { |
|
var theme = this.context.theme; |
|
var props = this.props; |
|
var classNames = [ |
|
'fc-popover', |
|
theme.getClass('popover'), |
|
].concat(props.extraClassNames || []); |
|
return (createElement("div", __assign({ className: classNames.join(' ') }, props.extraAttrs, { ref: this.handleRootEl }), |
|
createElement("div", { className: 'fc-popover-header ' + theme.getClass('popoverHeader') }, |
|
createElement("span", { className: "fc-popover-title" }, props.title), |
|
createElement("span", { className: 'fc-popover-close ' + theme.getIconClass('close'), onClick: this.handleCloseClick })), |
|
createElement("div", { className: 'fc-popover-body ' + theme.getClass('popoverContent') }, props.children))); |
|
}; |
|
Popover.prototype.componentDidMount = function () { |
|
document.addEventListener('mousedown', this.handleDocumentMousedown); |
|
document.addEventListener('scroll', this.handleDocumentScroll); |
|
this.updateSize(); |
|
}; |
|
Popover.prototype.componentWillUnmount = function () { |
|
document.removeEventListener('mousedown', this.handleDocumentMousedown); |
|
document.removeEventListener('scroll', this.handleDocumentScroll); |
|
}; |
|
// TODO: adjust on window resize |
|
/* |
|
NOTE: the popover is position:fixed, so coordinates are relative to the viewport |
|
NOTE: the PARENT calls this as well, on window resize. we would have wanted to use the repositioner, |
|
but need to ensure that all other components have updated size first (for alignmentEl) |
|
*/ |
|
Popover.prototype.updateSize = function () { |
|
var _a = this.props, alignmentEl = _a.alignmentEl, topAlignmentEl = _a.topAlignmentEl; |
|
var rootEl = this.rootEl; |
|
if (!rootEl) { |
|
return; // not sure why this was null, but we shouldn't let external components call updateSize() anyway |
|
} |
|
var dims = rootEl.getBoundingClientRect(); // only used for width,height |
|
var alignment = alignmentEl.getBoundingClientRect(); |
|
var top = topAlignmentEl ? topAlignmentEl.getBoundingClientRect().top : alignment.top; |
|
top = Math.min(top, window.innerHeight - dims.height - PADDING_FROM_VIEWPORT); |
|
top = Math.max(top, PADDING_FROM_VIEWPORT); |
|
var left; |
|
if (this.context.isRtl) { |
|
left = alignment.right - dims.width; |
|
} |
|
else { |
|
left = alignment.left; |
|
} |
|
left = Math.min(left, window.innerWidth - dims.width - PADDING_FROM_VIEWPORT); |
|
left = Math.max(left, PADDING_FROM_VIEWPORT); |
|
applyStyle(rootEl, { top: top, left: left }); |
|
}; |
|
return Popover; |
|
}(BaseComponent)); |
|
|
|
var MorePopover = /** @class */ (function (_super) { |
|
__extends(MorePopover, _super); |
|
function MorePopover() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.rootElRef = createRef(); |
|
return _this; |
|
} |
|
MorePopover.prototype.render = function () { |
|
var _a = this.context, options = _a.options, dateEnv = _a.dateEnv; |
|
var props = this.props; |
|
var date = props.date, hiddenInstances = props.hiddenInstances, todayRange = props.todayRange, dateProfile = props.dateProfile, selectedInstanceId = props.selectedInstanceId; |
|
var title = dateEnv.format(date, options.dayPopoverFormat); |
|
return (createElement(DayCellRoot, { date: date, dateProfile: dateProfile, todayRange: todayRange, elRef: this.rootElRef }, function (rootElRef, dayClassNames, dataAttrs) { return (createElement(Popover, { elRef: rootElRef, title: title, extraClassNames: ['fc-more-popover'].concat(dayClassNames), extraAttrs: dataAttrs, onClose: props.onCloseClick, alignmentEl: props.alignmentEl, topAlignmentEl: props.topAlignmentEl }, |
|
createElement(DayCellContent, { date: date, dateProfile: dateProfile, todayRange: todayRange }, function (innerElRef, innerContent) { return (innerContent && |
|
createElement("div", { className: "fc-more-popover-misc", ref: innerElRef }, innerContent)); }), |
|
props.segs.map(function (seg) { |
|
var instanceId = seg.eventRange.instance.instanceId; |
|
return (createElement("div", { className: "fc-daygrid-event-harness", key: instanceId, style: { |
|
visibility: hiddenInstances[instanceId] ? 'hidden' : '', |
|
} }, hasListItemDisplay(seg) ? (createElement(TableListItemEvent, __assign({ seg: seg, isDragging: false, isSelected: instanceId === selectedInstanceId, defaultDisplayEventEnd: false }, getSegMeta(seg, todayRange)))) : (createElement(TableBlockEvent, __assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === selectedInstanceId, defaultDisplayEventEnd: false }, getSegMeta(seg, todayRange)))))); |
|
}))); })); |
|
}; |
|
MorePopover.prototype.positionToHit = function (positionLeft, positionTop, originEl) { |
|
var rootEl = this.rootElRef.current; |
|
if (!originEl || !rootEl) { // why? |
|
return null; |
|
} |
|
var originRect = originEl.getBoundingClientRect(); |
|
var elRect = rootEl.getBoundingClientRect(); |
|
var newOriginLeft = elRect.left - originRect.left; |
|
var newOriginTop = elRect.top - originRect.top; |
|
var localLeft = positionLeft - newOriginLeft; |
|
var localTop = positionTop - newOriginTop; |
|
var date = this.props.date; |
|
if ( // ugly way to detect intersection |
|
localLeft >= 0 && localLeft < elRect.width && |
|
localTop >= 0 && localTop < elRect.height) { |
|
return { |
|
dateSpan: { |
|
allDay: true, |
|
range: { start: date, end: addDays(date, 1) }, |
|
}, |
|
dayEl: rootEl, |
|
relativeRect: { |
|
left: newOriginLeft, |
|
top: newOriginTop, |
|
right: elRect.width, |
|
bottom: elRect.height, |
|
}, |
|
layer: 1, |
|
}; |
|
} |
|
return null; |
|
}; |
|
return MorePopover; |
|
}(DateComponent)); |
|
|
|
var Table = /** @class */ (function (_super) { |
|
__extends(Table, _super); |
|
function Table() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.splitBusinessHourSegs = memoize(splitSegsByRow); |
|
_this.splitBgEventSegs = memoize(splitSegsByRow); |
|
_this.splitFgEventSegs = memoize(splitSegsByRow); |
|
_this.splitDateSelectionSegs = memoize(splitSegsByRow); |
|
_this.splitEventDrag = memoize(splitInteractionByRow); |
|
_this.splitEventResize = memoize(splitInteractionByRow); |
|
_this.buildBuildMoreLinkText = memoize(buildBuildMoreLinkText); |
|
_this.morePopoverRef = createRef(); |
|
_this.rowRefs = new RefMap(); |
|
_this.state = { |
|
morePopoverState: null, |
|
}; |
|
_this.handleRootEl = function (rootEl) { |
|
_this.rootEl = rootEl; |
|
setRef(_this.props.elRef, rootEl); |
|
}; |
|
// TODO: bad names "more link click" versus "more click" |
|
_this.handleMoreLinkClick = function (arg) { |
|
var context = _this.context; |
|
var dateEnv = context.dateEnv; |
|
var clickOption = context.options.moreLinkClick; |
|
function segForPublic(seg) { |
|
var _a = seg.eventRange, def = _a.def, instance = _a.instance, range = _a.range; |
|
return { |
|
event: new EventApi(context, def, instance), |
|
start: dateEnv.toDate(range.start), |
|
end: dateEnv.toDate(range.end), |
|
isStart: seg.isStart, |
|
isEnd: seg.isEnd, |
|
}; |
|
} |
|
if (typeof clickOption === 'function') { |
|
clickOption = clickOption({ |
|
date: dateEnv.toDate(arg.date), |
|
allDay: true, |
|
allSegs: arg.allSegs.map(segForPublic), |
|
hiddenSegs: arg.hiddenSegs.map(segForPublic), |
|
jsEvent: arg.ev, |
|
view: context.viewApi, |
|
}); // hack to handle void |
|
} |
|
if (!clickOption || clickOption === 'popover') { |
|
_this.setState({ |
|
morePopoverState: __assign(__assign({}, arg), { currentFgEventSegs: _this.props.fgEventSegs, fromRow: arg.fromRow, fromCol: arg.fromCol }), |
|
}); |
|
} |
|
else if (typeof clickOption === 'string') { // a view name |
|
context.calendarApi.zoomTo(arg.date, clickOption); |
|
} |
|
}; |
|
_this.handleMorePopoverClose = function () { |
|
_this.setState({ |
|
morePopoverState: null, |
|
}); |
|
}; |
|
return _this; |
|
} |
|
Table.prototype.render = function () { |
|
var _this = this; |
|
var props = this.props; |
|
var dateProfile = props.dateProfile, dayMaxEventRows = props.dayMaxEventRows, dayMaxEvents = props.dayMaxEvents, expandRows = props.expandRows; |
|
var morePopoverState = this.state.morePopoverState; |
|
var rowCnt = props.cells.length; |
|
var businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, rowCnt); |
|
var bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, rowCnt); |
|
var fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, rowCnt); |
|
var dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, rowCnt); |
|
var eventDragByRow = this.splitEventDrag(props.eventDrag, rowCnt); |
|
var eventResizeByRow = this.splitEventResize(props.eventResize, rowCnt); |
|
var buildMoreLinkText = this.buildBuildMoreLinkText(this.context.options.moreLinkText); |
|
var limitViaBalanced = dayMaxEvents === true || dayMaxEventRows === true; |
|
// if rows can't expand to fill fixed height, can't do balanced-height event limit |
|
// TODO: best place to normalize these options? |
|
if (limitViaBalanced && !expandRows) { |
|
limitViaBalanced = false; |
|
dayMaxEventRows = null; |
|
dayMaxEvents = null; |
|
} |
|
var classNames = [ |
|
'fc-daygrid-body', |
|
limitViaBalanced ? 'fc-daygrid-body-balanced' : 'fc-daygrid-body-unbalanced', |
|
expandRows ? '' : 'fc-daygrid-body-natural', |
|
]; |
|
return (createElement("div", { className: classNames.join(' '), ref: this.handleRootEl, style: { |
|
// these props are important to give this wrapper correct dimensions for interactions |
|
// TODO: if we set it here, can we avoid giving to inner tables? |
|
width: props.clientWidth, |
|
minWidth: props.tableMinWidth, |
|
} }, |
|
createElement(NowTimer, { unit: "day" }, function (nowDate, todayRange) { return (createElement(Fragment, null, |
|
createElement("table", { className: "fc-scrollgrid-sync-table", style: { |
|
width: props.clientWidth, |
|
minWidth: props.tableMinWidth, |
|
height: expandRows ? props.clientHeight : '', |
|
} }, |
|
props.colGroupNode, |
|
createElement("tbody", null, props.cells.map(function (cells, row) { return (createElement(TableRow, { ref: _this.rowRefs.createRef(row), key: cells.length |
|
? cells[0].date.toISOString() /* best? or put key on cell? or use diff formatter? */ |
|
: row // in case there are no cells (like when resource view is loading) |
|
, showDayNumbers: rowCnt > 1, showWeekNumbers: props.showWeekNumbers, todayRange: todayRange, dateProfile: dateProfile, cells: cells, renderIntro: props.renderRowIntro, businessHourSegs: businessHourSegsByRow[row], eventSelection: props.eventSelection, bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* hack */, fgEventSegs: fgEventSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows, clientWidth: props.clientWidth, clientHeight: props.clientHeight, buildMoreLinkText: buildMoreLinkText, onMoreClick: function (arg) { |
|
_this.handleMoreLinkClick(__assign(__assign({}, arg), { fromRow: row })); |
|
} })); }))), |
|
(!props.forPrint && morePopoverState && morePopoverState.currentFgEventSegs === props.fgEventSegs) && (createElement(MorePopover, { ref: _this.morePopoverRef, date: morePopoverState.date, dateProfile: dateProfile, segs: morePopoverState.allSegs, alignmentEl: morePopoverState.dayEl, topAlignmentEl: rowCnt === 1 ? props.headerAlignElRef.current : null, onCloseClick: _this.handleMorePopoverClose, selectedInstanceId: props.eventSelection, hiddenInstances: // yuck |
|
(props.eventDrag ? props.eventDrag.affectedInstances : null) || |
|
(props.eventResize ? props.eventResize.affectedInstances : null) || |
|
{}, todayRange: todayRange })))); }))); |
|
}; |
|
// Hit System |
|
// ---------------------------------------------------------------------------------------------------- |
|
Table.prototype.prepareHits = function () { |
|
this.rowPositions = new PositionCache(this.rootEl, this.rowRefs.collect().map(function (rowObj) { return rowObj.getCellEls()[0]; }), // first cell el in each row. TODO: not optimal |
|
false, true); |
|
this.colPositions = new PositionCache(this.rootEl, this.rowRefs.currentMap[0].getCellEls(), // cell els in first row |
|
true, // horizontal |
|
false); |
|
}; |
|
Table.prototype.positionToHit = function (leftPosition, topPosition) { |
|
var morePopover = this.morePopoverRef.current; |
|
var morePopoverHit = morePopover ? morePopover.positionToHit(leftPosition, topPosition, this.rootEl) : null; |
|
var morePopoverState = this.state.morePopoverState; |
|
if (morePopoverHit) { |
|
return __assign({ row: morePopoverState.fromRow, col: morePopoverState.fromCol }, morePopoverHit); |
|
} |
|
var _a = this, colPositions = _a.colPositions, rowPositions = _a.rowPositions; |
|
var col = colPositions.leftToIndex(leftPosition); |
|
var row = rowPositions.topToIndex(topPosition); |
|
if (row != null && col != null) { |
|
return { |
|
row: row, |
|
col: col, |
|
dateSpan: { |
|
range: this.getCellRange(row, col), |
|
allDay: true, |
|
}, |
|
dayEl: this.getCellEl(row, col), |
|
relativeRect: { |
|
left: colPositions.lefts[col], |
|
right: colPositions.rights[col], |
|
top: rowPositions.tops[row], |
|
bottom: rowPositions.bottoms[row], |
|
}, |
|
}; |
|
} |
|
return null; |
|
}; |
|
Table.prototype.getCellEl = function (row, col) { |
|
return this.rowRefs.currentMap[row].getCellEls()[col]; // TODO: not optimal |
|
}; |
|
Table.prototype.getCellRange = function (row, col) { |
|
var start = this.props.cells[row][col].date; |
|
var end = addDays(start, 1); |
|
return { start: start, end: end }; |
|
}; |
|
return Table; |
|
}(DateComponent)); |
|
function buildBuildMoreLinkText(moreLinkTextInput) { |
|
if (typeof moreLinkTextInput === 'function') { |
|
return moreLinkTextInput; |
|
} |
|
return function (num) { return "+" + num + " " + moreLinkTextInput; }; |
|
} |
|
function isSegAllDay(seg) { |
|
return seg.eventRange.def.allDay; |
|
} |
|
|
|
var DayTableSlicer = /** @class */ (function (_super) { |
|
__extends(DayTableSlicer, _super); |
|
function DayTableSlicer() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.forceDayIfListItem = true; |
|
return _this; |
|
} |
|
DayTableSlicer.prototype.sliceRange = function (dateRange, dayTableModel) { |
|
return dayTableModel.sliceRange(dateRange); |
|
}; |
|
return DayTableSlicer; |
|
}(Slicer)); |
|
|
|
var DayTable = /** @class */ (function (_super) { |
|
__extends(DayTable, _super); |
|
function DayTable() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.slicer = new DayTableSlicer(); |
|
_this.tableRef = createRef(); |
|
_this.handleRootEl = function (rootEl) { |
|
if (rootEl) { |
|
_this.context.registerInteractiveComponent(_this, { el: rootEl }); |
|
} |
|
else { |
|
_this.context.unregisterInteractiveComponent(_this); |
|
} |
|
}; |
|
return _this; |
|
} |
|
DayTable.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
return (createElement(Table, __assign({ ref: this.tableRef, elRef: this.handleRootEl }, this.slicer.sliceProps(props, props.dateProfile, props.nextDayThreshold, context, props.dayTableModel), { dateProfile: props.dateProfile, cells: props.dayTableModel.cells, colGroupNode: props.colGroupNode, tableMinWidth: props.tableMinWidth, renderRowIntro: props.renderRowIntro, dayMaxEvents: props.dayMaxEvents, dayMaxEventRows: props.dayMaxEventRows, showWeekNumbers: props.showWeekNumbers, expandRows: props.expandRows, headerAlignElRef: props.headerAlignElRef, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: props.forPrint }))); |
|
}; |
|
DayTable.prototype.prepareHits = function () { |
|
this.tableRef.current.prepareHits(); |
|
}; |
|
DayTable.prototype.queryHit = function (positionLeft, positionTop) { |
|
var rawHit = this.tableRef.current.positionToHit(positionLeft, positionTop); |
|
if (rawHit) { |
|
return { |
|
component: this, |
|
dateSpan: rawHit.dateSpan, |
|
dayEl: rawHit.dayEl, |
|
rect: { |
|
left: rawHit.relativeRect.left, |
|
right: rawHit.relativeRect.right, |
|
top: rawHit.relativeRect.top, |
|
bottom: rawHit.relativeRect.bottom, |
|
}, |
|
layer: 0, |
|
}; |
|
} |
|
return null; |
|
}; |
|
return DayTable; |
|
}(DateComponent)); |
|
|
|
var DayTableView = /** @class */ (function (_super) { |
|
__extends(DayTableView, _super); |
|
function DayTableView() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.buildDayTableModel = memoize(buildDayTableModel); |
|
_this.headerRef = createRef(); |
|
_this.tableRef = createRef(); |
|
return _this; |
|
} |
|
DayTableView.prototype.render = function () { |
|
var _this = this; |
|
var _a = this.context, options = _a.options, dateProfileGenerator = _a.dateProfileGenerator; |
|
var props = this.props; |
|
var dayTableModel = this.buildDayTableModel(props.dateProfile, dateProfileGenerator); |
|
var headerContent = options.dayHeaders && (createElement(DayHeader, { ref: this.headerRef, dateProfile: props.dateProfile, dates: dayTableModel.headerDates, datesRepDistinctDays: dayTableModel.rowCnt === 1 })); |
|
var bodyContent = function (contentArg) { return (createElement(DayTable, { ref: _this.tableRef, dateProfile: props.dateProfile, dayTableModel: dayTableModel, businessHours: props.businessHours, dateSelection: props.dateSelection, eventStore: props.eventStore, eventUiBases: props.eventUiBases, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, nextDayThreshold: options.nextDayThreshold, colGroupNode: contentArg.tableColGroupNode, tableMinWidth: contentArg.tableMinWidth, dayMaxEvents: options.dayMaxEvents, dayMaxEventRows: options.dayMaxEventRows, showWeekNumbers: options.weekNumbers, expandRows: !props.isHeightAuto, headerAlignElRef: _this.headerElRef, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, forPrint: props.forPrint })); }; |
|
return options.dayMinWidth |
|
? this.renderHScrollLayout(headerContent, bodyContent, dayTableModel.colCnt, options.dayMinWidth) |
|
: this.renderSimpleLayout(headerContent, bodyContent); |
|
}; |
|
return DayTableView; |
|
}(TableView)); |
|
function buildDayTableModel(dateProfile, dateProfileGenerator) { |
|
var daySeries = new DaySeriesModel(dateProfile.renderRange, dateProfileGenerator); |
|
return new DayTableModel(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit)); |
|
} |
|
|
|
var TableDateProfileGenerator = /** @class */ (function (_super) { |
|
__extends(TableDateProfileGenerator, _super); |
|
function TableDateProfileGenerator() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
// Computes the date range that will be rendered. |
|
TableDateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) { |
|
var dateEnv = this.props.dateEnv; |
|
var renderRange = _super.prototype.buildRenderRange.call(this, currentRange, currentRangeUnit, isRangeAllDay); |
|
var start = renderRange.start; |
|
var end = renderRange.end; |
|
var endOfWeek; |
|
// year and month views should be aligned with weeks. this is already done for week |
|
if (/^(year|month)$/.test(currentRangeUnit)) { |
|
start = dateEnv.startOfWeek(start); |
|
// make end-of-week if not already |
|
endOfWeek = dateEnv.startOfWeek(end); |
|
if (endOfWeek.valueOf() !== end.valueOf()) { |
|
end = addWeeks(endOfWeek, 1); |
|
} |
|
} |
|
// ensure 6 weeks |
|
if (this.props.monthMode && |
|
this.props.fixedWeekCount) { |
|
var rowCnt = Math.ceil(// could be partial weeks due to hiddenDays |
|
diffWeeks(start, end)); |
|
end = addWeeks(end, 6 - rowCnt); |
|
} |
|
return { start: start, end: end }; |
|
}; |
|
return TableDateProfileGenerator; |
|
}(DateProfileGenerator)); |
|
|
|
var OPTION_REFINERS$1 = { |
|
moreLinkClick: identity, |
|
moreLinkClassNames: identity, |
|
moreLinkContent: identity, |
|
moreLinkDidMount: identity, |
|
moreLinkWillUnmount: identity, |
|
}; |
|
|
|
var dayGridPlugin = createPlugin({ |
|
initialView: 'dayGridMonth', |
|
optionRefiners: OPTION_REFINERS$1, |
|
views: { |
|
dayGrid: { |
|
component: DayTableView, |
|
dateProfileGeneratorClass: TableDateProfileGenerator, |
|
}, |
|
dayGridDay: { |
|
type: 'dayGrid', |
|
duration: { days: 1 }, |
|
}, |
|
dayGridWeek: { |
|
type: 'dayGrid', |
|
duration: { weeks: 1 }, |
|
}, |
|
dayGridMonth: { |
|
type: 'dayGrid', |
|
duration: { months: 1 }, |
|
monthMode: true, |
|
fixedWeekCount: true, |
|
}, |
|
}, |
|
}); |
|
|
|
var AllDaySplitter = /** @class */ (function (_super) { |
|
__extends(AllDaySplitter, _super); |
|
function AllDaySplitter() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
AllDaySplitter.prototype.getKeyInfo = function () { |
|
return { |
|
allDay: {}, |
|
timed: {}, |
|
}; |
|
}; |
|
AllDaySplitter.prototype.getKeysForDateSpan = function (dateSpan) { |
|
if (dateSpan.allDay) { |
|
return ['allDay']; |
|
} |
|
return ['timed']; |
|
}; |
|
AllDaySplitter.prototype.getKeysForEventDef = function (eventDef) { |
|
if (!eventDef.allDay) { |
|
return ['timed']; |
|
} |
|
if (hasBgRendering(eventDef)) { |
|
return ['timed', 'allDay']; |
|
} |
|
return ['allDay']; |
|
}; |
|
return AllDaySplitter; |
|
}(Splitter)); |
|
|
|
var DEFAULT_SLAT_LABEL_FORMAT = createFormatter({ |
|
hour: 'numeric', |
|
minute: '2-digit', |
|
omitZeroMinute: true, |
|
meridiem: 'short', |
|
}); |
|
function TimeColsAxisCell(props) { |
|
var classNames = [ |
|
'fc-timegrid-slot', |
|
'fc-timegrid-slot-label', |
|
props.isLabeled ? 'fc-scrollgrid-shrink' : 'fc-timegrid-slot-minor', |
|
]; |
|
return (createElement(ViewContextType.Consumer, null, function (context) { |
|
if (!props.isLabeled) { |
|
return (createElement("td", { className: classNames.join(' '), "data-time": props.isoTimeStr })); |
|
} |
|
var dateEnv = context.dateEnv, options = context.options, viewApi = context.viewApi; |
|
var labelFormat = // TODO: fully pre-parse |
|
options.slotLabelFormat == null ? DEFAULT_SLAT_LABEL_FORMAT : |
|
Array.isArray(options.slotLabelFormat) ? createFormatter(options.slotLabelFormat[0]) : |
|
createFormatter(options.slotLabelFormat); |
|
var hookProps = { |
|
level: 0, |
|
time: props.time, |
|
date: dateEnv.toDate(props.date), |
|
view: viewApi, |
|
text: dateEnv.format(props.date, labelFormat), |
|
}; |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.slotLabelClassNames, content: options.slotLabelContent, defaultContent: renderInnerContent$3, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("td", { ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-time": props.isoTimeStr }, |
|
createElement("div", { className: "fc-timegrid-slot-label-frame fc-scrollgrid-shrink-frame" }, |
|
createElement("div", { className: "fc-timegrid-slot-label-cushion fc-scrollgrid-shrink-cushion", ref: innerElRef }, innerContent)))); })); |
|
})); |
|
} |
|
function renderInnerContent$3(props) { |
|
return props.text; |
|
} |
|
|
|
var TimeBodyAxis = /** @class */ (function (_super) { |
|
__extends(TimeBodyAxis, _super); |
|
function TimeBodyAxis() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TimeBodyAxis.prototype.render = function () { |
|
return this.props.slatMetas.map(function (slatMeta) { return (createElement("tr", { key: slatMeta.key }, |
|
createElement(TimeColsAxisCell, __assign({}, slatMeta)))); }); |
|
}; |
|
return TimeBodyAxis; |
|
}(BaseComponent)); |
|
|
|
var DEFAULT_WEEK_NUM_FORMAT$1 = createFormatter({ week: 'short' }); |
|
var AUTO_ALL_DAY_MAX_EVENT_ROWS = 5; |
|
var TimeColsView = /** @class */ (function (_super) { |
|
__extends(TimeColsView, _super); |
|
function TimeColsView() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.allDaySplitter = new AllDaySplitter(); // for use by subclasses |
|
_this.headerElRef = createRef(); |
|
_this.rootElRef = createRef(); |
|
_this.scrollerElRef = createRef(); |
|
_this.state = { |
|
slatCoords: null, |
|
}; |
|
_this.handleScrollTopRequest = function (scrollTop) { |
|
var scrollerEl = _this.scrollerElRef.current; |
|
if (scrollerEl) { // TODO: not sure how this could ever be null. weirdness with the reducer |
|
scrollerEl.scrollTop = scrollTop; |
|
} |
|
}; |
|
/* Header Render Methods |
|
------------------------------------------------------------------------------------------------------------------*/ |
|
_this.renderHeadAxis = function (rowKey, frameHeight) { |
|
if (frameHeight === void 0) { frameHeight = ''; } |
|
var options = _this.context.options; |
|
var dateProfile = _this.props.dateProfile; |
|
var range = dateProfile.renderRange; |
|
var dayCnt = diffDays(range.start, range.end); |
|
var navLinkAttrs = (options.navLinks && dayCnt === 1) // only do in day views (to avoid doing in week views that dont need it) |
|
? { 'data-navlink': buildNavLinkData(range.start, 'week'), tabIndex: 0 } |
|
: {}; |
|
if (options.weekNumbers && rowKey === 'day') { |
|
return (createElement(WeekNumberRoot, { date: range.start, defaultFormat: DEFAULT_WEEK_NUM_FORMAT$1 }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("th", { ref: rootElRef, className: [ |
|
'fc-timegrid-axis', |
|
'fc-scrollgrid-shrink', |
|
].concat(classNames).join(' ') }, |
|
createElement("div", { className: "fc-timegrid-axis-frame fc-scrollgrid-shrink-frame fc-timegrid-axis-frame-liquid", style: { height: frameHeight } }, |
|
createElement("a", __assign({ ref: innerElRef, className: "fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner" }, navLinkAttrs), innerContent)))); })); |
|
} |
|
return (createElement("th", { className: "fc-timegrid-axis" }, |
|
createElement("div", { className: "fc-timegrid-axis-frame", style: { height: frameHeight } }))); |
|
}; |
|
/* Table Component Render Methods |
|
------------------------------------------------------------------------------------------------------------------*/ |
|
// only a one-way height sync. we don't send the axis inner-content height to the DayGrid, |
|
// but DayGrid still needs to have classNames on inner elements in order to measure. |
|
_this.renderTableRowAxis = function (rowHeight) { |
|
var _a = _this.context, options = _a.options, viewApi = _a.viewApi; |
|
var hookProps = { |
|
text: options.allDayText, |
|
view: viewApi, |
|
}; |
|
return ( |
|
// TODO: make reusable hook. used in list view too |
|
createElement(RenderHook, { hookProps: hookProps, classNames: options.allDayClassNames, content: options.allDayContent, defaultContent: renderAllDayInner, didMount: options.allDayDidMount, willUnmount: options.allDayWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("td", { ref: rootElRef, className: [ |
|
'fc-timegrid-axis', |
|
'fc-scrollgrid-shrink', |
|
].concat(classNames).join(' ') }, |
|
createElement("div", { className: 'fc-timegrid-axis-frame fc-scrollgrid-shrink-frame' + (rowHeight == null ? ' fc-timegrid-axis-frame-liquid' : ''), style: { height: rowHeight } }, |
|
createElement("span", { className: "fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner", ref: innerElRef }, innerContent)))); })); |
|
}; |
|
_this.handleSlatCoords = function (slatCoords) { |
|
_this.setState({ slatCoords: slatCoords }); |
|
}; |
|
return _this; |
|
} |
|
// rendering |
|
// ---------------------------------------------------------------------------------------------------- |
|
TimeColsView.prototype.renderSimpleLayout = function (headerRowContent, allDayContent, timeContent) { |
|
var _a = this, context = _a.context, props = _a.props; |
|
var sections = []; |
|
var stickyHeaderDates = getStickyHeaderDates(context.options); |
|
if (headerRowContent) { |
|
sections.push({ |
|
type: 'header', |
|
key: 'header', |
|
isSticky: stickyHeaderDates, |
|
chunk: { |
|
elRef: this.headerElRef, |
|
tableClassName: 'fc-col-header', |
|
rowContent: headerRowContent, |
|
}, |
|
}); |
|
} |
|
if (allDayContent) { |
|
sections.push({ |
|
type: 'body', |
|
key: 'all-day', |
|
chunk: { content: allDayContent }, |
|
}); |
|
sections.push({ |
|
type: 'body', |
|
key: 'all-day-divider', |
|
outerContent: ( // TODO: rename to cellContent so don't need to define <tr>? |
|
createElement("tr", { className: "fc-scrollgrid-section" }, |
|
createElement("td", { className: 'fc-timegrid-divider ' + context.theme.getClass('tableCellShaded') }))), |
|
}); |
|
} |
|
sections.push({ |
|
type: 'body', |
|
key: 'body', |
|
liquid: true, |
|
expandRows: Boolean(context.options.expandRows), |
|
chunk: { |
|
scrollerElRef: this.scrollerElRef, |
|
content: timeContent, |
|
}, |
|
}); |
|
return (createElement(ViewRoot, { viewSpec: context.viewSpec, elRef: this.rootElRef }, function (rootElRef, classNames) { return (createElement("div", { className: ['fc-timegrid'].concat(classNames).join(' '), ref: rootElRef }, |
|
createElement(SimpleScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, cols: [{ width: 'shrink' }], sections: sections }))); })); |
|
}; |
|
TimeColsView.prototype.renderHScrollLayout = function (headerRowContent, allDayContent, timeContent, colCnt, dayMinWidth, slatMetas, slatCoords) { |
|
var _this = this; |
|
var ScrollGrid = this.context.pluginHooks.scrollGridImpl; |
|
if (!ScrollGrid) { |
|
throw new Error('No ScrollGrid implementation'); |
|
} |
|
var _a = this, context = _a.context, props = _a.props; |
|
var stickyHeaderDates = !props.forPrint && getStickyHeaderDates(context.options); |
|
var stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(context.options); |
|
var sections = []; |
|
if (headerRowContent) { |
|
sections.push({ |
|
type: 'header', |
|
key: 'header', |
|
isSticky: stickyHeaderDates, |
|
syncRowHeights: true, |
|
chunks: [ |
|
{ |
|
key: 'axis', |
|
rowContent: function (arg) { return (createElement("tr", null, _this.renderHeadAxis('day', arg.rowSyncHeights[0]))); }, |
|
}, |
|
{ |
|
key: 'cols', |
|
elRef: this.headerElRef, |
|
tableClassName: 'fc-col-header', |
|
rowContent: headerRowContent, |
|
}, |
|
], |
|
}); |
|
} |
|
if (allDayContent) { |
|
sections.push({ |
|
type: 'body', |
|
key: 'all-day', |
|
syncRowHeights: true, |
|
chunks: [ |
|
{ |
|
key: 'axis', |
|
rowContent: function (contentArg) { return (createElement("tr", null, _this.renderTableRowAxis(contentArg.rowSyncHeights[0]))); }, |
|
}, |
|
{ |
|
key: 'cols', |
|
content: allDayContent, |
|
}, |
|
], |
|
}); |
|
sections.push({ |
|
key: 'all-day-divider', |
|
type: 'body', |
|
outerContent: ( // TODO: rename to cellContent so don't need to define <tr>? |
|
createElement("tr", { className: "fc-scrollgrid-section" }, |
|
createElement("td", { colSpan: 2, className: 'fc-timegrid-divider ' + context.theme.getClass('tableCellShaded') }))), |
|
}); |
|
} |
|
var isNowIndicator = context.options.nowIndicator; |
|
sections.push({ |
|
type: 'body', |
|
key: 'body', |
|
liquid: true, |
|
expandRows: Boolean(context.options.expandRows), |
|
chunks: [ |
|
{ |
|
key: 'axis', |
|
content: function (arg) { return ( |
|
// TODO: make this now-indicator arrow more DRY with TimeColsContent |
|
createElement("div", { className: "fc-timegrid-axis-chunk" }, |
|
createElement("table", { style: { height: arg.expandRows ? arg.clientHeight : '' } }, |
|
arg.tableColGroupNode, |
|
createElement("tbody", null, |
|
createElement(TimeBodyAxis, { slatMetas: slatMetas }))), |
|
createElement("div", { className: "fc-timegrid-now-indicator-container" }, |
|
createElement(NowTimer, { unit: isNowIndicator ? 'minute' : 'day' /* hacky */ }, function (nowDate) { |
|
var nowIndicatorTop = isNowIndicator && |
|
slatCoords && |
|
slatCoords.safeComputeTop(nowDate); // might return void |
|
if (typeof nowIndicatorTop === 'number') { |
|
return (createElement(NowIndicatorRoot, { isAxis: true, date: nowDate }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("div", { ref: rootElRef, className: ['fc-timegrid-now-indicator-arrow'].concat(classNames).join(' '), style: { top: nowIndicatorTop } }, innerContent)); })); |
|
} |
|
return null; |
|
})))); }, |
|
}, |
|
{ |
|
key: 'cols', |
|
scrollerElRef: this.scrollerElRef, |
|
content: timeContent, |
|
}, |
|
], |
|
}); |
|
if (stickyFooterScrollbar) { |
|
sections.push({ |
|
key: 'footer', |
|
type: 'footer', |
|
isSticky: true, |
|
chunks: [ |
|
{ |
|
key: 'axis', |
|
content: renderScrollShim, |
|
}, |
|
{ |
|
key: 'cols', |
|
content: renderScrollShim, |
|
}, |
|
], |
|
}); |
|
} |
|
return (createElement(ViewRoot, { viewSpec: context.viewSpec, elRef: this.rootElRef }, function (rootElRef, classNames) { return (createElement("div", { className: ['fc-timegrid'].concat(classNames).join(' '), ref: rootElRef }, |
|
createElement(ScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, colGroups: [ |
|
{ width: 'shrink', cols: [{ width: 'shrink' }] }, |
|
{ cols: [{ span: colCnt, minWidth: dayMinWidth }] }, |
|
], sections: sections }))); })); |
|
}; |
|
/* Dimensions |
|
------------------------------------------------------------------------------------------------------------------*/ |
|
TimeColsView.prototype.getAllDayMaxEventProps = function () { |
|
var _a = this.context.options, dayMaxEvents = _a.dayMaxEvents, dayMaxEventRows = _a.dayMaxEventRows; |
|
if (dayMaxEvents === true || dayMaxEventRows === true) { // is auto? |
|
dayMaxEvents = undefined; |
|
dayMaxEventRows = AUTO_ALL_DAY_MAX_EVENT_ROWS; // make sure "auto" goes to a real number |
|
} |
|
return { dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows }; |
|
}; |
|
return TimeColsView; |
|
}(DateComponent)); |
|
function renderAllDayInner(hookProps) { |
|
return hookProps.text; |
|
} |
|
|
|
var TimeColsSlatsCoords = /** @class */ (function () { |
|
function TimeColsSlatsCoords(positions, dateProfile, slotDuration) { |
|
this.positions = positions; |
|
this.dateProfile = dateProfile; |
|
this.slotDuration = slotDuration; |
|
} |
|
TimeColsSlatsCoords.prototype.safeComputeTop = function (date) { |
|
var dateProfile = this.dateProfile; |
|
if (rangeContainsMarker(dateProfile.currentRange, date)) { |
|
var startOfDayDate = startOfDay(date); |
|
var timeMs = date.valueOf() - startOfDayDate.valueOf(); |
|
if (timeMs >= asRoughMs(dateProfile.slotMinTime) && |
|
timeMs < asRoughMs(dateProfile.slotMaxTime)) { |
|
return this.computeTimeTop(createDuration(timeMs)); |
|
} |
|
} |
|
return null; |
|
}; |
|
// Computes the top coordinate, relative to the bounds of the grid, of the given date. |
|
// A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight. |
|
TimeColsSlatsCoords.prototype.computeDateTop = function (when, startOfDayDate) { |
|
if (!startOfDayDate) { |
|
startOfDayDate = startOfDay(when); |
|
} |
|
return this.computeTimeTop(createDuration(when.valueOf() - startOfDayDate.valueOf())); |
|
}; |
|
// Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration). |
|
// This is a makeshify way to compute the time-top. Assumes all slatMetas dates are uniform. |
|
// Eventually allow computation with arbirary slat dates. |
|
TimeColsSlatsCoords.prototype.computeTimeTop = function (duration) { |
|
var _a = this, positions = _a.positions, dateProfile = _a.dateProfile; |
|
var len = positions.els.length; |
|
// floating-point value of # of slots covered |
|
var slatCoverage = (duration.milliseconds - asRoughMs(dateProfile.slotMinTime)) / asRoughMs(this.slotDuration); |
|
var slatIndex; |
|
var slatRemainder; |
|
// compute a floating-point number for how many slats should be progressed through. |
|
// from 0 to number of slats (inclusive) |
|
// constrained because slotMinTime/slotMaxTime might be customized. |
|
slatCoverage = Math.max(0, slatCoverage); |
|
slatCoverage = Math.min(len, slatCoverage); |
|
// an integer index of the furthest whole slat |
|
// from 0 to number slats (*exclusive*, so len-1) |
|
slatIndex = Math.floor(slatCoverage); |
|
slatIndex = Math.min(slatIndex, len - 1); |
|
// how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition. |
|
// could be 1.0 if slatCoverage is covering *all* the slots |
|
slatRemainder = slatCoverage - slatIndex; |
|
return positions.tops[slatIndex] + |
|
positions.getHeight(slatIndex) * slatRemainder; |
|
}; |
|
return TimeColsSlatsCoords; |
|
}()); |
|
|
|
var TimeColsSlatsBody = /** @class */ (function (_super) { |
|
__extends(TimeColsSlatsBody, _super); |
|
function TimeColsSlatsBody() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TimeColsSlatsBody.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var options = context.options; |
|
var slatElRefs = props.slatElRefs; |
|
return (createElement("tbody", null, props.slatMetas.map(function (slatMeta, i) { |
|
var hookProps = { |
|
time: slatMeta.time, |
|
date: context.dateEnv.toDate(slatMeta.date), |
|
view: context.viewApi, |
|
}; |
|
var classNames = [ |
|
'fc-timegrid-slot', |
|
'fc-timegrid-slot-lane', |
|
slatMeta.isLabeled ? '' : 'fc-timegrid-slot-minor', |
|
]; |
|
return (createElement("tr", { key: slatMeta.key, ref: slatElRefs.createRef(slatMeta.key) }, |
|
props.axis && (createElement(TimeColsAxisCell, __assign({}, slatMeta))), |
|
createElement(RenderHook, { hookProps: hookProps, classNames: options.slotLaneClassNames, content: options.slotLaneContent, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("td", { ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-time": slatMeta.isoTimeStr }, innerContent)); }))); |
|
}))); |
|
}; |
|
return TimeColsSlatsBody; |
|
}(BaseComponent)); |
|
|
|
/* |
|
for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL. |
|
*/ |
|
var TimeColsSlats = /** @class */ (function (_super) { |
|
__extends(TimeColsSlats, _super); |
|
function TimeColsSlats() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.rootElRef = createRef(); |
|
_this.slatElRefs = new RefMap(); |
|
return _this; |
|
} |
|
TimeColsSlats.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
return (createElement("div", { className: "fc-timegrid-slots", ref: this.rootElRef }, |
|
createElement("table", { className: context.theme.getClass('table'), style: { |
|
minWidth: props.tableMinWidth, |
|
width: props.clientWidth, |
|
height: props.minHeight, |
|
} }, |
|
props.tableColGroupNode /* relies on there only being a single <col> for the axis */, |
|
createElement(TimeColsSlatsBody, { slatElRefs: this.slatElRefs, axis: props.axis, slatMetas: props.slatMetas })))); |
|
}; |
|
TimeColsSlats.prototype.componentDidMount = function () { |
|
this.updateSizing(); |
|
}; |
|
TimeColsSlats.prototype.componentDidUpdate = function () { |
|
this.updateSizing(); |
|
}; |
|
TimeColsSlats.prototype.componentWillUnmount = function () { |
|
if (this.props.onCoords) { |
|
this.props.onCoords(null); |
|
} |
|
}; |
|
TimeColsSlats.prototype.updateSizing = function () { |
|
var _a = this, context = _a.context, props = _a.props; |
|
if (props.onCoords && |
|
props.clientWidth !== null // means sizing has stabilized |
|
) { |
|
var rootEl = this.rootElRef.current; |
|
if (rootEl.offsetHeight) { // not hidden by css |
|
props.onCoords(new TimeColsSlatsCoords(new PositionCache(this.rootElRef.current, collectSlatEls(this.slatElRefs.currentMap, props.slatMetas), false, true), this.props.dateProfile, context.options.slotDuration)); |
|
} |
|
} |
|
}; |
|
return TimeColsSlats; |
|
}(BaseComponent)); |
|
function collectSlatEls(elMap, slatMetas) { |
|
return slatMetas.map(function (slatMeta) { return elMap[slatMeta.key]; }); |
|
} |
|
|
|
function splitSegsByCol(segs, colCnt) { |
|
var segsByCol = []; |
|
var i; |
|
for (i = 0; i < colCnt; i += 1) { |
|
segsByCol.push([]); |
|
} |
|
if (segs) { |
|
for (i = 0; i < segs.length; i += 1) { |
|
segsByCol[segs[i].col].push(segs[i]); |
|
} |
|
} |
|
return segsByCol; |
|
} |
|
function splitInteractionByCol(ui, colCnt) { |
|
var byRow = []; |
|
if (!ui) { |
|
for (var i = 0; i < colCnt; i += 1) { |
|
byRow[i] = null; |
|
} |
|
} |
|
else { |
|
for (var i = 0; i < colCnt; i += 1) { |
|
byRow[i] = { |
|
affectedInstances: ui.affectedInstances, |
|
isEvent: ui.isEvent, |
|
segs: [], |
|
}; |
|
} |
|
for (var _i = 0, _a = ui.segs; _i < _a.length; _i++) { |
|
var seg = _a[_i]; |
|
byRow[seg.col].segs.push(seg); |
|
} |
|
} |
|
return byRow; |
|
} |
|
|
|
// UNFORTUNATELY, assigns results to the top/bottom/level/forwardCoord/backwardCoord props of the actual segs. |
|
// TODO: return hash (by instanceId) of results |
|
function computeSegCoords(segs, dayDate, slatCoords, eventMinHeight, eventOrderSpecs) { |
|
computeSegVerticals(segs, dayDate, slatCoords, eventMinHeight); |
|
return computeSegHorizontals(segs, eventOrderSpecs); // requires top/bottom from computeSegVerticals |
|
} |
|
// For each segment in an array, computes and assigns its top and bottom properties |
|
function computeSegVerticals(segs, dayDate, slatCoords, eventMinHeight) { |
|
for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { |
|
var seg = segs_1[_i]; |
|
seg.top = slatCoords.computeDateTop(seg.start, dayDate); |
|
seg.bottom = Math.max(seg.top + (eventMinHeight || 0), // yuck |
|
slatCoords.computeDateTop(seg.end, dayDate)); |
|
} |
|
} |
|
// Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each. |
|
// Assumed the segs are already ordered. |
|
// NOTE: Also reorders the given array by date! |
|
function computeSegHorizontals(segs, eventOrderSpecs) { |
|
// IMPORTANT TO CLEAR OLD RESULTS :( |
|
for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { |
|
var seg = segs_2[_i]; |
|
seg.level = null; |
|
seg.forwardCoord = null; |
|
seg.backwardCoord = null; |
|
seg.forwardPressure = null; |
|
} |
|
segs = sortEventSegs(segs, eventOrderSpecs); |
|
var level0; |
|
var levels = buildSlotSegLevels(segs); |
|
computeForwardSlotSegs(levels); |
|
if ((level0 = levels[0])) { |
|
for (var _a = 0, level0_1 = level0; _a < level0_1.length; _a++) { |
|
var seg = level0_1[_a]; |
|
computeSlotSegPressures(seg); |
|
} |
|
for (var _b = 0, level0_2 = level0; _b < level0_2.length; _b++) { |
|
var seg = level0_2[_b]; |
|
computeSegForwardBack(seg, 0, 0, eventOrderSpecs); |
|
} |
|
} |
|
return segs; |
|
} |
|
// Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is |
|
// left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date. |
|
function buildSlotSegLevels(segs) { |
|
var levels = []; |
|
var i; |
|
var seg; |
|
var j; |
|
for (i = 0; i < segs.length; i += 1) { |
|
seg = segs[i]; |
|
// go through all the levels and stop on the first level where there are no collisions |
|
for (j = 0; j < levels.length; j += 1) { |
|
if (!computeSlotSegCollisions(seg, levels[j]).length) { |
|
break; |
|
} |
|
} |
|
seg.level = j; |
|
(levels[j] || (levels[j] = [])).push(seg); |
|
} |
|
return levels; |
|
} |
|
// Find all the segments in `otherSegs` that vertically collide with `seg`. |
|
// Append into an optionally-supplied `results` array and return. |
|
function computeSlotSegCollisions(seg, otherSegs, results) { |
|
if (results === void 0) { results = []; } |
|
for (var i = 0; i < otherSegs.length; i += 1) { |
|
if (isSlotSegCollision(seg, otherSegs[i])) { |
|
results.push(otherSegs[i]); |
|
} |
|
} |
|
return results; |
|
} |
|
// Do these segments occupy the same vertical space? |
|
function isSlotSegCollision(seg1, seg2) { |
|
return seg1.bottom > seg2.top && seg1.top < seg2.bottom; |
|
} |
|
// For every segment, figure out the other segments that are in subsequent |
|
// levels that also occupy the same vertical space. Accumulate in seg.forwardSegs |
|
function computeForwardSlotSegs(levels) { |
|
var i; |
|
var level; |
|
var j; |
|
var seg; |
|
var k; |
|
for (i = 0; i < levels.length; i += 1) { |
|
level = levels[i]; |
|
for (j = 0; j < level.length; j += 1) { |
|
seg = level[j]; |
|
seg.forwardSegs = []; |
|
for (k = i + 1; k < levels.length; k += 1) { |
|
computeSlotSegCollisions(seg, levels[k], seg.forwardSegs); |
|
} |
|
} |
|
} |
|
} |
|
// Figure out which path forward (via seg.forwardSegs) results in the longest path until |
|
// the furthest edge is reached. The number of segments in this path will be seg.forwardPressure |
|
function computeSlotSegPressures(seg) { |
|
var forwardSegs = seg.forwardSegs; |
|
var forwardPressure = 0; |
|
var i; |
|
var forwardSeg; |
|
if (seg.forwardPressure == null) { // not already computed |
|
for (i = 0; i < forwardSegs.length; i += 1) { |
|
forwardSeg = forwardSegs[i]; |
|
// figure out the child's maximum forward path |
|
computeSlotSegPressures(forwardSeg); |
|
// either use the existing maximum, or use the child's forward pressure |
|
// plus one (for the forwardSeg itself) |
|
forwardPressure = Math.max(forwardPressure, 1 + forwardSeg.forwardPressure); |
|
} |
|
seg.forwardPressure = forwardPressure; |
|
} |
|
} |
|
// Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range |
|
// from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and |
|
// seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left. |
|
// |
|
// The segment might be part of a "series", which means consecutive segments with the same pressure |
|
// who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of |
|
// segments behind this one in the current series, and `seriesBackwardCoord` is the starting |
|
// coordinate of the first segment in the series. |
|
function computeSegForwardBack(seg, seriesBackwardPressure, seriesBackwardCoord, eventOrderSpecs) { |
|
var forwardSegs = seg.forwardSegs; |
|
var i; |
|
if (seg.forwardCoord == null) { // not already computed |
|
if (!forwardSegs.length) { |
|
// if there are no forward segments, this segment should butt up against the edge |
|
seg.forwardCoord = 1; |
|
} |
|
else { |
|
// sort highest pressure first |
|
sortForwardSegs(forwardSegs, eventOrderSpecs); |
|
// this segment's forwardCoord will be calculated from the backwardCoord of the |
|
// highest-pressure forward segment. |
|
computeSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord, eventOrderSpecs); |
|
seg.forwardCoord = forwardSegs[0].backwardCoord; |
|
} |
|
// calculate the backwardCoord from the forwardCoord. consider the series |
|
seg.backwardCoord = seg.forwardCoord - |
|
(seg.forwardCoord - seriesBackwardCoord) / // available width for series |
|
(seriesBackwardPressure + 1); // # of segments in the series |
|
// use this segment's coordinates to computed the coordinates of the less-pressurized |
|
// forward segments |
|
for (i = 0; i < forwardSegs.length; i += 1) { |
|
computeSegForwardBack(forwardSegs[i], 0, seg.forwardCoord, eventOrderSpecs); |
|
} |
|
} |
|
} |
|
function sortForwardSegs(forwardSegs, eventOrderSpecs) { |
|
var objs = forwardSegs.map(buildTimeGridSegCompareObj); |
|
var specs = [ |
|
// put higher-pressure first |
|
{ field: 'forwardPressure', order: -1 }, |
|
// put segments that are closer to initial edge first (and favor ones with no coords yet) |
|
{ field: 'backwardCoord', order: 1 }, |
|
].concat(eventOrderSpecs); |
|
objs.sort(function (obj0, obj1) { return compareByFieldSpecs(obj0, obj1, specs); }); |
|
return objs.map(function (c) { return c._seg; }); |
|
} |
|
function buildTimeGridSegCompareObj(seg) { |
|
var obj = buildSegCompareObj(seg); |
|
obj.forwardPressure = seg.forwardPressure; |
|
obj.backwardCoord = seg.backwardCoord; |
|
return obj; |
|
} |
|
|
|
var DEFAULT_TIME_FORMAT = createFormatter({ |
|
hour: 'numeric', |
|
minute: '2-digit', |
|
meridiem: false, |
|
}); |
|
var TimeColEvent = /** @class */ (function (_super) { |
|
__extends(TimeColEvent, _super); |
|
function TimeColEvent() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TimeColEvent.prototype.render = function () { |
|
var classNames = [ |
|
'fc-timegrid-event', |
|
'fc-v-event', |
|
]; |
|
if (this.props.isCondensed) { |
|
classNames.push('fc-timegrid-event-condensed'); |
|
} |
|
return (createElement(StandardEvent, __assign({}, this.props, { defaultTimeFormat: DEFAULT_TIME_FORMAT, extraClassNames: classNames }))); |
|
}; |
|
return TimeColEvent; |
|
}(BaseComponent)); |
|
|
|
var TimeColMisc = /** @class */ (function (_super) { |
|
__extends(TimeColMisc, _super); |
|
function TimeColMisc() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TimeColMisc.prototype.render = function () { |
|
var props = this.props; |
|
return (createElement(DayCellContent, { date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, extraHookProps: props.extraHookProps }, function (innerElRef, innerContent) { return (innerContent && |
|
createElement("div", { className: "fc-timegrid-col-misc", ref: innerElRef }, innerContent)); })); |
|
}; |
|
return TimeColMisc; |
|
}(BaseComponent)); |
|
|
|
config.timeGridEventCondensedHeight = 30; |
|
var TimeCol = /** @class */ (function (_super) { |
|
__extends(TimeCol, _super); |
|
function TimeCol() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
TimeCol.prototype.render = function () { |
|
var _this = this; |
|
var _a = this, props = _a.props, context = _a.context; |
|
var isSelectMirror = context.options.selectMirror; |
|
var mirrorSegs = (props.eventDrag && props.eventDrag.segs) || |
|
(props.eventResize && props.eventResize.segs) || |
|
(isSelectMirror && props.dateSelectionSegs) || |
|
[]; |
|
var interactionAffectedInstances = // TODO: messy way to compute this |
|
(props.eventDrag && props.eventDrag.affectedInstances) || |
|
(props.eventResize && props.eventResize.affectedInstances) || |
|
{}; |
|
return (createElement(DayCellRoot, { elRef: props.elRef, date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, extraHookProps: props.extraHookProps }, function (rootElRef, classNames, dataAttrs) { return (createElement("td", __assign({ ref: rootElRef, className: ['fc-timegrid-col'].concat(classNames, props.extraClassNames || []).join(' ') }, dataAttrs, props.extraDataAttrs), |
|
createElement("div", { className: "fc-timegrid-col-frame" }, |
|
createElement("div", { className: "fc-timegrid-col-bg" }, |
|
_this.renderFillSegs(props.businessHourSegs, 'non-business'), |
|
_this.renderFillSegs(props.bgEventSegs, 'bg-event'), |
|
_this.renderFillSegs(props.dateSelectionSegs, 'highlight')), |
|
createElement("div", { className: "fc-timegrid-col-events" }, _this.renderFgSegs(props.fgEventSegs, interactionAffectedInstances)), |
|
createElement("div", { className: "fc-timegrid-col-events" }, _this.renderFgSegs(mirrorSegs, {}, Boolean(props.eventDrag), Boolean(props.eventResize), Boolean(isSelectMirror))), |
|
createElement("div", { className: "fc-timegrid-now-indicator-container" }, _this.renderNowIndicator(props.nowIndicatorSegs)), |
|
createElement(TimeColMisc, { date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, extraHookProps: props.extraHookProps })))); })); |
|
}; |
|
TimeCol.prototype.renderFgSegs = function (segs, segIsInvisible, isDragging, isResizing, isDateSelecting) { |
|
var props = this.props; |
|
if (props.forPrint) { |
|
return this.renderPrintFgSegs(segs); |
|
} |
|
if (props.slatCoords) { |
|
return this.renderPositionedFgSegs(segs, segIsInvisible, isDragging, isResizing, isDateSelecting); |
|
} |
|
return null; |
|
}; |
|
TimeCol.prototype.renderPrintFgSegs = function (segs) { |
|
var _a = this, props = _a.props, context = _a.context; |
|
// not DRY |
|
segs = sortEventSegs(segs, context.options.eventOrder); |
|
return segs.map(function (seg) { return (createElement("div", { className: "fc-timegrid-event-harness", key: seg.eventRange.instance.instanceId }, |
|
createElement(TimeColEvent, __assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false, isCondensed: false }, getSegMeta(seg, props.todayRange, props.nowDate))))); }); |
|
}; |
|
TimeCol.prototype.renderPositionedFgSegs = function (segs, segIsInvisible, isDragging, isResizing, isDateSelecting) { |
|
var _this = this; |
|
var _a = this, context = _a.context, props = _a.props; |
|
// assigns TO THE SEGS THEMSELVES |
|
// also, receives resorted array |
|
segs = computeSegCoords(segs, props.date, props.slatCoords, context.options.eventMinHeight, context.options.eventOrder); |
|
return segs.map(function (seg) { |
|
var instanceId = seg.eventRange.instance.instanceId; |
|
var isMirror = isDragging || isResizing || isDateSelecting; |
|
var positionCss = isMirror |
|
// will span entire column width |
|
// also, won't assign z-index, which is good, fc-event-mirror will overpower other harnesses |
|
? __assign({ left: 0, right: 0 }, _this.computeSegTopBottomCss(seg)) : _this.computeFgSegPositionCss(seg); |
|
return (createElement("div", { className: 'fc-timegrid-event-harness' + (seg.level > 0 ? ' fc-timegrid-event-harness-inset' : ''), key: instanceId, style: __assign({ visibility: segIsInvisible[instanceId] ? 'hidden' : '' }, positionCss) }, |
|
createElement(TimeColEvent, __assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection, isCondensed: (seg.bottom - seg.top) < config.timeGridEventCondensedHeight }, getSegMeta(seg, props.todayRange, props.nowDate))))); |
|
}); |
|
}; |
|
TimeCol.prototype.renderFillSegs = function (segs, fillType) { |
|
var _this = this; |
|
var _a = this, context = _a.context, props = _a.props; |
|
if (!props.slatCoords) { |
|
return null; |
|
} |
|
// BAD: assigns TO THE SEGS THEMSELVES |
|
computeSegVerticals(segs, props.date, props.slatCoords, context.options.eventMinHeight); |
|
var children = segs.map(function (seg) { return (createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-timegrid-bg-harness", style: _this.computeSegTopBottomCss(seg) }, fillType === 'bg-event' ? |
|
createElement(BgEvent, __assign({ seg: seg }, getSegMeta(seg, props.todayRange, props.nowDate))) : |
|
renderFill(fillType))); }); |
|
return createElement(Fragment, null, children); |
|
}; |
|
TimeCol.prototype.renderNowIndicator = function (segs) { |
|
var _a = this.props, slatCoords = _a.slatCoords, date = _a.date; |
|
if (!slatCoords) { |
|
return null; |
|
} |
|
return segs.map(function (seg, i) { return (createElement(NowIndicatorRoot, { isAxis: false, date: date, |
|
// key doesn't matter. will only ever be one |
|
key: i }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("div", { ref: rootElRef, className: ['fc-timegrid-now-indicator-line'].concat(classNames).join(' '), style: { top: slatCoords.computeDateTop(seg.start, date) } }, innerContent)); })); }); |
|
}; |
|
TimeCol.prototype.computeFgSegPositionCss = function (seg) { |
|
var _a = this.context, isRtl = _a.isRtl, options = _a.options; |
|
var shouldOverlap = options.slotEventOverlap; |
|
var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point |
|
var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point |
|
var left; // amount of space from left edge, a fraction of the total width |
|
var right; // amount of space from right edge, a fraction of the total width |
|
if (shouldOverlap) { |
|
// double the width, but don't go beyond the maximum forward coordinate (1.0) |
|
forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2); |
|
} |
|
if (isRtl) { |
|
left = 1 - forwardCoord; |
|
right = backwardCoord; |
|
} |
|
else { |
|
left = backwardCoord; |
|
right = 1 - forwardCoord; |
|
} |
|
var props = { |
|
zIndex: seg.level + 1, |
|
left: left * 100 + '%', |
|
right: right * 100 + '%', |
|
}; |
|
if (shouldOverlap && seg.forwardPressure) { |
|
// add padding to the edge so that forward stacked events don't cover the resizer's icon |
|
props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width |
|
} |
|
return __assign(__assign({}, props), this.computeSegTopBottomCss(seg)); |
|
}; |
|
TimeCol.prototype.computeSegTopBottomCss = function (seg) { |
|
return { |
|
top: seg.top, |
|
bottom: -seg.bottom, |
|
}; |
|
}; |
|
return TimeCol; |
|
}(BaseComponent)); |
|
|
|
var TimeColsContent = /** @class */ (function (_super) { |
|
__extends(TimeColsContent, _super); |
|
function TimeColsContent() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.splitFgEventSegs = memoize(splitSegsByCol); |
|
_this.splitBgEventSegs = memoize(splitSegsByCol); |
|
_this.splitBusinessHourSegs = memoize(splitSegsByCol); |
|
_this.splitNowIndicatorSegs = memoize(splitSegsByCol); |
|
_this.splitDateSelectionSegs = memoize(splitSegsByCol); |
|
_this.splitEventDrag = memoize(splitInteractionByCol); |
|
_this.splitEventResize = memoize(splitInteractionByCol); |
|
_this.rootElRef = createRef(); |
|
_this.cellElRefs = new RefMap(); |
|
return _this; |
|
} |
|
TimeColsContent.prototype.render = function () { |
|
var _this = this; |
|
var _a = this, props = _a.props, context = _a.context; |
|
var nowIndicatorTop = context.options.nowIndicator && |
|
props.slatCoords && |
|
props.slatCoords.safeComputeTop(props.nowDate); // might return void |
|
var colCnt = props.cells.length; |
|
var fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, colCnt); |
|
var bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, colCnt); |
|
var businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, colCnt); |
|
var nowIndicatorSegsByRow = this.splitNowIndicatorSegs(props.nowIndicatorSegs, colCnt); |
|
var dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, colCnt); |
|
var eventDragByRow = this.splitEventDrag(props.eventDrag, colCnt); |
|
var eventResizeByRow = this.splitEventResize(props.eventResize, colCnt); |
|
return (createElement("div", { className: "fc-timegrid-cols", ref: this.rootElRef }, |
|
createElement("table", { style: { |
|
minWidth: props.tableMinWidth, |
|
width: props.clientWidth, |
|
} }, |
|
props.tableColGroupNode, |
|
createElement("tbody", null, |
|
createElement("tr", null, |
|
props.axis && (createElement("td", { className: "fc-timegrid-col fc-timegrid-axis" }, |
|
createElement("div", { className: "fc-timegrid-col-frame" }, |
|
createElement("div", { className: "fc-timegrid-now-indicator-container" }, typeof nowIndicatorTop === 'number' && (createElement(NowIndicatorRoot, { isAxis: true, date: props.nowDate }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("div", { ref: rootElRef, className: ['fc-timegrid-now-indicator-arrow'].concat(classNames).join(' '), style: { top: nowIndicatorTop } }, innerContent)); })))))), |
|
props.cells.map(function (cell, i) { return (createElement(TimeCol, { key: cell.key, elRef: _this.cellElRefs.createRef(cell.key), dateProfile: props.dateProfile, date: cell.date, nowDate: props.nowDate, todayRange: props.todayRange, extraHookProps: cell.extraHookProps, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames, fgEventSegs: fgEventSegsByRow[i], bgEventSegs: bgEventSegsByRow[i], businessHourSegs: businessHourSegsByRow[i], nowIndicatorSegs: nowIndicatorSegsByRow[i], dateSelectionSegs: dateSelectionSegsByRow[i], eventDrag: eventDragByRow[i], eventResize: eventResizeByRow[i], slatCoords: props.slatCoords, eventSelection: props.eventSelection, forPrint: props.forPrint })); })))))); |
|
}; |
|
TimeColsContent.prototype.componentDidMount = function () { |
|
this.updateCoords(); |
|
}; |
|
TimeColsContent.prototype.componentDidUpdate = function () { |
|
this.updateCoords(); |
|
}; |
|
TimeColsContent.prototype.updateCoords = function () { |
|
var props = this.props; |
|
if (props.onColCoords && |
|
props.clientWidth !== null // means sizing has stabilized |
|
) { |
|
props.onColCoords(new PositionCache(this.rootElRef.current, collectCellEls(this.cellElRefs.currentMap, props.cells), true, // horizontal |
|
false)); |
|
} |
|
}; |
|
return TimeColsContent; |
|
}(BaseComponent)); |
|
function collectCellEls(elMap, cells) { |
|
return cells.map(function (cell) { return elMap[cell.key]; }); |
|
} |
|
|
|
/* A component that renders one or more columns of vertical time slots |
|
----------------------------------------------------------------------------------------------------------------------*/ |
|
var TimeCols = /** @class */ (function (_super) { |
|
__extends(TimeCols, _super); |
|
function TimeCols() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.processSlotOptions = memoize(processSlotOptions); |
|
_this.state = { |
|
slatCoords: null, |
|
}; |
|
_this.handleScrollRequest = function (request) { |
|
var onScrollTopRequest = _this.props.onScrollTopRequest; |
|
var slatCoords = _this.state.slatCoords; |
|
if (onScrollTopRequest && slatCoords) { |
|
if (request.time) { |
|
var top_1 = slatCoords.computeTimeTop(request.time); |
|
top_1 = Math.ceil(top_1); // zoom can give weird floating-point values. rather scroll a little bit further |
|
if (top_1) { |
|
top_1 += 1; // to overcome top border that slots beyond the first have. looks better |
|
} |
|
onScrollTopRequest(top_1); |
|
} |
|
return true; |
|
} |
|
return false; |
|
}; |
|
_this.handleColCoords = function (colCoords) { |
|
_this.colCoords = colCoords; |
|
}; |
|
_this.handleSlatCoords = function (slatCoords) { |
|
_this.setState({ slatCoords: slatCoords }); |
|
if (_this.props.onSlatCoords) { |
|
_this.props.onSlatCoords(slatCoords); |
|
} |
|
}; |
|
return _this; |
|
} |
|
TimeCols.prototype.render = function () { |
|
var _a = this, props = _a.props, state = _a.state; |
|
return (createElement("div", { className: "fc-timegrid-body", ref: props.rootElRef, style: { |
|
// these props are important to give this wrapper correct dimensions for interactions |
|
// TODO: if we set it here, can we avoid giving to inner tables? |
|
width: props.clientWidth, |
|
minWidth: props.tableMinWidth, |
|
} }, |
|
createElement(TimeColsSlats, { axis: props.axis, dateProfile: props.dateProfile, slatMetas: props.slatMetas, clientWidth: props.clientWidth, minHeight: props.expandRows ? props.clientHeight : '', tableMinWidth: props.tableMinWidth, tableColGroupNode: props.axis ? props.tableColGroupNode : null /* axis depends on the colgroup's shrinking */, onCoords: this.handleSlatCoords }), |
|
createElement(TimeColsContent, { cells: props.cells, axis: props.axis, dateProfile: props.dateProfile, businessHourSegs: props.businessHourSegs, bgEventSegs: props.bgEventSegs, fgEventSegs: props.fgEventSegs, dateSelectionSegs: props.dateSelectionSegs, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange, nowDate: props.nowDate, nowIndicatorSegs: props.nowIndicatorSegs, clientWidth: props.clientWidth, tableMinWidth: props.tableMinWidth, tableColGroupNode: props.tableColGroupNode, slatCoords: state.slatCoords, onColCoords: this.handleColCoords, forPrint: props.forPrint }))); |
|
}; |
|
TimeCols.prototype.componentDidMount = function () { |
|
this.scrollResponder = this.context.createScrollResponder(this.handleScrollRequest); |
|
}; |
|
TimeCols.prototype.componentDidUpdate = function (prevProps) { |
|
this.scrollResponder.update(prevProps.dateProfile !== this.props.dateProfile); |
|
}; |
|
TimeCols.prototype.componentWillUnmount = function () { |
|
this.scrollResponder.detach(); |
|
}; |
|
TimeCols.prototype.positionToHit = function (positionLeft, positionTop) { |
|
var _a = this.context, dateEnv = _a.dateEnv, options = _a.options; |
|
var colCoords = this.colCoords; |
|
var dateProfile = this.props.dateProfile; |
|
var slatCoords = this.state.slatCoords; |
|
var _b = this.processSlotOptions(this.props.slotDuration, options.snapDuration), snapDuration = _b.snapDuration, snapsPerSlot = _b.snapsPerSlot; |
|
var colIndex = colCoords.leftToIndex(positionLeft); |
|
var slatIndex = slatCoords.positions.topToIndex(positionTop); |
|
if (colIndex != null && slatIndex != null) { |
|
var slatTop = slatCoords.positions.tops[slatIndex]; |
|
var slatHeight = slatCoords.positions.getHeight(slatIndex); |
|
var partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1 |
|
var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat |
|
var snapIndex = slatIndex * snapsPerSlot + localSnapIndex; |
|
var dayDate = this.props.cells[colIndex].date; |
|
var time = addDurations(dateProfile.slotMinTime, multiplyDuration(snapDuration, snapIndex)); |
|
var start = dateEnv.add(dayDate, time); |
|
var end = dateEnv.add(start, snapDuration); |
|
return { |
|
col: colIndex, |
|
dateSpan: { |
|
range: { start: start, end: end }, |
|
allDay: false, |
|
}, |
|
dayEl: colCoords.els[colIndex], |
|
relativeRect: { |
|
left: colCoords.lefts[colIndex], |
|
right: colCoords.rights[colIndex], |
|
top: slatTop, |
|
bottom: slatTop + slatHeight, |
|
}, |
|
}; |
|
} |
|
return null; |
|
}; |
|
return TimeCols; |
|
}(BaseComponent)); |
|
function processSlotOptions(slotDuration, snapDurationOverride) { |
|
var snapDuration = snapDurationOverride || slotDuration; |
|
var snapsPerSlot = wholeDivideDurations(slotDuration, snapDuration); |
|
if (snapsPerSlot === null) { |
|
snapDuration = slotDuration; |
|
snapsPerSlot = 1; |
|
// TODO: say warning? |
|
} |
|
return { snapDuration: snapDuration, snapsPerSlot: snapsPerSlot }; |
|
} |
|
|
|
var DayTimeColsSlicer = /** @class */ (function (_super) { |
|
__extends(DayTimeColsSlicer, _super); |
|
function DayTimeColsSlicer() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
DayTimeColsSlicer.prototype.sliceRange = function (range, dayRanges) { |
|
var segs = []; |
|
for (var col = 0; col < dayRanges.length; col += 1) { |
|
var segRange = intersectRanges(range, dayRanges[col]); |
|
if (segRange) { |
|
segs.push({ |
|
start: segRange.start, |
|
end: segRange.end, |
|
isStart: segRange.start.valueOf() === range.start.valueOf(), |
|
isEnd: segRange.end.valueOf() === range.end.valueOf(), |
|
col: col, |
|
}); |
|
} |
|
} |
|
return segs; |
|
}; |
|
return DayTimeColsSlicer; |
|
}(Slicer)); |
|
|
|
var DayTimeCols = /** @class */ (function (_super) { |
|
__extends(DayTimeCols, _super); |
|
function DayTimeCols() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.buildDayRanges = memoize(buildDayRanges); |
|
_this.slicer = new DayTimeColsSlicer(); |
|
_this.timeColsRef = createRef(); |
|
_this.handleRootEl = function (rootEl) { |
|
if (rootEl) { |
|
_this.context.registerInteractiveComponent(_this, { el: rootEl }); |
|
} |
|
else { |
|
_this.context.unregisterInteractiveComponent(_this); |
|
} |
|
}; |
|
return _this; |
|
} |
|
DayTimeCols.prototype.render = function () { |
|
var _this = this; |
|
var _a = this, props = _a.props, context = _a.context; |
|
var dateProfile = props.dateProfile, dayTableModel = props.dayTableModel; |
|
var isNowIndicator = context.options.nowIndicator; |
|
var dayRanges = this.buildDayRanges(dayTableModel, dateProfile, context.dateEnv); |
|
// give it the first row of cells |
|
// TODO: would move this further down hierarchy, but sliceNowDate needs it |
|
return (createElement(NowTimer, { unit: isNowIndicator ? 'minute' : 'day' }, function (nowDate, todayRange) { return (createElement(TimeCols, __assign({ ref: _this.timeColsRef, rootElRef: _this.handleRootEl }, _this.slicer.sliceProps(props, dateProfile, null, context, dayRanges), { forPrint: props.forPrint, axis: props.axis, dateProfile: dateProfile, slatMetas: props.slatMetas, slotDuration: props.slotDuration, cells: dayTableModel.cells[0], tableColGroupNode: props.tableColGroupNode, tableMinWidth: props.tableMinWidth, clientWidth: props.clientWidth, clientHeight: props.clientHeight, expandRows: props.expandRows, nowDate: nowDate, nowIndicatorSegs: isNowIndicator && _this.slicer.sliceNowDate(nowDate, context, dayRanges), todayRange: todayRange, onScrollTopRequest: props.onScrollTopRequest, onSlatCoords: props.onSlatCoords }))); })); |
|
}; |
|
DayTimeCols.prototype.queryHit = function (positionLeft, positionTop) { |
|
var rawHit = this.timeColsRef.current.positionToHit(positionLeft, positionTop); |
|
if (rawHit) { |
|
return { |
|
component: this, |
|
dateSpan: rawHit.dateSpan, |
|
dayEl: rawHit.dayEl, |
|
rect: { |
|
left: rawHit.relativeRect.left, |
|
right: rawHit.relativeRect.right, |
|
top: rawHit.relativeRect.top, |
|
bottom: rawHit.relativeRect.bottom, |
|
}, |
|
layer: 0, |
|
}; |
|
} |
|
return null; |
|
}; |
|
return DayTimeCols; |
|
}(DateComponent)); |
|
function buildDayRanges(dayTableModel, dateProfile, dateEnv) { |
|
var ranges = []; |
|
for (var _i = 0, _a = dayTableModel.headerDates; _i < _a.length; _i++) { |
|
var date = _a[_i]; |
|
ranges.push({ |
|
start: dateEnv.add(date, dateProfile.slotMinTime), |
|
end: dateEnv.add(date, dateProfile.slotMaxTime), |
|
}); |
|
} |
|
return ranges; |
|
} |
|
|
|
// potential nice values for the slot-duration and interval-duration |
|
// from largest to smallest |
|
var STOCK_SUB_DURATIONS = [ |
|
{ hours: 1 }, |
|
{ minutes: 30 }, |
|
{ minutes: 15 }, |
|
{ seconds: 30 }, |
|
{ seconds: 15 }, |
|
]; |
|
function buildSlatMetas(slotMinTime, slotMaxTime, explicitLabelInterval, slotDuration, dateEnv) { |
|
var dayStart = new Date(0); |
|
var slatTime = slotMinTime; |
|
var slatIterator = createDuration(0); |
|
var labelInterval = explicitLabelInterval || computeLabelInterval(slotDuration); |
|
var metas = []; |
|
while (asRoughMs(slatTime) < asRoughMs(slotMaxTime)) { |
|
var date = dateEnv.add(dayStart, slatTime); |
|
var isLabeled = wholeDivideDurations(slatIterator, labelInterval) !== null; |
|
metas.push({ |
|
date: date, |
|
time: slatTime, |
|
key: date.toISOString(), |
|
isoTimeStr: formatIsoTimeString(date), |
|
isLabeled: isLabeled, |
|
}); |
|
slatTime = addDurations(slatTime, slotDuration); |
|
slatIterator = addDurations(slatIterator, slotDuration); |
|
} |
|
return metas; |
|
} |
|
// Computes an automatic value for slotLabelInterval |
|
function computeLabelInterval(slotDuration) { |
|
var i; |
|
var labelInterval; |
|
var slotsPerLabel; |
|
// find the smallest stock label interval that results in more than one slots-per-label |
|
for (i = STOCK_SUB_DURATIONS.length - 1; i >= 0; i -= 1) { |
|
labelInterval = createDuration(STOCK_SUB_DURATIONS[i]); |
|
slotsPerLabel = wholeDivideDurations(labelInterval, slotDuration); |
|
if (slotsPerLabel !== null && slotsPerLabel > 1) { |
|
return labelInterval; |
|
} |
|
} |
|
return slotDuration; // fall back |
|
} |
|
|
|
var DayTimeColsView = /** @class */ (function (_super) { |
|
__extends(DayTimeColsView, _super); |
|
function DayTimeColsView() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.buildTimeColsModel = memoize(buildTimeColsModel); |
|
_this.buildSlatMetas = memoize(buildSlatMetas); |
|
return _this; |
|
} |
|
DayTimeColsView.prototype.render = function () { |
|
var _this = this; |
|
var _a = this.context, options = _a.options, dateEnv = _a.dateEnv, dateProfileGenerator = _a.dateProfileGenerator; |
|
var props = this.props; |
|
var dateProfile = props.dateProfile; |
|
var dayTableModel = this.buildTimeColsModel(dateProfile, dateProfileGenerator); |
|
var splitProps = this.allDaySplitter.splitProps(props); |
|
var slatMetas = this.buildSlatMetas(dateProfile.slotMinTime, dateProfile.slotMaxTime, options.slotLabelInterval, options.slotDuration, dateEnv); |
|
var dayMinWidth = options.dayMinWidth; |
|
var hasAttachedAxis = !dayMinWidth; |
|
var hasDetachedAxis = dayMinWidth; |
|
var headerContent = options.dayHeaders && (createElement(DayHeader, { dates: dayTableModel.headerDates, dateProfile: dateProfile, datesRepDistinctDays: true, renderIntro: hasAttachedAxis ? this.renderHeadAxis : null })); |
|
var allDayContent = (options.allDaySlot !== false) && (function (contentArg) { return (createElement(DayTable, __assign({}, splitProps.allDay, { dateProfile: dateProfile, dayTableModel: dayTableModel, nextDayThreshold: options.nextDayThreshold, tableMinWidth: contentArg.tableMinWidth, colGroupNode: contentArg.tableColGroupNode, renderRowIntro: hasAttachedAxis ? _this.renderTableRowAxis : null, showWeekNumbers: false, expandRows: false, headerAlignElRef: _this.headerElRef, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, forPrint: props.forPrint }, _this.getAllDayMaxEventProps()))); }); |
|
var timeGridContent = function (contentArg) { return (createElement(DayTimeCols, __assign({}, splitProps.timed, { dayTableModel: dayTableModel, dateProfile: dateProfile, axis: hasAttachedAxis, slotDuration: options.slotDuration, slatMetas: slatMetas, forPrint: props.forPrint, tableColGroupNode: contentArg.tableColGroupNode, tableMinWidth: contentArg.tableMinWidth, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, onSlatCoords: _this.handleSlatCoords, expandRows: contentArg.expandRows, onScrollTopRequest: _this.handleScrollTopRequest }))); }; |
|
return hasDetachedAxis |
|
? this.renderHScrollLayout(headerContent, allDayContent, timeGridContent, dayTableModel.colCnt, dayMinWidth, slatMetas, this.state.slatCoords) |
|
: this.renderSimpleLayout(headerContent, allDayContent, timeGridContent); |
|
}; |
|
return DayTimeColsView; |
|
}(TimeColsView)); |
|
function buildTimeColsModel(dateProfile, dateProfileGenerator) { |
|
var daySeries = new DaySeriesModel(dateProfile.renderRange, dateProfileGenerator); |
|
return new DayTableModel(daySeries, false); |
|
} |
|
|
|
var OPTION_REFINERS$2 = { |
|
allDaySlot: Boolean, |
|
}; |
|
|
|
var timeGridPlugin = createPlugin({ |
|
initialView: 'timeGridWeek', |
|
optionRefiners: OPTION_REFINERS$2, |
|
views: { |
|
timeGrid: { |
|
component: DayTimeColsView, |
|
usesMinMaxTime: true, |
|
allDaySlot: true, |
|
slotDuration: '00:30:00', |
|
slotEventOverlap: true, |
|
}, |
|
timeGridDay: { |
|
type: 'timeGrid', |
|
duration: { days: 1 }, |
|
}, |
|
timeGridWeek: { |
|
type: 'timeGrid', |
|
duration: { weeks: 1 }, |
|
}, |
|
}, |
|
}); |
|
|
|
var ListViewHeaderRow = /** @class */ (function (_super) { |
|
__extends(ListViewHeaderRow, _super); |
|
function ListViewHeaderRow() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
ListViewHeaderRow.prototype.render = function () { |
|
var _a = this.props, dayDate = _a.dayDate, todayRange = _a.todayRange; |
|
var _b = this.context, theme = _b.theme, dateEnv = _b.dateEnv, options = _b.options, viewApi = _b.viewApi; |
|
var dayMeta = getDateMeta(dayDate, todayRange); |
|
// will ever be falsy? |
|
var text = options.listDayFormat ? dateEnv.format(dayDate, options.listDayFormat) : ''; |
|
// will ever be falsy? also, BAD NAME "alt" |
|
var sideText = options.listDaySideFormat ? dateEnv.format(dayDate, options.listDaySideFormat) : ''; |
|
var navLinkData = options.navLinks |
|
? buildNavLinkData(dayDate) |
|
: null; |
|
var hookProps = __assign({ date: dateEnv.toDate(dayDate), view: viewApi, text: text, |
|
sideText: sideText, |
|
navLinkData: navLinkData }, dayMeta); |
|
var classNames = ['fc-list-day'].concat(getDayClassNames(dayMeta, theme)); |
|
// TODO: make a reusable HOC for dayHeader (used in daygrid/timegrid too) |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.dayHeaderClassNames, content: options.dayHeaderContent, defaultContent: renderInnerContent$4, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("tr", { ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-date": formatDayString(dayDate) }, |
|
createElement("th", { colSpan: 3 }, |
|
createElement("div", { className: 'fc-list-day-cushion ' + theme.getClass('tableCellShaded'), ref: innerElRef }, innerContent)))); })); |
|
}; |
|
return ListViewHeaderRow; |
|
}(BaseComponent)); |
|
function renderInnerContent$4(props) { |
|
var navLinkAttrs = props.navLinkData // is there a type for this? |
|
? { 'data-navlink': props.navLinkData, tabIndex: 0 } |
|
: {}; |
|
return (createElement(Fragment, null, |
|
props.text && (createElement("a", __assign({ className: "fc-list-day-text" }, navLinkAttrs), props.text)), |
|
props.sideText && (createElement("a", __assign({ className: "fc-list-day-side-text" }, navLinkAttrs), props.sideText)))); |
|
} |
|
|
|
var DEFAULT_TIME_FORMAT$1 = createFormatter({ |
|
hour: 'numeric', |
|
minute: '2-digit', |
|
meridiem: 'short', |
|
}); |
|
var ListViewEventRow = /** @class */ (function (_super) { |
|
__extends(ListViewEventRow, _super); |
|
function ListViewEventRow() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
ListViewEventRow.prototype.render = function () { |
|
var _a = this, props = _a.props, context = _a.context; |
|
var seg = props.seg; |
|
var timeFormat = context.options.eventTimeFormat || DEFAULT_TIME_FORMAT$1; |
|
return (createElement(EventRoot, { seg: seg, timeText: "" // BAD. because of all-day content |
|
, disableDragging: true, disableResizing: true, defaultContent: renderEventInnerContent, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday, isSelected: props.isSelected, isDragging: props.isDragging, isResizing: props.isResizing, isDateSelecting: props.isDateSelecting }, function (rootElRef, classNames, innerElRef, innerContent, hookProps) { return (createElement("tr", { className: ['fc-list-event', hookProps.event.url ? 'fc-event-forced-url' : ''].concat(classNames).join(' '), ref: rootElRef }, |
|
buildTimeContent(seg, timeFormat, context), |
|
createElement("td", { className: "fc-list-event-graphic" }, |
|
createElement("span", { className: "fc-list-event-dot", style: { borderColor: hookProps.borderColor || hookProps.backgroundColor } })), |
|
createElement("td", { className: "fc-list-event-title", ref: innerElRef }, innerContent))); })); |
|
}; |
|
return ListViewEventRow; |
|
}(BaseComponent)); |
|
function renderEventInnerContent(props) { |
|
var event = props.event; |
|
var url = event.url; |
|
var anchorAttrs = url ? { href: url } : {}; |
|
return (createElement("a", __assign({}, anchorAttrs), event.title)); |
|
} |
|
function buildTimeContent(seg, timeFormat, context) { |
|
var options = context.options; |
|
if (options.displayEventTime !== false) { |
|
var eventDef = seg.eventRange.def; |
|
var eventInstance = seg.eventRange.instance; |
|
var doAllDay = false; |
|
var timeText = void 0; |
|
if (eventDef.allDay) { |
|
doAllDay = true; |
|
} |
|
else if (isMultiDayRange(seg.eventRange.range)) { // TODO: use (!isStart || !isEnd) instead? |
|
if (seg.isStart) { |
|
timeText = buildSegTimeText(seg, timeFormat, context, null, null, eventInstance.range.start, seg.end); |
|
} |
|
else if (seg.isEnd) { |
|
timeText = buildSegTimeText(seg, timeFormat, context, null, null, seg.start, eventInstance.range.end); |
|
} |
|
else { |
|
doAllDay = true; |
|
} |
|
} |
|
else { |
|
timeText = buildSegTimeText(seg, timeFormat, context); |
|
} |
|
if (doAllDay) { |
|
var hookProps = { |
|
text: context.options.allDayText, |
|
view: context.viewApi, |
|
}; |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.allDayClassNames, content: options.allDayContent, defaultContent: renderAllDayInner$1, didMount: options.allDayDidMount, willUnmount: options.allDayWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("td", { className: ['fc-list-event-time'].concat(classNames).join(' '), ref: rootElRef }, innerContent)); })); |
|
} |
|
return (createElement("td", { className: "fc-list-event-time" }, timeText)); |
|
} |
|
return null; |
|
} |
|
function renderAllDayInner$1(hookProps) { |
|
return hookProps.text; |
|
} |
|
|
|
/* |
|
Responsible for the scroller, and forwarding event-related actions into the "grid". |
|
*/ |
|
var ListView = /** @class */ (function (_super) { |
|
__extends(ListView, _super); |
|
function ListView() { |
|
var _this = _super !== null && _super.apply(this, arguments) || this; |
|
_this.computeDateVars = memoize(computeDateVars); |
|
_this.eventStoreToSegs = memoize(_this._eventStoreToSegs); |
|
_this.setRootEl = function (rootEl) { |
|
if (rootEl) { |
|
_this.context.registerInteractiveComponent(_this, { |
|
el: rootEl, |
|
}); |
|
} |
|
else { |
|
_this.context.unregisterInteractiveComponent(_this); |
|
} |
|
}; |
|
return _this; |
|
} |
|
ListView.prototype.render = function () { |
|
var _this = this; |
|
var _a = this, props = _a.props, context = _a.context; |
|
var extraClassNames = [ |
|
'fc-list', |
|
context.theme.getClass('table'), |
|
context.options.stickyHeaderDates !== false ? 'fc-list-sticky' : '', |
|
]; |
|
var _b = this.computeDateVars(props.dateProfile), dayDates = _b.dayDates, dayRanges = _b.dayRanges; |
|
var eventSegs = this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges); |
|
return (createElement(ViewRoot, { viewSpec: context.viewSpec, elRef: this.setRootEl }, function (rootElRef, classNames) { return (createElement("div", { ref: rootElRef, className: extraClassNames.concat(classNames).join(' ') }, |
|
createElement(Scroller, { liquid: !props.isHeightAuto, overflowX: props.isHeightAuto ? 'visible' : 'hidden', overflowY: props.isHeightAuto ? 'visible' : 'auto' }, eventSegs.length > 0 ? |
|
_this.renderSegList(eventSegs, dayDates) : |
|
_this.renderEmptyMessage()))); })); |
|
}; |
|
ListView.prototype.renderEmptyMessage = function () { |
|
var _a = this.context, options = _a.options, viewApi = _a.viewApi; |
|
var hookProps = { |
|
text: options.noEventsText, |
|
view: viewApi, |
|
}; |
|
return (createElement(RenderHook, { hookProps: hookProps, classNames: options.noEventsClassNames, content: options.noEventsContent, defaultContent: renderNoEventsInner, didMount: options.noEventsDidMount, willUnmount: options.noEventsWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("div", { className: ['fc-list-empty'].concat(classNames).join(' '), ref: rootElRef }, |
|
createElement("div", { className: "fc-list-empty-cushion", ref: innerElRef }, innerContent))); })); |
|
}; |
|
ListView.prototype.renderSegList = function (allSegs, dayDates) { |
|
var _a = this.context, theme = _a.theme, options = _a.options; |
|
var segsByDay = groupSegsByDay(allSegs); // sparse array |
|
return (createElement(NowTimer, { unit: "day" }, function (nowDate, todayRange) { |
|
var innerNodes = []; |
|
for (var dayIndex = 0; dayIndex < segsByDay.length; dayIndex += 1) { |
|
var daySegs = segsByDay[dayIndex]; |
|
if (daySegs) { // sparse array, so might be undefined |
|
var dayStr = dayDates[dayIndex].toISOString(); |
|
// append a day header |
|
innerNodes.push(createElement(ListViewHeaderRow, { key: dayStr, dayDate: dayDates[dayIndex], todayRange: todayRange })); |
|
daySegs = sortEventSegs(daySegs, options.eventOrder); |
|
for (var _i = 0, daySegs_1 = daySegs; _i < daySegs_1.length; _i++) { |
|
var seg = daySegs_1[_i]; |
|
innerNodes.push(createElement(ListViewEventRow, __assign({ key: dayStr + ':' + seg.eventRange.instance.instanceId /* are multiple segs for an instanceId */, seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false }, getSegMeta(seg, todayRange, nowDate)))); |
|
} |
|
} |
|
} |
|
return (createElement("table", { className: 'fc-list-table ' + theme.getClass('table') }, |
|
createElement("tbody", null, innerNodes))); |
|
})); |
|
}; |
|
ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) { |
|
return this.eventRangesToSegs(sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.context.options.nextDayThreshold).fg, dayRanges); |
|
}; |
|
ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) { |
|
var segs = []; |
|
for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) { |
|
var eventRange = eventRanges_1[_i]; |
|
segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges)); |
|
} |
|
return segs; |
|
}; |
|
ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) { |
|
var dateEnv = this.context.dateEnv; |
|
var nextDayThreshold = this.context.options.nextDayThreshold; |
|
var range = eventRange.range; |
|
var allDay = eventRange.def.allDay; |
|
var dayIndex; |
|
var segRange; |
|
var seg; |
|
var segs = []; |
|
for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex += 1) { |
|
segRange = intersectRanges(range, dayRanges[dayIndex]); |
|
if (segRange) { |
|
seg = { |
|
component: this, |
|
eventRange: eventRange, |
|
start: segRange.start, |
|
end: segRange.end, |
|
isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(), |
|
isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(), |
|
dayIndex: dayIndex, |
|
}; |
|
segs.push(seg); |
|
// detect when range won't go fully into the next day, |
|
// and mutate the latest seg to the be the end. |
|
if (!seg.isEnd && !allDay && |
|
dayIndex + 1 < dayRanges.length && |
|
range.end < |
|
dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) { |
|
seg.end = range.end; |
|
seg.isEnd = true; |
|
break; |
|
} |
|
} |
|
} |
|
return segs; |
|
}; |
|
return ListView; |
|
}(DateComponent)); |
|
function renderNoEventsInner(hookProps) { |
|
return hookProps.text; |
|
} |
|
function computeDateVars(dateProfile) { |
|
var dayStart = startOfDay(dateProfile.renderRange.start); |
|
var viewEnd = dateProfile.renderRange.end; |
|
var dayDates = []; |
|
var dayRanges = []; |
|
while (dayStart < viewEnd) { |
|
dayDates.push(dayStart); |
|
dayRanges.push({ |
|
start: dayStart, |
|
end: addDays(dayStart, 1), |
|
}); |
|
dayStart = addDays(dayStart, 1); |
|
} |
|
return { dayDates: dayDates, dayRanges: dayRanges }; |
|
} |
|
// Returns a sparse array of arrays, segs grouped by their dayIndex |
|
function groupSegsByDay(segs) { |
|
var segsByDay = []; // sparse array |
|
var i; |
|
var seg; |
|
for (i = 0; i < segs.length; i += 1) { |
|
seg = segs[i]; |
|
(segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = [])) |
|
.push(seg); |
|
} |
|
return segsByDay; |
|
} |
|
|
|
var OPTION_REFINERS$3 = { |
|
listDayFormat: createFalsableFormatter, |
|
listDaySideFormat: createFalsableFormatter, |
|
noEventsClassNames: identity, |
|
noEventsContent: identity, |
|
noEventsDidMount: identity, |
|
noEventsWillUnmount: identity, |
|
}; |
|
function createFalsableFormatter(input) { |
|
return input === false ? null : createFormatter(input); |
|
} |
|
|
|
var listPlugin = createPlugin({ |
|
optionRefiners: OPTION_REFINERS$3, |
|
views: { |
|
list: { |
|
component: ListView, |
|
buttonTextKey: 'list', |
|
listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' }, |
|
}, |
|
listDay: { |
|
type: 'list', |
|
duration: { days: 1 }, |
|
listDayFormat: { weekday: 'long' }, |
|
}, |
|
listWeek: { |
|
type: 'list', |
|
duration: { weeks: 1 }, |
|
listDayFormat: { weekday: 'long' }, |
|
listDaySideFormat: { month: 'long', day: 'numeric', year: 'numeric' }, |
|
}, |
|
listMonth: { |
|
type: 'list', |
|
duration: { month: 1 }, |
|
listDaySideFormat: { weekday: 'long' }, |
|
}, |
|
listYear: { |
|
type: 'list', |
|
duration: { year: 1 }, |
|
listDaySideFormat: { weekday: 'long' }, |
|
}, |
|
}, |
|
}); |
|
|
|
var BootstrapTheme = /** @class */ (function (_super) { |
|
__extends(BootstrapTheme, _super); |
|
function BootstrapTheme() { |
|
return _super !== null && _super.apply(this, arguments) || this; |
|
} |
|
return BootstrapTheme; |
|
}(Theme)); |
|
BootstrapTheme.prototype.classes = { |
|
root: 'fc-theme-bootstrap', |
|
table: 'table-bordered', |
|
tableCellShaded: 'table-active', |
|
buttonGroup: 'btn-group', |
|
button: 'btn btn-primary', |
|
buttonActive: 'active', |
|
popover: 'popover', |
|
popoverHeader: 'popover-header', |
|
popoverContent: 'popover-body', |
|
}; |
|
BootstrapTheme.prototype.baseIconClass = 'fa'; |
|
BootstrapTheme.prototype.iconClasses = { |
|
close: 'fa-times', |
|
prev: 'fa-chevron-left', |
|
next: 'fa-chevron-right', |
|
prevYear: 'fa-angle-double-left', |
|
nextYear: 'fa-angle-double-right', |
|
}; |
|
BootstrapTheme.prototype.rtlIconClasses = { |
|
prev: 'fa-chevron-right', |
|
next: 'fa-chevron-left', |
|
prevYear: 'fa-angle-double-right', |
|
nextYear: 'fa-angle-double-left', |
|
}; |
|
BootstrapTheme.prototype.iconOverrideOption = 'bootstrapFontAwesome'; // TODO: make TS-friendly. move the option-processing into this plugin |
|
BootstrapTheme.prototype.iconOverrideCustomButtonOption = 'bootstrapFontAwesome'; |
|
BootstrapTheme.prototype.iconOverridePrefix = 'fa-'; |
|
var plugin = createPlugin({ |
|
themeClasses: { |
|
bootstrap: BootstrapTheme, |
|
}, |
|
}); |
|
|
|
// rename this file to options.ts like other packages? |
|
var OPTION_REFINERS$4 = { |
|
googleCalendarApiKey: String, |
|
}; |
|
|
|
var EVENT_SOURCE_REFINERS$1 = { |
|
googleCalendarApiKey: String, |
|
googleCalendarId: String, |
|
googleCalendarApiBase: String, |
|
extraParams: identity, |
|
}; |
|
|
|
// TODO: expose somehow |
|
var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars'; |
|
var eventSourceDef$3 = { |
|
parseMeta: function (refined) { |
|
var googleCalendarId = refined.googleCalendarId; |
|
if (!googleCalendarId && refined.url) { |
|
googleCalendarId = parseGoogleCalendarId(refined.url); |
|
} |
|
if (googleCalendarId) { |
|
return { |
|
googleCalendarId: googleCalendarId, |
|
googleCalendarApiKey: refined.googleCalendarApiKey, |
|
googleCalendarApiBase: refined.googleCalendarApiBase, |
|
extraParams: refined.extraParams, |
|
}; |
|
} |
|
return null; |
|
}, |
|
fetch: function (arg, onSuccess, onFailure) { |
|
var _a = arg.context, dateEnv = _a.dateEnv, options = _a.options; |
|
var meta = arg.eventSource.meta; |
|
var apiKey = meta.googleCalendarApiKey || options.googleCalendarApiKey; |
|
if (!apiKey) { |
|
onFailure({ |
|
message: 'Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/', |
|
}); |
|
} |
|
else { |
|
var url = buildUrl(meta); |
|
// TODO: make DRY with json-feed-event-source |
|
var extraParams = meta.extraParams; |
|
var extraParamsObj = typeof extraParams === 'function' ? extraParams() : extraParams; |
|
var requestParams_1 = buildRequestParams$1(arg.range, apiKey, extraParamsObj, dateEnv); |
|
requestJson('GET', url, requestParams_1, function (body, xhr) { |
|
if (body.error) { |
|
onFailure({ |
|
message: 'Google Calendar API: ' + body.error.message, |
|
errors: body.error.errors, |
|
xhr: xhr, |
|
}); |
|
} |
|
else { |
|
onSuccess({ |
|
rawEvents: gcalItemsToRawEventDefs(body.items, requestParams_1.timeZone), |
|
xhr: xhr, |
|
}); |
|
} |
|
}, function (message, xhr) { |
|
onFailure({ message: message, xhr: xhr }); |
|
}); |
|
} |
|
}, |
|
}; |
|
function parseGoogleCalendarId(url) { |
|
var match; |
|
// detect if the ID was specified as a single string. |
|
// will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars. |
|
if (/^[^/]+@([^/.]+\.)*(google|googlemail|gmail)\.com$/.test(url)) { |
|
return url; |
|
} |
|
if ((match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^/]*)/.exec(url)) || |
|
(match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^/]*)/.exec(url))) { |
|
return decodeURIComponent(match[1]); |
|
} |
|
return null; |
|
} |
|
function buildUrl(meta) { |
|
var apiBase = meta.googleCalendarApiBase; |
|
if (!apiBase) { |
|
apiBase = API_BASE; |
|
} |
|
return apiBase + '/' + encodeURIComponent(meta.googleCalendarId) + '/events'; |
|
} |
|
function buildRequestParams$1(range, apiKey, extraParams, dateEnv) { |
|
var params; |
|
var startStr; |
|
var endStr; |
|
if (dateEnv.canComputeOffset) { |
|
// strings will naturally have offsets, which GCal needs |
|
startStr = dateEnv.formatIso(range.start); |
|
endStr = dateEnv.formatIso(range.end); |
|
} |
|
else { |
|
// when timezone isn't known, we don't know what the UTC offset should be, so ask for +/- 1 day |
|
// from the UTC day-start to guarantee we're getting all the events |
|
// (start/end will be UTC-coerced dates, so toISOString is okay) |
|
startStr = addDays(range.start, -1).toISOString(); |
|
endStr = addDays(range.end, 1).toISOString(); |
|
} |
|
params = __assign(__assign({}, (extraParams || {})), { key: apiKey, timeMin: startStr, timeMax: endStr, singleEvents: true, maxResults: 9999 }); |
|
if (dateEnv.timeZone !== 'local') { |
|
params.timeZone = dateEnv.timeZone; |
|
} |
|
return params; |
|
} |
|
function gcalItemsToRawEventDefs(items, gcalTimezone) { |
|
return items.map(function (item) { return gcalItemToRawEventDef(item, gcalTimezone); }); |
|
} |
|
function gcalItemToRawEventDef(item, gcalTimezone) { |
|
var url = item.htmlLink || null; |
|
// make the URLs for each event show times in the correct timezone |
|
if (url && gcalTimezone) { |
|
url = injectQsComponent(url, 'ctz=' + gcalTimezone); |
|
} |
|
return { |
|
id: item.id, |
|
title: item.summary, |
|
start: item.start.dateTime || item.start.date, |
|
end: item.end.dateTime || item.end.date, |
|
url: url, |
|
location: item.location, |
|
description: item.description, |
|
}; |
|
} |
|
// Injects a string like "arg=value" into the querystring of a URL |
|
// TODO: move to a general util file? |
|
function injectQsComponent(url, component) { |
|
// inject it after the querystring but before the fragment |
|
return url.replace(/(\?.*?)?(#|$)/, function (whole, qs, hash) { return (qs ? qs + '&' : '?') + component + hash; }); |
|
} |
|
var googleCalendarPlugin = createPlugin({ |
|
eventSourceDefs: [eventSourceDef$3], |
|
optionRefiners: OPTION_REFINERS$4, |
|
eventSourceRefiners: EVENT_SOURCE_REFINERS$1, |
|
}); |
|
|
|
globalPlugins.push(interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin, plugin, googleCalendarPlugin); |
|
|
|
exports.BASE_OPTION_DEFAULTS = BASE_OPTION_DEFAULTS; |
|
exports.BASE_OPTION_REFINERS = BASE_OPTION_REFINERS; |
|
exports.BaseComponent = BaseComponent; |
|
exports.BgEvent = BgEvent; |
|
exports.BootstrapTheme = BootstrapTheme; |
|
exports.Calendar = Calendar; |
|
exports.CalendarApi = CalendarApi; |
|
exports.CalendarContent = CalendarContent; |
|
exports.CalendarDataManager = CalendarDataManager; |
|
exports.CalendarDataProvider = CalendarDataProvider; |
|
exports.CalendarRoot = CalendarRoot; |
|
exports.Component = Component; |
|
exports.ContentHook = ContentHook; |
|
exports.CustomContentRenderContext = CustomContentRenderContext; |
|
exports.DateComponent = DateComponent; |
|
exports.DateEnv = DateEnv; |
|
exports.DateProfileGenerator = DateProfileGenerator; |
|
exports.DayCellContent = DayCellContent; |
|
exports.DayCellRoot = DayCellRoot; |
|
exports.DayGridView = DayTableView; |
|
exports.DayHeader = DayHeader; |
|
exports.DaySeriesModel = DaySeriesModel; |
|
exports.DayTable = DayTable; |
|
exports.DayTableModel = DayTableModel; |
|
exports.DayTableSlicer = DayTableSlicer; |
|
exports.DayTimeCols = DayTimeCols; |
|
exports.DayTimeColsSlicer = DayTimeColsSlicer; |
|
exports.DayTimeColsView = DayTimeColsView; |
|
exports.DelayedRunner = DelayedRunner; |
|
exports.Draggable = ExternalDraggable; |
|
exports.ElementDragging = ElementDragging; |
|
exports.ElementScrollController = ElementScrollController; |
|
exports.Emitter = Emitter; |
|
exports.EventApi = EventApi; |
|
exports.EventRoot = EventRoot; |
|
exports.EventSourceApi = EventSourceApi; |
|
exports.FeaturefulElementDragging = FeaturefulElementDragging; |
|
exports.Fragment = Fragment; |
|
exports.Interaction = Interaction; |
|
exports.ListView = ListView; |
|
exports.MountHook = MountHook; |
|
exports.NamedTimeZoneImpl = NamedTimeZoneImpl; |
|
exports.NowIndicatorRoot = NowIndicatorRoot; |
|
exports.NowTimer = NowTimer; |
|
exports.PointerDragging = PointerDragging; |
|
exports.PositionCache = PositionCache; |
|
exports.RefMap = RefMap; |
|
exports.RenderHook = RenderHook; |
|
exports.ScrollController = ScrollController; |
|
exports.ScrollResponder = ScrollResponder; |
|
exports.Scroller = Scroller; |
|
exports.SimpleScrollGrid = SimpleScrollGrid; |
|
exports.Slicer = Slicer; |
|
exports.Splitter = Splitter; |
|
exports.StandardEvent = StandardEvent; |
|
exports.Table = Table; |
|
exports.TableDateCell = TableDateCell; |
|
exports.TableDowCell = TableDowCell; |
|
exports.TableView = TableView; |
|
exports.Theme = Theme; |
|
exports.ThirdPartyDraggable = ThirdPartyDraggable; |
|
exports.TimeCols = TimeCols; |
|
exports.TimeColsSlatsCoords = TimeColsSlatsCoords; |
|
exports.TimeColsView = TimeColsView; |
|
exports.ViewApi = ViewApi; |
|
exports.ViewContextType = ViewContextType; |
|
exports.ViewRoot = ViewRoot; |
|
exports.WeekNumberRoot = WeekNumberRoot; |
|
exports.WindowScrollController = WindowScrollController; |
|
exports.addDays = addDays; |
|
exports.addDurations = addDurations; |
|
exports.addMs = addMs; |
|
exports.addWeeks = addWeeks; |
|
exports.allowContextMenu = allowContextMenu; |
|
exports.allowSelection = allowSelection; |
|
exports.applyMutationToEventStore = applyMutationToEventStore; |
|
exports.applyStyle = applyStyle; |
|
exports.applyStyleProp = applyStyleProp; |
|
exports.asCleanDays = asCleanDays; |
|
exports.asRoughMinutes = asRoughMinutes; |
|
exports.asRoughMs = asRoughMs; |
|
exports.asRoughSeconds = asRoughSeconds; |
|
exports.buildClassNameNormalizer = buildClassNameNormalizer; |
|
exports.buildDayRanges = buildDayRanges; |
|
exports.buildDayTableModel = buildDayTableModel; |
|
exports.buildEventApis = buildEventApis; |
|
exports.buildEventRangeKey = buildEventRangeKey; |
|
exports.buildHashFromArray = buildHashFromArray; |
|
exports.buildNavLinkData = buildNavLinkData; |
|
exports.buildSegCompareObj = buildSegCompareObj; |
|
exports.buildSegTimeText = buildSegTimeText; |
|
exports.buildSlatMetas = buildSlatMetas; |
|
exports.buildTimeColsModel = buildTimeColsModel; |
|
exports.collectFromHash = collectFromHash; |
|
exports.combineEventUis = combineEventUis; |
|
exports.compareByFieldSpec = compareByFieldSpec; |
|
exports.compareByFieldSpecs = compareByFieldSpecs; |
|
exports.compareNumbers = compareNumbers; |
|
exports.compareObjs = compareObjs; |
|
exports.computeEdges = computeEdges; |
|
exports.computeFallbackHeaderFormat = computeFallbackHeaderFormat; |
|
exports.computeHeightAndMargins = computeHeightAndMargins; |
|
exports.computeInnerRect = computeInnerRect; |
|
exports.computeRect = computeRect; |
|
exports.computeSegDraggable = computeSegDraggable; |
|
exports.computeSegEndResizable = computeSegEndResizable; |
|
exports.computeSegStartResizable = computeSegStartResizable; |
|
exports.computeShrinkWidth = computeShrinkWidth; |
|
exports.computeSmallestCellWidth = computeSmallestCellWidth; |
|
exports.computeVisibleDayRange = computeVisibleDayRange; |
|
exports.config = config; |
|
exports.constrainPoint = constrainPoint; |
|
exports.createContext = createContext$1; |
|
exports.createDuration = createDuration; |
|
exports.createElement = createElement; |
|
exports.createEmptyEventStore = createEmptyEventStore; |
|
exports.createEventInstance = createEventInstance; |
|
exports.createEventUi = createEventUi; |
|
exports.createFormatter = createFormatter; |
|
exports.createPlugin = createPlugin; |
|
exports.createRef = createRef; |
|
exports.diffDates = diffDates; |
|
exports.diffDayAndTime = diffDayAndTime; |
|
exports.diffDays = diffDays; |
|
exports.diffPoints = diffPoints; |
|
exports.diffWeeks = diffWeeks; |
|
exports.diffWholeDays = diffWholeDays; |
|
exports.diffWholeWeeks = diffWholeWeeks; |
|
exports.disableCursor = disableCursor; |
|
exports.elementClosest = elementClosest; |
|
exports.elementMatches = elementMatches; |
|
exports.enableCursor = enableCursor; |
|
exports.eventTupleToStore = eventTupleToStore; |
|
exports.filterEventStoreDefs = filterEventStoreDefs; |
|
exports.filterHash = filterHash; |
|
exports.findDirectChildren = findDirectChildren; |
|
exports.findElements = findElements; |
|
exports.flexibleCompare = flexibleCompare; |
|
exports.flushToDom = flushToDom$1; |
|
exports.formatDate = formatDate; |
|
exports.formatDayString = formatDayString; |
|
exports.formatIsoTimeString = formatIsoTimeString; |
|
exports.formatRange = formatRange; |
|
exports.getAllowYScrolling = getAllowYScrolling; |
|
exports.getCanVGrowWithinCell = getCanVGrowWithinCell; |
|
exports.getClippingParents = getClippingParents; |
|
exports.getDateMeta = getDateMeta; |
|
exports.getDayClassNames = getDayClassNames; |
|
exports.getDefaultEventEnd = getDefaultEventEnd; |
|
exports.getElSeg = getElSeg; |
|
exports.getEventClassNames = getEventClassNames; |
|
exports.getIsRtlScrollbarOnLeft = getIsRtlScrollbarOnLeft; |
|
exports.getRectCenter = getRectCenter; |
|
exports.getRelevantEvents = getRelevantEvents; |
|
exports.getScrollGridClassNames = getScrollGridClassNames; |
|
exports.getScrollbarWidths = getScrollbarWidths; |
|
exports.getSectionClassNames = getSectionClassNames; |
|
exports.getSectionHasLiquidHeight = getSectionHasLiquidHeight; |
|
exports.getSegMeta = getSegMeta; |
|
exports.getSlotClassNames = getSlotClassNames; |
|
exports.getStickyFooterScrollbar = getStickyFooterScrollbar; |
|
exports.getStickyHeaderDates = getStickyHeaderDates; |
|
exports.getUnequalProps = getUnequalProps; |
|
exports.globalLocales = globalLocales; |
|
exports.globalPlugins = globalPlugins; |
|
exports.greatestDurationDenominator = greatestDurationDenominator; |
|
exports.guid = guid; |
|
exports.hasBgRendering = hasBgRendering; |
|
exports.hasShrinkWidth = hasShrinkWidth; |
|
exports.identity = identity; |
|
exports.interactionSettingsStore = interactionSettingsStore; |
|
exports.interactionSettingsToStore = interactionSettingsToStore; |
|
exports.intersectRanges = intersectRanges; |
|
exports.intersectRects = intersectRects; |
|
exports.isArraysEqual = isArraysEqual; |
|
exports.isColPropsEqual = isColPropsEqual; |
|
exports.isDateSpansEqual = isDateSpansEqual; |
|
exports.isInt = isInt; |
|
exports.isInteractionValid = isInteractionValid; |
|
exports.isMultiDayRange = isMultiDayRange; |
|
exports.isPropsEqual = isPropsEqual; |
|
exports.isPropsValid = isPropsValid; |
|
exports.isValidDate = isValidDate; |
|
exports.listenBySelector = listenBySelector; |
|
exports.mapHash = mapHash; |
|
exports.memoize = memoize; |
|
exports.memoizeArraylike = memoizeArraylike; |
|
exports.memoizeHashlike = memoizeHashlike; |
|
exports.memoizeObjArg = memoizeObjArg; |
|
exports.mergeEventStores = mergeEventStores; |
|
exports.multiplyDuration = multiplyDuration; |
|
exports.padStart = padStart; |
|
exports.parseBusinessHours = parseBusinessHours; |
|
exports.parseClassNames = parseClassNames; |
|
exports.parseDragMeta = parseDragMeta; |
|
exports.parseEventDef = parseEventDef; |
|
exports.parseFieldSpecs = parseFieldSpecs; |
|
exports.parseMarker = parse; |
|
exports.pointInsideRect = pointInsideRect; |
|
exports.preventContextMenu = preventContextMenu; |
|
exports.preventDefault = preventDefault; |
|
exports.preventSelection = preventSelection; |
|
exports.rangeContainsMarker = rangeContainsMarker; |
|
exports.rangeContainsRange = rangeContainsRange; |
|
exports.rangesEqual = rangesEqual; |
|
exports.rangesIntersect = rangesIntersect; |
|
exports.refineEventDef = refineEventDef; |
|
exports.refineProps = refineProps; |
|
exports.removeElement = removeElement; |
|
exports.removeExact = removeExact; |
|
exports.render = render; |
|
exports.renderChunkContent = renderChunkContent; |
|
exports.renderFill = renderFill; |
|
exports.renderMicroColGroup = renderMicroColGroup; |
|
exports.renderScrollShim = renderScrollShim; |
|
exports.requestJson = requestJson; |
|
exports.sanitizeShrinkWidth = sanitizeShrinkWidth; |
|
exports.setElSeg = setElSeg; |
|
exports.setRef = setRef; |
|
exports.sliceEventStore = sliceEventStore; |
|
exports.sliceEvents = sliceEvents; |
|
exports.sortEventSegs = sortEventSegs; |
|
exports.startOfDay = startOfDay; |
|
exports.translateRect = translateRect; |
|
exports.triggerDateSelect = triggerDateSelect; |
|
exports.unmountComponentAtNode = unmountComponentAtNode$1; |
|
exports.unpromisify = unpromisify; |
|
exports.version = version; |
|
exports.whenTransitionDone = whenTransitionDone; |
|
exports.wholeDivideDurations = wholeDivideDurations; |
|
|
|
Object.defineProperty(exports, '__esModule', { value: true }); |
|
|
|
return exports; |
|
|
|
}({}));
|
|
|