2031 lines
94 KiB
JavaScript
2031 lines
94 KiB
JavaScript
|
/*!
|
||
|
FullCalendar Timeline Plugin v4.4.3
|
||
|
Docs & License: https://fullcalendar.io/scheduler
|
||
|
(c) 2019 Adam Shaw
|
||
|
*/
|
||
|
|
||
|
(function (global, factory) {
|
||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
|
||
|
typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
|
||
|
(global = global || self, factory(global.FullCalendarTimeline = {}, global.FullCalendar));
|
||
|
}(this, function (exports, core) { '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 (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||
|
return extendStatics(d, b);
|
||
|
};
|
||
|
|
||
|
function __extends(d, b) {
|
||
|
extendStatics(d, b);
|
||
|
function __() { this.constructor = d; }
|
||
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||
|
}
|
||
|
|
||
|
var __assign = function() {
|
||
|
__assign = Object.assign || function __assign(t) {
|
||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||
|
s = arguments[i];
|
||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
||
|
}
|
||
|
return t;
|
||
|
};
|
||
|
return __assign.apply(this, arguments);
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
A rectangular area of content that lives within a Scroller.
|
||
|
Can have "gutters", areas of dead spacing around the perimeter.
|
||
|
Also very useful for forcing a width, which a Scroller cannot do alone.
|
||
|
Has a content area that lives above a background area.
|
||
|
*/
|
||
|
var ScrollerCanvas = /** @class */ (function () {
|
||
|
function ScrollerCanvas() {
|
||
|
this.gutters = {};
|
||
|
this.el = core.htmlToElement("<div class=\"fc-scroller-canvas\"> <div class=\"fc-content\"></div> <div class=\"fc-bg\"></div> </div>");
|
||
|
this.contentEl = this.el.querySelector('.fc-content');
|
||
|
this.bgEl = this.el.querySelector('.fc-bg');
|
||
|
}
|
||
|
/*
|
||
|
If falsy, resets all the gutters to 0
|
||
|
*/
|
||
|
ScrollerCanvas.prototype.setGutters = function (gutters) {
|
||
|
if (!gutters) {
|
||
|
this.gutters = {};
|
||
|
}
|
||
|
else {
|
||
|
__assign(this.gutters, gutters);
|
||
|
}
|
||
|
this.updateSize();
|
||
|
};
|
||
|
ScrollerCanvas.prototype.setWidth = function (width) {
|
||
|
this.width = width;
|
||
|
this.updateSize();
|
||
|
};
|
||
|
ScrollerCanvas.prototype.setMinWidth = function (minWidth) {
|
||
|
this.minWidth = minWidth;
|
||
|
this.updateSize();
|
||
|
};
|
||
|
ScrollerCanvas.prototype.clearWidth = function () {
|
||
|
this.width = null;
|
||
|
this.minWidth = null;
|
||
|
this.updateSize();
|
||
|
};
|
||
|
ScrollerCanvas.prototype.updateSize = function () {
|
||
|
var _a = this, gutters = _a.gutters, el = _a.el;
|
||
|
// is border-box (width includes padding)
|
||
|
core.forceClassName(el, 'fc-gutter-left', gutters.left);
|
||
|
core.forceClassName(el, 'fc-gutter-right', gutters.right);
|
||
|
core.forceClassName(el, 'fc-gutter-top', gutters.top);
|
||
|
core.forceClassName(el, 'fc-gutter-bottom', gutters.bottom);
|
||
|
core.applyStyle(el, {
|
||
|
paddingLeft: gutters.left || '',
|
||
|
paddingRight: gutters.right || '',
|
||
|
paddingTop: gutters.top || '',
|
||
|
paddingBottom: gutters.bottom || '',
|
||
|
width: (this.width != null) ?
|
||
|
this.width + (gutters.left || 0) + (gutters.right || 0) :
|
||
|
'',
|
||
|
minWidth: (this.minWidth != null) ?
|
||
|
this.minWidth + (gutters.left || 0) + (gutters.right || 0) :
|
||
|
''
|
||
|
});
|
||
|
core.applyStyle(this.bgEl, {
|
||
|
left: gutters.left || '',
|
||
|
right: gutters.right || '',
|
||
|
top: gutters.top || '',
|
||
|
bottom: gutters.bottom || ''
|
||
|
});
|
||
|
};
|
||
|
return ScrollerCanvas;
|
||
|
}());
|
||
|
|
||
|
var EnhancedScroller = /** @class */ (function (_super) {
|
||
|
__extends(EnhancedScroller, _super);
|
||
|
function EnhancedScroller(overflowX, overflowY) {
|
||
|
var _this = _super.call(this, overflowX, overflowY) || this;
|
||
|
// Scroll Events
|
||
|
// ----------------------------------------------------------------------------------------------
|
||
|
_this.reportScroll = function () {
|
||
|
if (!_this.isScrolling) {
|
||
|
_this.reportScrollStart();
|
||
|
}
|
||
|
_this.trigger('scroll');
|
||
|
_this.isMoving = true;
|
||
|
_this.requestMovingEnd();
|
||
|
};
|
||
|
_this.reportScrollStart = function () {
|
||
|
if (!_this.isScrolling) {
|
||
|
_this.isScrolling = true;
|
||
|
_this.trigger('scrollStart', _this.isTouching); // created in constructor
|
||
|
}
|
||
|
};
|
||
|
// Touch Events
|
||
|
// ----------------------------------------------------------------------------------------------
|
||
|
// will fire *before* the scroll event is fired
|
||
|
_this.reportTouchStart = function () {
|
||
|
_this.isTouching = true;
|
||
|
};
|
||
|
_this.reportTouchEnd = function () {
|
||
|
if (_this.isTouching) {
|
||
|
_this.isTouching = false;
|
||
|
// if touch scrolling was re-enabled during a recent touch scroll
|
||
|
// then unbind the handlers that are preventing it from happening.
|
||
|
if (_this.isTouchScrollEnabled) {
|
||
|
_this.unbindPreventTouchScroll(); // won't do anything if not bound
|
||
|
}
|
||
|
// if the user ended their touch, and the scroll area wasn't moving,
|
||
|
// we consider this to be the end of the scroll.
|
||
|
if (!_this.isMoving) {
|
||
|
_this.reportScrollEnd(); // won't fire if already ended
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
_this.isScrolling = false;
|
||
|
_this.isTouching = false;
|
||
|
_this.isMoving = false;
|
||
|
_this.isTouchScrollEnabled = true;
|
||
|
_this.requestMovingEnd = core.debounce(_this.reportMovingEnd, 500);
|
||
|
_this.canvas = new ScrollerCanvas();
|
||
|
_this.el.appendChild(_this.canvas.el);
|
||
|
_this.applyOverflow();
|
||
|
_this.bindHandlers();
|
||
|
return _this;
|
||
|
}
|
||
|
EnhancedScroller.prototype.destroy = function () {
|
||
|
_super.prototype.destroy.call(this);
|
||
|
this.unbindHandlers();
|
||
|
};
|
||
|
// Touch scroll prevention
|
||
|
// ----------------------------------------------------------------------------------------------
|
||
|
EnhancedScroller.prototype.disableTouchScroll = function () {
|
||
|
this.isTouchScrollEnabled = false;
|
||
|
this.bindPreventTouchScroll(); // will be unbound in enableTouchScroll or reportTouchEnd
|
||
|
};
|
||
|
EnhancedScroller.prototype.enableTouchScroll = function () {
|
||
|
this.isTouchScrollEnabled = true;
|
||
|
// only immediately unbind if a touch event is NOT in progress.
|
||
|
// otherwise, it will be handled by reportTouchEnd.
|
||
|
if (!this.isTouching) {
|
||
|
this.unbindPreventTouchScroll();
|
||
|
}
|
||
|
};
|
||
|
EnhancedScroller.prototype.bindPreventTouchScroll = function () {
|
||
|
if (!this.preventTouchScrollHandler) {
|
||
|
this.el.addEventListener('touchmove', (this.preventTouchScrollHandler = core.preventDefault));
|
||
|
}
|
||
|
};
|
||
|
EnhancedScroller.prototype.unbindPreventTouchScroll = function () {
|
||
|
if (this.preventTouchScrollHandler) {
|
||
|
this.el.removeEventListener('touchmove', this.preventTouchScrollHandler);
|
||
|
this.preventTouchScrollHandler = null;
|
||
|
}
|
||
|
};
|
||
|
// Handlers
|
||
|
// ----------------------------------------------------------------------------------------------
|
||
|
EnhancedScroller.prototype.bindHandlers = function () {
|
||
|
this.el.addEventListener('scroll', this.reportScroll);
|
||
|
this.el.addEventListener('touchstart', this.reportTouchStart, { passive: true });
|
||
|
this.el.addEventListener('touchend', this.reportTouchEnd);
|
||
|
};
|
||
|
EnhancedScroller.prototype.unbindHandlers = function () {
|
||
|
this.el.removeEventListener('scroll', this.reportScroll);
|
||
|
this.el.removeEventListener('touchstart', this.reportTouchStart, { passive: true });
|
||
|
this.el.removeEventListener('touchend', this.reportTouchEnd);
|
||
|
};
|
||
|
EnhancedScroller.prototype.reportMovingEnd = function () {
|
||
|
this.isMoving = false;
|
||
|
// only end the scroll if not currently touching.
|
||
|
// if touching, the scrolling will end later, on touchend.
|
||
|
if (!this.isTouching) {
|
||
|
this.reportScrollEnd();
|
||
|
}
|
||
|
};
|
||
|
EnhancedScroller.prototype.reportScrollEnd = function () {
|
||
|
if (this.isScrolling) {
|
||
|
this.trigger('scrollEnd');
|
||
|
this.isScrolling = false;
|
||
|
}
|
||
|
};
|
||
|
// Horizontal Scroll Normalization
|
||
|
// ----------------------------------------------------------------------------------------------
|
||
|
// http://stackoverflow.com/questions/24276619/better-way-to-get-the-viewport-of-a-scrollable-div-in-rtl-mode/24394376#24394376
|
||
|
// TODO: move all this to util functions
|
||
|
/*
|
||
|
If RTL, and scrolled to the left, returns NEGATIVE value (like Firefox)
|
||
|
*/
|
||
|
EnhancedScroller.prototype.getScrollLeft = function () {
|
||
|
var el = this.el;
|
||
|
var direction = window.getComputedStyle(el).direction;
|
||
|
var val = el.scrollLeft;
|
||
|
if (direction === 'rtl') {
|
||
|
switch (getRtlScrollSystem()) {
|
||
|
case 'positive':
|
||
|
val = (val + el.clientWidth) - el.scrollWidth;
|
||
|
break;
|
||
|
case 'reverse':
|
||
|
val = -val;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return val;
|
||
|
};
|
||
|
/*
|
||
|
Accepts a NEGATIVE value for when scrolled in RTL
|
||
|
*/
|
||
|
EnhancedScroller.prototype.setScrollLeft = function (val) {
|
||
|
var el = this.el;
|
||
|
var direction = window.getComputedStyle(el).direction;
|
||
|
if (direction === 'rtl') {
|
||
|
switch (getRtlScrollSystem()) {
|
||
|
case 'positive':
|
||
|
val = (val - el.clientWidth) + el.scrollWidth;
|
||
|
break;
|
||
|
case 'reverse':
|
||
|
val = -val;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
el.scrollLeft = val;
|
||
|
};
|
||
|
/*
|
||
|
Always returns the number of pixels scrolled from the leftmost position (even if RTL).
|
||
|
Always positive.
|
||
|
*/
|
||
|
EnhancedScroller.prototype.getScrollFromLeft = function () {
|
||
|
var el = this.el;
|
||
|
var direction = window.getComputedStyle(el).direction;
|
||
|
var val = el.scrollLeft;
|
||
|
if (direction === 'rtl') {
|
||
|
switch (getRtlScrollSystem()) {
|
||
|
case 'negative':
|
||
|
val = (val - el.clientWidth) + el.scrollWidth;
|
||
|
break;
|
||
|
case 'reverse':
|
||
|
val = (-val - el.clientWidth) + el.scrollWidth;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return val;
|
||
|
};
|
||
|
return EnhancedScroller;
|
||
|
}(core.ScrollComponent));
|
||
|
core.EmitterMixin.mixInto(EnhancedScroller);
|
||
|
// Horizontal Scroll System Detection
|
||
|
// ----------------------------------------------------------------------------------------------
|
||
|
var _rtlScrollSystem;
|
||
|
function getRtlScrollSystem() {
|
||
|
return _rtlScrollSystem || (_rtlScrollSystem = detectRtlScrollSystem());
|
||
|
}
|
||
|
function detectRtlScrollSystem() {
|
||
|
var el = core.htmlToElement("<div style=\" position: absolute; top: -1000px; width: 1px; height: 1px; overflow: scroll; direction: rtl; font-size: 100px; \">A</div>");
|
||
|
document.body.appendChild(el);
|
||
|
var system;
|
||
|
if (el.scrollLeft > 0) {
|
||
|
system = 'positive';
|
||
|
}
|
||
|
else {
|
||
|
el.scrollLeft = 1;
|
||
|
if (el.scrollLeft > 0) {
|
||
|
system = 'reverse';
|
||
|
}
|
||
|
else {
|
||
|
system = 'negative';
|
||
|
}
|
||
|
}
|
||
|
core.removeElement(el);
|
||
|
return system;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
A Scroller, but with a wrapping div that allows "clipping" away of native scrollbars,
|
||
|
giving the appearance that there are no scrollbars.
|
||
|
*/
|
||
|
var ClippedScroller = /** @class */ (function () {
|
||
|
/*
|
||
|
Received overflows can be set to 'clipped', meaning scrollbars shouldn't be visible
|
||
|
to the user, but the area should still scroll.
|
||
|
*/
|
||
|
function ClippedScroller(overflowX, overflowY, parentEl) {
|
||
|
this.isHScrollbarsClipped = false;
|
||
|
this.isVScrollbarsClipped = false;
|
||
|
if (overflowX === 'clipped-scroll') {
|
||
|
overflowX = 'scroll';
|
||
|
this.isHScrollbarsClipped = true;
|
||
|
}
|
||
|
if (overflowY === 'clipped-scroll') {
|
||
|
overflowY = 'scroll';
|
||
|
this.isVScrollbarsClipped = true;
|
||
|
}
|
||
|
this.enhancedScroll = new EnhancedScroller(overflowX, overflowY);
|
||
|
parentEl.appendChild(this.el = core.createElement('div', {
|
||
|
className: 'fc-scroller-clip'
|
||
|
}));
|
||
|
this.el.appendChild(this.enhancedScroll.el);
|
||
|
}
|
||
|
ClippedScroller.prototype.destroy = function () {
|
||
|
core.removeElement(this.el);
|
||
|
};
|
||
|
ClippedScroller.prototype.updateSize = function () {
|
||
|
var enhancedScroll = this.enhancedScroll;
|
||
|
var scrollEl = enhancedScroll.el;
|
||
|
var edges = core.computeEdges(scrollEl);
|
||
|
var cssProps = { marginLeft: 0, marginRight: 0, marginTop: 0, marginBottom: 0 };
|
||
|
// give the inner scrolling div negative margins so that its scrollbars
|
||
|
// are nudged outside of the bounding box of the wrapper, which is overflow:hidden
|
||
|
if (this.isVScrollbarsClipped) {
|
||
|
cssProps.marginLeft = -edges.scrollbarLeft;
|
||
|
cssProps.marginRight = -edges.scrollbarRight;
|
||
|
}
|
||
|
if (this.isHScrollbarsClipped) {
|
||
|
cssProps.marginBottom = -edges.scrollbarBottom;
|
||
|
}
|
||
|
core.applyStyle(scrollEl, cssProps);
|
||
|
// if we are attempting to hide the scrollbars offscreen, OSX/iOS will still
|
||
|
// display the floating scrollbars. attach a className to force-hide them.
|
||
|
if ((this.isHScrollbarsClipped || (enhancedScroll.overflowX === 'hidden')) && // should never show?
|
||
|
(this.isVScrollbarsClipped || (enhancedScroll.overflowY === 'hidden')) && // should never show?
|
||
|
!( // doesn't have any scrollbar mass
|
||
|
edges.scrollbarLeft ||
|
||
|
edges.scrollbarRight ||
|
||
|
edges.scrollbarBottom)) {
|
||
|
scrollEl.classList.add('fc-no-scrollbars');
|
||
|
}
|
||
|
else {
|
||
|
scrollEl.classList.remove('fc-no-scrollbars');
|
||
|
}
|
||
|
};
|
||
|
ClippedScroller.prototype.setHeight = function (height) {
|
||
|
this.enhancedScroll.setHeight(height);
|
||
|
};
|
||
|
/*
|
||
|
Accounts for 'clipped' scrollbars
|
||
|
*/
|
||
|
ClippedScroller.prototype.getScrollbarWidths = function () {
|
||
|
var widths = this.enhancedScroll.getScrollbarWidths();
|
||
|
if (this.isVScrollbarsClipped) {
|
||
|
widths.left = 0;
|
||
|
widths.right = 0;
|
||
|
}
|
||
|
if (this.isHScrollbarsClipped) {
|
||
|
widths.bottom = 0;
|
||
|
}
|
||
|
return widths;
|
||
|
};
|
||
|
return ClippedScroller;
|
||
|
}());
|
||
|
|
||
|
var ScrollJoiner = /** @class */ (function () {
|
||
|
function ScrollJoiner(axis, scrollers) {
|
||
|
this.axis = axis;
|
||
|
this.scrollers = scrollers;
|
||
|
for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
|
||
|
var scroller = _a[_i];
|
||
|
this.initScroller(scroller);
|
||
|
}
|
||
|
}
|
||
|
ScrollJoiner.prototype.initScroller = function (scroller) {
|
||
|
var _this = this;
|
||
|
var enhancedScroll = scroller.enhancedScroll;
|
||
|
// when the user scrolls via mousewheel, we know for sure the target
|
||
|
// scroller should be the master. capture the various x-browser events that fire.
|
||
|
var onScroll = function () {
|
||
|
_this.assignMasterScroller(scroller);
|
||
|
};
|
||
|
'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ').forEach(function (evName) {
|
||
|
enhancedScroll.el.addEventListener(evName, onScroll);
|
||
|
});
|
||
|
enhancedScroll
|
||
|
.on('scrollStart', function () {
|
||
|
if (!_this.masterScroller) {
|
||
|
_this.assignMasterScroller(scroller);
|
||
|
}
|
||
|
})
|
||
|
.on('scroll', function () {
|
||
|
if (scroller === _this.masterScroller) {
|
||
|
for (var _i = 0, _a = _this.scrollers; _i < _a.length; _i++) {
|
||
|
var otherScroller = _a[_i];
|
||
|
if (otherScroller !== scroller) {
|
||
|
switch (_this.axis) {
|
||
|
case 'horizontal':
|
||
|
otherScroller.enhancedScroll.el.scrollLeft = enhancedScroll.el.scrollLeft;
|
||
|
break;
|
||
|
case 'vertical':
|
||
|
otherScroller.enhancedScroll.setScrollTop(enhancedScroll.getScrollTop());
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
.on('scrollEnd', function () {
|
||
|
if (scroller === _this.masterScroller) {
|
||
|
_this.unassignMasterScroller();
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
ScrollJoiner.prototype.assignMasterScroller = function (scroller) {
|
||
|
this.unassignMasterScroller();
|
||
|
this.masterScroller = scroller;
|
||
|
for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
|
||
|
var otherScroller = _a[_i];
|
||
|
if (otherScroller !== scroller) {
|
||
|
otherScroller.enhancedScroll.disableTouchScroll();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
ScrollJoiner.prototype.unassignMasterScroller = function () {
|
||
|
if (this.masterScroller) {
|
||
|
for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
|
||
|
var otherScroller = _a[_i];
|
||
|
otherScroller.enhancedScroll.enableTouchScroll();
|
||
|
}
|
||
|
this.masterScroller = null;
|
||
|
}
|
||
|
};
|
||
|
ScrollJoiner.prototype.update = function () {
|
||
|
var allWidths = this.scrollers.map(function (scroller) { return scroller.getScrollbarWidths(); });
|
||
|
var maxLeft = 0;
|
||
|
var maxRight = 0;
|
||
|
var maxTop = 0;
|
||
|
var maxBottom = 0;
|
||
|
var widths;
|
||
|
var i;
|
||
|
for (var _i = 0, allWidths_1 = allWidths; _i < allWidths_1.length; _i++) {
|
||
|
widths = allWidths_1[_i];
|
||
|
maxLeft = Math.max(maxLeft, widths.left);
|
||
|
maxRight = Math.max(maxRight, widths.right);
|
||
|
maxTop = Math.max(maxTop, widths.top);
|
||
|
maxBottom = Math.max(maxBottom, widths.bottom);
|
||
|
}
|
||
|
for (i = 0; i < this.scrollers.length; i++) {
|
||
|
var scroller = this.scrollers[i];
|
||
|
widths = allWidths[i];
|
||
|
scroller.enhancedScroll.canvas.setGutters(this.axis === 'horizontal' ?
|
||
|
{
|
||
|
left: maxLeft - widths.left,
|
||
|
right: maxRight - widths.right
|
||
|
} :
|
||
|
{
|
||
|
top: maxTop - widths.top,
|
||
|
bottom: maxBottom - widths.bottom
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
return ScrollJoiner;
|
||
|
}());
|
||
|
|
||
|
var HeaderBodyLayout = /** @class */ (function () {
|
||
|
/*
|
||
|
verticalScroll = 'auto' | 'clipped-scroll'
|
||
|
*/
|
||
|
function HeaderBodyLayout(headerContainerEl, bodyContainerEl, verticalScroll) {
|
||
|
this.headerScroller = new ClippedScroller('clipped-scroll', 'hidden', headerContainerEl);
|
||
|
this.bodyScroller = new ClippedScroller('auto', verticalScroll, bodyContainerEl);
|
||
|
this.scrollJoiner = new ScrollJoiner('horizontal', [
|
||
|
this.headerScroller,
|
||
|
this.bodyScroller
|
||
|
]);
|
||
|
}
|
||
|
HeaderBodyLayout.prototype.destroy = function () {
|
||
|
this.headerScroller.destroy();
|
||
|
this.bodyScroller.destroy();
|
||
|
};
|
||
|
HeaderBodyLayout.prototype.setHeight = function (totalHeight, isAuto) {
|
||
|
var bodyHeight;
|
||
|
if (isAuto) {
|
||
|
bodyHeight = 'auto';
|
||
|
}
|
||
|
else {
|
||
|
bodyHeight = totalHeight - this.queryHeadHeight();
|
||
|
}
|
||
|
this.bodyScroller.setHeight(bodyHeight);
|
||
|
this.headerScroller.updateSize(); // adjusts gutters and classNames
|
||
|
this.bodyScroller.updateSize(); // adjusts gutters and classNames
|
||
|
this.scrollJoiner.update();
|
||
|
};
|
||
|
HeaderBodyLayout.prototype.queryHeadHeight = function () {
|
||
|
return this.headerScroller.enhancedScroll.canvas.contentEl.getBoundingClientRect().height;
|
||
|
};
|
||
|
return HeaderBodyLayout;
|
||
|
}());
|
||
|
|
||
|
var TimelineHeader = /** @class */ (function (_super) {
|
||
|
__extends(TimelineHeader, _super);
|
||
|
function TimelineHeader(parentEl) {
|
||
|
var _this = _super.call(this) || this;
|
||
|
_this.parentEl = parentEl;
|
||
|
return _this;
|
||
|
}
|
||
|
TimelineHeader.prototype.firstContext = function (context) {
|
||
|
this.parentEl.appendChild(this.tableEl = core.createElement('table', {
|
||
|
className: context.theme.getClass('tableGrid')
|
||
|
}));
|
||
|
};
|
||
|
TimelineHeader.prototype.destroy = function () {
|
||
|
core.removeElement(this.tableEl);
|
||
|
_super.prototype.destroy.call(this);
|
||
|
};
|
||
|
TimelineHeader.prototype.render = function (props) {
|
||
|
this.renderDates(props.tDateProfile);
|
||
|
};
|
||
|
TimelineHeader.prototype.renderDates = function (tDateProfile) {
|
||
|
var _a = this.context, dateEnv = _a.dateEnv, theme = _a.theme;
|
||
|
var cellRows = tDateProfile.cellRows;
|
||
|
var lastRow = cellRows[cellRows.length - 1];
|
||
|
var isChrono = core.asRoughMs(tDateProfile.labelInterval) > core.asRoughMs(tDateProfile.slotDuration);
|
||
|
var oneDay = core.isSingleDay(tDateProfile.slotDuration);
|
||
|
var html = '<colgroup>';
|
||
|
// needs to be a col for each body slat. header cells will have colspans
|
||
|
for (var i = tDateProfile.slotCnt - 1; i >= 0; i--) {
|
||
|
html += '<col/>';
|
||
|
}
|
||
|
html += '</colgroup>';
|
||
|
html += '<tbody>';
|
||
|
for (var _i = 0, cellRows_1 = cellRows; _i < cellRows_1.length; _i++) {
|
||
|
var rowCells = cellRows_1[_i];
|
||
|
var isLast = rowCells === lastRow;
|
||
|
html += '<tr' + (isChrono && isLast ? ' class="fc-chrono"' : '') + '>';
|
||
|
for (var _b = 0, rowCells_1 = rowCells; _b < rowCells_1.length; _b++) {
|
||
|
var cell = rowCells_1[_b];
|
||
|
var headerCellClassNames = [theme.getClass('widgetHeader')];
|
||
|
if (cell.isWeekStart) {
|
||
|
headerCellClassNames.push('fc-em-cell');
|
||
|
}
|
||
|
if (oneDay) {
|
||
|
headerCellClassNames = headerCellClassNames.concat(core.getDayClasses(cell.date, this.props.dateProfile, this.context, true) // adds "today" class and other day-based classes
|
||
|
);
|
||
|
}
|
||
|
html +=
|
||
|
'<th class="' + headerCellClassNames.join(' ') + '"' +
|
||
|
' data-date="' + dateEnv.formatIso(cell.date, { omitTime: !tDateProfile.isTimeScale, omitTimeZoneOffset: true }) + '"' +
|
||
|
(cell.colspan > 1 ? ' colspan="' + cell.colspan + '"' : '') +
|
||
|
'>' +
|
||
|
'<div class="fc-cell-content">' +
|
||
|
cell.spanHtml +
|
||
|
'</div>' +
|
||
|
'</th>';
|
||
|
}
|
||
|
html += '</tr>';
|
||
|
}
|
||
|
html += '</tbody>';
|
||
|
this.tableEl.innerHTML = html; // TODO: does this work cross-browser?
|
||
|
this.slatColEls = core.findElements(this.tableEl, 'col');
|
||
|
this.innerEls = core.findElements(this.tableEl.querySelector('tr:last-child'), // compound selector won't work because of query-root problem
|
||
|
'th .fc-cell-text');
|
||
|
core.findElements(this.tableEl.querySelectorAll('tr:not(:last-child)'), // compound selector won't work because of query-root problem
|
||
|
'th .fc-cell-text').forEach(function (innerEl) {
|
||
|
innerEl.classList.add('fc-sticky');
|
||
|
});
|
||
|
};
|
||
|
return TimelineHeader;
|
||
|
}(core.Component));
|
||
|
|
||
|
var TimelineSlats = /** @class */ (function (_super) {
|
||
|
__extends(TimelineSlats, _super);
|
||
|
function TimelineSlats(parentEl) {
|
||
|
var _this = _super.call(this) || this;
|
||
|
parentEl.appendChild(_this.el = core.createElement('div', { className: 'fc-slats' }));
|
||
|
return _this;
|
||
|
}
|
||
|
TimelineSlats.prototype.destroy = function () {
|
||
|
core.removeElement(this.el);
|
||
|
_super.prototype.destroy.call(this);
|
||
|
};
|
||
|
TimelineSlats.prototype.render = function (props) {
|
||
|
this.renderDates(props.tDateProfile);
|
||
|
};
|
||
|
TimelineSlats.prototype.renderDates = function (tDateProfile) {
|
||
|
var _a = this.context, calendar = _a.calendar, view = _a.view, theme = _a.theme, dateEnv = _a.dateEnv;
|
||
|
var slotDates = tDateProfile.slotDates, isWeekStarts = tDateProfile.isWeekStarts;
|
||
|
var html = '<table class="' + theme.getClass('tableGrid') + '">' +
|
||
|
'<colgroup>';
|
||
|
for (var i = 0; i < slotDates.length; i++) {
|
||
|
html += '<col/>';
|
||
|
}
|
||
|
html += '</colgroup>';
|
||
|
html += '<tbody><tr>';
|
||
|
for (var i = 0; i < slotDates.length; i++) {
|
||
|
html += this.slatCellHtml(slotDates[i], isWeekStarts[i], tDateProfile);
|
||
|
}
|
||
|
html += '</tr></tbody></table>';
|
||
|
this.el.innerHTML = html;
|
||
|
this.slatColEls = core.findElements(this.el, 'col');
|
||
|
this.slatEls = core.findElements(this.el, 'td');
|
||
|
for (var i = 0; i < slotDates.length; i++) {
|
||
|
calendar.publiclyTrigger('dayRender', [
|
||
|
{
|
||
|
date: dateEnv.toDate(slotDates[i]),
|
||
|
el: this.slatEls[i],
|
||
|
view: view
|
||
|
}
|
||
|
]);
|
||
|
}
|
||
|
this.outerCoordCache = new core.PositionCache(this.el, this.slatEls, true, // isHorizontal
|
||
|
false // isVertical
|
||
|
);
|
||
|
// for the inner divs within the slats
|
||
|
// used for event rendering and scrollTime, to disregard slat border
|
||
|
this.innerCoordCache = new core.PositionCache(this.el, core.findChildren(this.slatEls, 'div'), true, // isHorizontal
|
||
|
false // isVertical
|
||
|
);
|
||
|
};
|
||
|
TimelineSlats.prototype.slatCellHtml = function (date, isEm, tDateProfile) {
|
||
|
var _a = this.context, theme = _a.theme, dateEnv = _a.dateEnv;
|
||
|
var classes;
|
||
|
if (tDateProfile.isTimeScale) {
|
||
|
classes = [];
|
||
|
classes.push(core.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval)) ?
|
||
|
'fc-major' :
|
||
|
'fc-minor');
|
||
|
}
|
||
|
else {
|
||
|
classes = core.getDayClasses(date, this.props.dateProfile, this.context);
|
||
|
classes.push('fc-day');
|
||
|
}
|
||
|
classes.unshift(theme.getClass('widgetContent'));
|
||
|
if (isEm) {
|
||
|
classes.push('fc-em-cell');
|
||
|
}
|
||
|
return '<td class="' + classes.join(' ') + '"' +
|
||
|
' data-date="' + dateEnv.formatIso(date, { omitTime: !tDateProfile.isTimeScale, omitTimeZoneOffset: true }) + '"' +
|
||
|
'><div></div></td>';
|
||
|
};
|
||
|
TimelineSlats.prototype.updateSize = function () {
|
||
|
this.outerCoordCache.build();
|
||
|
this.innerCoordCache.build();
|
||
|
};
|
||
|
TimelineSlats.prototype.positionToHit = function (leftPosition) {
|
||
|
var outerCoordCache = this.outerCoordCache;
|
||
|
var _a = this.context, dateEnv = _a.dateEnv, isRtl = _a.isRtl;
|
||
|
var tDateProfile = this.props.tDateProfile;
|
||
|
var slatIndex = outerCoordCache.leftToIndex(leftPosition);
|
||
|
if (slatIndex != null) {
|
||
|
// somewhat similar to what TimeGrid does. consolidate?
|
||
|
var slatWidth = outerCoordCache.getWidth(slatIndex);
|
||
|
var partial = isRtl ?
|
||
|
(outerCoordCache.rights[slatIndex] - leftPosition) / slatWidth :
|
||
|
(leftPosition - outerCoordCache.lefts[slatIndex]) / slatWidth;
|
||
|
var localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot);
|
||
|
var start = dateEnv.add(tDateProfile.slotDates[slatIndex], core.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
|
||
|
var end = dateEnv.add(start, tDateProfile.snapDuration);
|
||
|
return {
|
||
|
dateSpan: {
|
||
|
range: { start: start, end: end },
|
||
|
allDay: !this.props.tDateProfile.isTimeScale
|
||
|
},
|
||
|
dayEl: this.slatColEls[slatIndex],
|
||
|
left: outerCoordCache.lefts[slatIndex],
|
||
|
right: outerCoordCache.rights[slatIndex]
|
||
|
};
|
||
|
}
|
||
|
return null;
|
||
|
};
|
||
|
return TimelineSlats;
|
||
|
}(core.Component));
|
||
|
|
||
|
var MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
|
||
|
var MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
|
||
|
var MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
|
||
|
core.config.MAX_TIMELINE_SLOTS = 1000;
|
||
|
// potential nice values for slot-duration and interval-duration
|
||
|
var STOCK_SUB_DURATIONS = [
|
||
|
{ years: 1 },
|
||
|
{ months: 1 },
|
||
|
{ days: 1 },
|
||
|
{ hours: 1 },
|
||
|
{ minutes: 30 },
|
||
|
{ minutes: 15 },
|
||
|
{ minutes: 10 },
|
||
|
{ minutes: 5 },
|
||
|
{ minutes: 1 },
|
||
|
{ seconds: 30 },
|
||
|
{ seconds: 15 },
|
||
|
{ seconds: 10 },
|
||
|
{ seconds: 5 },
|
||
|
{ seconds: 1 },
|
||
|
{ milliseconds: 500 },
|
||
|
{ milliseconds: 100 },
|
||
|
{ milliseconds: 10 },
|
||
|
{ milliseconds: 1 }
|
||
|
];
|
||
|
function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
|
||
|
var tDateProfile = {
|
||
|
labelInterval: queryDurationOption(allOptions, 'slotLabelInterval'),
|
||
|
slotDuration: queryDurationOption(allOptions, 'slotDuration')
|
||
|
};
|
||
|
validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
|
||
|
ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
|
||
|
ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
|
||
|
var input = allOptions.slotLabelFormat;
|
||
|
var rawFormats = Array.isArray(input) ?
|
||
|
input
|
||
|
: (input != null) ?
|
||
|
[input]
|
||
|
:
|
||
|
computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
|
||
|
tDateProfile.headerFormats = rawFormats.map(function (rawFormat) {
|
||
|
return core.createFormatter(rawFormat);
|
||
|
});
|
||
|
tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
|
||
|
var largeUnit = null;
|
||
|
if (!tDateProfile.isTimeScale) {
|
||
|
var slotUnit = core.greatestDurationDenominator(tDateProfile.slotDuration).unit;
|
||
|
if (/year|month|week/.test(slotUnit)) {
|
||
|
largeUnit = slotUnit;
|
||
|
}
|
||
|
}
|
||
|
tDateProfile.largeUnit = largeUnit;
|
||
|
tDateProfile.emphasizeWeeks =
|
||
|
core.isSingleDay(tDateProfile.slotDuration) &&
|
||
|
currentRangeAs('weeks', dateProfile, dateEnv) >= 2 &&
|
||
|
!allOptions.businessHours;
|
||
|
/*
|
||
|
console.log('label interval =', timelineView.labelInterval.humanize())
|
||
|
console.log('slot duration =', timelineView.slotDuration.humanize())
|
||
|
console.log('header formats =', timelineView.headerFormats)
|
||
|
console.log('isTimeScale', timelineView.isTimeScale)
|
||
|
console.log('largeUnit', timelineView.largeUnit)
|
||
|
*/
|
||
|
var rawSnapDuration = allOptions.snapDuration;
|
||
|
var snapDuration;
|
||
|
var snapsPerSlot;
|
||
|
if (rawSnapDuration) {
|
||
|
snapDuration = core.createDuration(rawSnapDuration);
|
||
|
snapsPerSlot = core.wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
|
||
|
// ^ TODO: warning if not whole?
|
||
|
}
|
||
|
if (snapsPerSlot == null) {
|
||
|
snapDuration = tDateProfile.slotDuration;
|
||
|
snapsPerSlot = 1;
|
||
|
}
|
||
|
tDateProfile.snapDuration = snapDuration;
|
||
|
tDateProfile.snapsPerSlot = snapsPerSlot;
|
||
|
// more...
|
||
|
var timeWindowMs = core.asRoughMs(dateProfile.maxTime) - core.asRoughMs(dateProfile.minTime);
|
||
|
// TODO: why not use normalizeRange!?
|
||
|
var normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
|
||
|
var normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
|
||
|
// apply minTime/maxTime
|
||
|
// TODO: View should be responsible.
|
||
|
if (tDateProfile.isTimeScale) {
|
||
|
normalizedStart = dateEnv.add(normalizedStart, dateProfile.minTime);
|
||
|
normalizedEnd = dateEnv.add(core.addDays(normalizedEnd, -1), dateProfile.maxTime);
|
||
|
}
|
||
|
tDateProfile.timeWindowMs = timeWindowMs;
|
||
|
tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
|
||
|
var slotDates = [];
|
||
|
var date = normalizedStart;
|
||
|
while (date < normalizedEnd) {
|
||
|
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
|
||
|
slotDates.push(date);
|
||
|
}
|
||
|
date = dateEnv.add(date, tDateProfile.slotDuration);
|
||
|
}
|
||
|
tDateProfile.slotDates = slotDates;
|
||
|
// more...
|
||
|
var snapIndex = -1;
|
||
|
var snapDiff = 0; // index of the diff :(
|
||
|
var snapDiffToIndex = [];
|
||
|
var snapIndexToDiff = [];
|
||
|
date = normalizedStart;
|
||
|
while (date < normalizedEnd) {
|
||
|
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
|
||
|
snapIndex++;
|
||
|
snapDiffToIndex.push(snapIndex);
|
||
|
snapIndexToDiff.push(snapDiff);
|
||
|
}
|
||
|
else {
|
||
|
snapDiffToIndex.push(snapIndex + 0.5);
|
||
|
}
|
||
|
date = dateEnv.add(date, tDateProfile.snapDuration);
|
||
|
snapDiff++;
|
||
|
}
|
||
|
tDateProfile.snapDiffToIndex = snapDiffToIndex;
|
||
|
tDateProfile.snapIndexToDiff = snapIndexToDiff;
|
||
|
tDateProfile.snapCnt = snapIndex + 1; // is always one behind
|
||
|
tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
|
||
|
// more...
|
||
|
tDateProfile.isWeekStarts = buildIsWeekStarts(tDateProfile, dateEnv);
|
||
|
tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv, allOptions);
|
||
|
return tDateProfile;
|
||
|
}
|
||
|
/*
|
||
|
snaps to appropriate unit
|
||
|
*/
|
||
|
function normalizeDate(date, tDateProfile, dateEnv) {
|
||
|
var normalDate = date;
|
||
|
if (!tDateProfile.isTimeScale) {
|
||
|
normalDate = core.startOfDay(normalDate);
|
||
|
if (tDateProfile.largeUnit) {
|
||
|
normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
|
||
|
}
|
||
|
}
|
||
|
return normalDate;
|
||
|
}
|
||
|
/*
|
||
|
snaps to appropriate unit
|
||
|
*/
|
||
|
function normalizeRange(range, tDateProfile, dateEnv) {
|
||
|
if (!tDateProfile.isTimeScale) {
|
||
|
range = core.computeVisibleDayRange(range);
|
||
|
if (tDateProfile.largeUnit) {
|
||
|
var dayRange = range; // preserve original result
|
||
|
range = {
|
||
|
start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
|
||
|
end: dateEnv.startOf(range.end, tDateProfile.largeUnit)
|
||
|
};
|
||
|
// if date is partially through the interval, or is in the same interval as the start,
|
||
|
// make the exclusive end be the *next* interval
|
||
|
if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
|
||
|
range = {
|
||
|
start: range.start,
|
||
|
end: dateEnv.add(range.end, tDateProfile.slotDuration)
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return range;
|
||
|
}
|
||
|
function isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator) {
|
||
|
if (dateProfileGenerator.isHiddenDay(date)) {
|
||
|
return false;
|
||
|
}
|
||
|
else if (tDateProfile.isTimeScale) {
|
||
|
// determine if the time is within minTime/maxTime, which may have wacky values
|
||
|
var day = core.startOfDay(date);
|
||
|
var timeMs = date.valueOf() - day.valueOf();
|
||
|
var ms = timeMs - core.asRoughMs(dateProfile.minTime); // milliseconds since minTime
|
||
|
ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
|
||
|
return ms < tDateProfile.timeWindowMs; // before the maxTime?
|
||
|
}
|
||
|
else {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
function queryDurationOption(allOptions, name) {
|
||
|
var input = allOptions[name];
|
||
|
if (input != null) {
|
||
|
return core.createDuration(input);
|
||
|
}
|
||
|
}
|
||
|
function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
|
||
|
var currentRange = dateProfile.currentRange;
|
||
|
// make sure labelInterval doesn't exceed the max number of cells
|
||
|
if (tDateProfile.labelInterval) {
|
||
|
var labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
|
||
|
if (labelCnt > core.config.MAX_TIMELINE_SLOTS) {
|
||
|
console.warn('slotLabelInterval results in too many cells');
|
||
|
tDateProfile.labelInterval = null;
|
||
|
}
|
||
|
}
|
||
|
// make sure slotDuration doesn't exceed the maximum number of cells
|
||
|
if (tDateProfile.slotDuration) {
|
||
|
var slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
|
||
|
if (slotCnt > core.config.MAX_TIMELINE_SLOTS) {
|
||
|
console.warn('slotDuration results in too many cells');
|
||
|
tDateProfile.slotDuration = null;
|
||
|
}
|
||
|
}
|
||
|
// make sure labelInterval is a multiple of slotDuration
|
||
|
if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
|
||
|
var slotsPerLabel = core.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
|
||
|
if (slotsPerLabel === null || slotsPerLabel < 1) {
|
||
|
console.warn('slotLabelInterval must be a multiple of slotDuration');
|
||
|
tDateProfile.slotDuration = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
|
||
|
var currentRange = dateProfile.currentRange;
|
||
|
var labelInterval = tDateProfile.labelInterval;
|
||
|
if (!labelInterval) {
|
||
|
// compute based off the slot duration
|
||
|
// find the largest label interval with an acceptable slots-per-label
|
||
|
var input = void 0;
|
||
|
if (tDateProfile.slotDuration) {
|
||
|
for (var _i = 0, STOCK_SUB_DURATIONS_1 = STOCK_SUB_DURATIONS; _i < STOCK_SUB_DURATIONS_1.length; _i++) {
|
||
|
input = STOCK_SUB_DURATIONS_1[_i];
|
||
|
var tryLabelInterval = core.createDuration(input);
|
||
|
var slotsPerLabel = core.wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
|
||
|
if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
|
||
|
labelInterval = tryLabelInterval;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// use the slot duration as a last resort
|
||
|
if (!labelInterval) {
|
||
|
labelInterval = tDateProfile.slotDuration;
|
||
|
}
|
||
|
// compute based off the view's duration
|
||
|
// find the largest label interval that yields the minimum number of labels
|
||
|
}
|
||
|
else {
|
||
|
for (var _a = 0, STOCK_SUB_DURATIONS_2 = STOCK_SUB_DURATIONS; _a < STOCK_SUB_DURATIONS_2.length; _a++) {
|
||
|
input = STOCK_SUB_DURATIONS_2[_a];
|
||
|
labelInterval = core.createDuration(input);
|
||
|
var labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
|
||
|
if (labelCnt >= MIN_AUTO_LABELS) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
tDateProfile.labelInterval = labelInterval;
|
||
|
}
|
||
|
return labelInterval;
|
||
|
}
|
||
|
function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
|
||
|
var currentRange = dateProfile.currentRange;
|
||
|
var slotDuration = tDateProfile.slotDuration;
|
||
|
if (!slotDuration) {
|
||
|
var labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
|
||
|
// compute based off the label interval
|
||
|
// find the largest slot duration that is different from labelInterval, but still acceptable
|
||
|
for (var _i = 0, STOCK_SUB_DURATIONS_3 = STOCK_SUB_DURATIONS; _i < STOCK_SUB_DURATIONS_3.length; _i++) {
|
||
|
var input = STOCK_SUB_DURATIONS_3[_i];
|
||
|
var trySlotDuration = core.createDuration(input);
|
||
|
var slotsPerLabel = core.wholeDivideDurations(labelInterval, trySlotDuration);
|
||
|
if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
|
||
|
slotDuration = trySlotDuration;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// only allow the value if it won't exceed the view's # of slots limit
|
||
|
if (slotDuration) {
|
||
|
var slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
|
||
|
if (slotCnt > MAX_AUTO_CELLS) {
|
||
|
slotDuration = null;
|
||
|
}
|
||
|
}
|
||
|
// use the label interval as a last resort
|
||
|
if (!slotDuration) {
|
||
|
slotDuration = labelInterval;
|
||
|
}
|
||
|
tDateProfile.slotDuration = slotDuration;
|
||
|
}
|
||
|
return slotDuration;
|
||
|
}
|
||
|
function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions) {
|
||
|
var format1;
|
||
|
var format2;
|
||
|
var labelInterval = tDateProfile.labelInterval;
|
||
|
var unit = core.greatestDurationDenominator(labelInterval).unit;
|
||
|
var weekNumbersVisible = allOptions.weekNumbers;
|
||
|
var format0 = (format1 = (format2 = null));
|
||
|
// NOTE: weekNumber computation function wont work
|
||
|
if ((unit === 'week') && !weekNumbersVisible) {
|
||
|
unit = 'day';
|
||
|
}
|
||
|
switch (unit) {
|
||
|
case 'year':
|
||
|
format0 = { year: 'numeric' }; // '2015'
|
||
|
break;
|
||
|
case 'month':
|
||
|
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
|
||
|
format0 = { year: 'numeric' }; // '2015'
|
||
|
}
|
||
|
format1 = { month: 'short' }; // 'Jan'
|
||
|
break;
|
||
|
case 'week':
|
||
|
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
|
||
|
format0 = { year: 'numeric' }; // '2015'
|
||
|
}
|
||
|
format1 = { week: 'narrow' }; // 'Wk4'
|
||
|
break;
|
||
|
case 'day':
|
||
|
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
|
||
|
format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
|
||
|
}
|
||
|
else if (currentRangeAs('months', dateProfile, dateEnv) > 1) {
|
||
|
format0 = { month: 'long' }; // 'January'
|
||
|
}
|
||
|
if (weekNumbersVisible) {
|
||
|
format1 = { week: 'short' }; // 'Wk 4'
|
||
|
}
|
||
|
format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
|
||
|
break;
|
||
|
case 'hour':
|
||
|
if (weekNumbersVisible) {
|
||
|
format0 = { week: 'short' }; // 'Wk 4'
|
||
|
}
|
||
|
if (currentRangeAs('days', dateProfile, dateEnv) > 1) {
|
||
|
format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
|
||
|
}
|
||
|
format2 = {
|
||
|
hour: 'numeric',
|
||
|
minute: '2-digit',
|
||
|
omitZeroMinute: true,
|
||
|
meridiem: 'short'
|
||
|
};
|
||
|
break;
|
||
|
case 'minute':
|
||
|
// sufficiently large number of different minute cells?
|
||
|
if ((core.asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
|
||
|
format0 = {
|
||
|
hour: 'numeric',
|
||
|
meridiem: 'short'
|
||
|
};
|
||
|
format1 = function (params) {
|
||
|
return ':' + core.padStart(params.date.minute, 2); // ':30'
|
||
|
};
|
||
|
}
|
||
|
else {
|
||
|
format0 = {
|
||
|
hour: 'numeric',
|
||
|
minute: 'numeric',
|
||
|
meridiem: 'short'
|
||
|
};
|
||
|
}
|
||
|
break;
|
||
|
case 'second':
|
||
|
// sufficiently large number of different second cells?
|
||
|
if ((core.asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
|
||
|
format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
|
||
|
format1 = function (params) {
|
||
|
return ':' + core.padStart(params.date.second, 2); // ':30'
|
||
|
};
|
||
|
}
|
||
|
else {
|
||
|
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
|
||
|
}
|
||
|
break;
|
||
|
case 'millisecond':
|
||
|
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
|
||
|
format1 = function (params) {
|
||
|
return '.' + core.padStart(params.millisecond, 3);
|
||
|
};
|
||
|
break;
|
||
|
}
|
||
|
return [].concat(format0 || [], format1 || [], format2 || []);
|
||
|
}
|
||
|
// Compute the number of the give units in the "current" range.
|
||
|
// Won't go more precise than days.
|
||
|
// Will return `0` if there's not a clean whole interval.
|
||
|
function currentRangeAs(unit, dateProfile, dateEnv) {
|
||
|
var range = dateProfile.currentRange;
|
||
|
var res = null;
|
||
|
if (unit === 'years') {
|
||
|
res = dateEnv.diffWholeYears(range.start, range.end);
|
||
|
}
|
||
|
else if (unit === 'months') {
|
||
|
res = dateEnv.diffWholeMonths(range.start, range.end);
|
||
|
}
|
||
|
else if (unit === 'weeks') {
|
||
|
res = dateEnv.diffWholeMonths(range.start, range.end);
|
||
|
}
|
||
|
else if (unit === 'days') {
|
||
|
res = core.diffWholeDays(range.start, range.end);
|
||
|
}
|
||
|
return res || 0;
|
||
|
}
|
||
|
function buildIsWeekStarts(tDateProfile, dateEnv) {
|
||
|
var slotDates = tDateProfile.slotDates, emphasizeWeeks = tDateProfile.emphasizeWeeks;
|
||
|
var prevWeekNumber = null;
|
||
|
var isWeekStarts = [];
|
||
|
for (var _i = 0, slotDates_1 = slotDates; _i < slotDates_1.length; _i++) {
|
||
|
var slotDate = slotDates_1[_i];
|
||
|
var weekNumber = dateEnv.computeWeekNumber(slotDate);
|
||
|
var isWeekStart = emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber);
|
||
|
prevWeekNumber = weekNumber;
|
||
|
isWeekStarts.push(isWeekStart);
|
||
|
}
|
||
|
return isWeekStarts;
|
||
|
}
|
||
|
function buildCellRows(tDateProfile, dateEnv, allOptions) {
|
||
|
var slotDates = tDateProfile.slotDates;
|
||
|
var formats = tDateProfile.headerFormats;
|
||
|
var cellRows = formats.map(function (format) { return []; }); // indexed by row,col
|
||
|
// specifically for navclicks
|
||
|
var rowUnits = formats.map(function (format) {
|
||
|
return format.getLargestUnit ? format.getLargestUnit() : null;
|
||
|
});
|
||
|
// builds cellRows and slotCells
|
||
|
for (var i = 0; i < slotDates.length; i++) {
|
||
|
var date = slotDates[i];
|
||
|
var isWeekStart = tDateProfile.isWeekStarts[i];
|
||
|
for (var row = 0; row < formats.length; row++) {
|
||
|
var format = formats[row];
|
||
|
var rowCells = cellRows[row];
|
||
|
var leadingCell = rowCells[rowCells.length - 1];
|
||
|
var isSuperRow = (formats.length > 1) && (row < (formats.length - 1)); // more than one row and not the last
|
||
|
var newCell = null;
|
||
|
if (isSuperRow) {
|
||
|
var text = dateEnv.format(date, format);
|
||
|
if (!leadingCell || (leadingCell.text !== text)) {
|
||
|
newCell = buildCellObject(date, text, rowUnits[row], allOptions, dateEnv);
|
||
|
}
|
||
|
else {
|
||
|
leadingCell.colspan += 1;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (!leadingCell ||
|
||
|
core.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
|
||
|
var text = dateEnv.format(date, format);
|
||
|
newCell = buildCellObject(date, text, rowUnits[row], allOptions, dateEnv);
|
||
|
}
|
||
|
else {
|
||
|
leadingCell.colspan += 1;
|
||
|
}
|
||
|
}
|
||
|
if (newCell) {
|
||
|
newCell.weekStart = isWeekStart;
|
||
|
rowCells.push(newCell);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return cellRows;
|
||
|
}
|
||
|
function buildCellObject(date, text, rowUnit, allOptions, dateEnv) {
|
||
|
var spanHtml = core.buildGotoAnchorHtml(allOptions, dateEnv, {
|
||
|
date: date,
|
||
|
type: rowUnit,
|
||
|
forceOff: !rowUnit
|
||
|
}, {
|
||
|
'class': 'fc-cell-text'
|
||
|
}, core.htmlEscape(text));
|
||
|
return { text: text, spanHtml: spanHtml, date: date, colspan: 1, isWeekStart: false };
|
||
|
}
|
||
|
|
||
|
var TimelineNowIndicator = /** @class */ (function () {
|
||
|
function TimelineNowIndicator(headParent, bodyParent) {
|
||
|
this.headParent = headParent;
|
||
|
this.bodyParent = bodyParent;
|
||
|
}
|
||
|
TimelineNowIndicator.prototype.render = function (coord, isRtl) {
|
||
|
var styleProps = isRtl ? { right: -coord } : { left: coord };
|
||
|
this.headParent.appendChild(this.arrowEl = core.createElement('div', {
|
||
|
className: 'fc-now-indicator fc-now-indicator-arrow',
|
||
|
style: styleProps
|
||
|
}));
|
||
|
this.bodyParent.appendChild(this.lineEl = core.createElement('div', {
|
||
|
className: 'fc-now-indicator fc-now-indicator-line',
|
||
|
style: styleProps
|
||
|
}));
|
||
|
};
|
||
|
TimelineNowIndicator.prototype.unrender = function () {
|
||
|
if (this.arrowEl) {
|
||
|
core.removeElement(this.arrowEl);
|
||
|
}
|
||
|
if (this.lineEl) {
|
||
|
core.removeElement(this.lineEl);
|
||
|
}
|
||
|
};
|
||
|
return TimelineNowIndicator;
|
||
|
}());
|
||
|
|
||
|
var STICKY_PROP_VAL = computeStickyPropVal(); // if null, means not supported at all
|
||
|
var IS_MS_EDGE = /Edge/.test(navigator.userAgent);
|
||
|
var IS_SAFARI = STICKY_PROP_VAL === '-webkit-sticky'; // good b/c doesn't confuse chrome
|
||
|
var STICKY_CLASSNAME = 'fc-sticky';
|
||
|
/*
|
||
|
useful beyond the native position:sticky for these reasons:
|
||
|
- support in IE11
|
||
|
- nice centering support
|
||
|
*/
|
||
|
var StickyScroller = /** @class */ (function () {
|
||
|
function StickyScroller(scroller, isRtl, isVertical) {
|
||
|
var _this = this;
|
||
|
this.usingRelative = null;
|
||
|
/*
|
||
|
known bug: called twice on init. problem when mixing with ScrollJoiner
|
||
|
*/
|
||
|
this.updateSize = function () {
|
||
|
var els = Array.prototype.slice.call(_this.scroller.canvas.el.querySelectorAll('.' + STICKY_CLASSNAME));
|
||
|
var elGeoms = _this.queryElGeoms(els);
|
||
|
var viewportWidth = _this.scroller.el.clientWidth;
|
||
|
if (_this.usingRelative) {
|
||
|
var elDestinations = _this.computeElDestinations(elGeoms, viewportWidth); // read before prepPositioning
|
||
|
assignRelativePositions(els, elGeoms, elDestinations);
|
||
|
}
|
||
|
else {
|
||
|
assignStickyPositions(els, elGeoms, viewportWidth);
|
||
|
}
|
||
|
};
|
||
|
this.scroller = scroller;
|
||
|
this.usingRelative =
|
||
|
!STICKY_PROP_VAL || // IE11
|
||
|
(IS_MS_EDGE && isRtl) || // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/18883305/
|
||
|
((IS_MS_EDGE || IS_SAFARI) && isVertical); // because doesn't work with rowspan in tables, our only vertial use
|
||
|
if (this.usingRelative) {
|
||
|
scroller.on('scrollEnd', this.updateSize);
|
||
|
}
|
||
|
}
|
||
|
StickyScroller.prototype.destroy = function () {
|
||
|
this.scroller.off('scrollEnd', this.updateSize);
|
||
|
};
|
||
|
StickyScroller.prototype.queryElGeoms = function (els) {
|
||
|
var canvasOrigin = this.scroller.canvas.el.getBoundingClientRect();
|
||
|
var elGeoms = [];
|
||
|
for (var _i = 0, els_1 = els; _i < els_1.length; _i++) {
|
||
|
var el = els_1[_i];
|
||
|
var parentBound = core.translateRect(el.parentNode.getBoundingClientRect(), -canvasOrigin.left, -canvasOrigin.top);
|
||
|
var elRect = el.getBoundingClientRect();
|
||
|
var computedStyles = window.getComputedStyle(el);
|
||
|
var computedTextAlign = window.getComputedStyle(el.parentNode).textAlign; // ask the parent
|
||
|
var intendedTextAlign = computedTextAlign;
|
||
|
var naturalBound = null;
|
||
|
if (computedStyles.position !== 'sticky') {
|
||
|
naturalBound = core.translateRect(elRect, -canvasOrigin.left - (parseFloat(computedStyles.left) || 0), // could be 'auto'
|
||
|
-canvasOrigin.top - (parseFloat(computedStyles.top) || 0));
|
||
|
}
|
||
|
if (el.hasAttribute('data-sticky-center')) {
|
||
|
intendedTextAlign = 'center';
|
||
|
}
|
||
|
elGeoms.push({
|
||
|
parentBound: parentBound,
|
||
|
naturalBound: naturalBound,
|
||
|
elWidth: elRect.width,
|
||
|
elHeight: elRect.height,
|
||
|
computedTextAlign: computedTextAlign,
|
||
|
intendedTextAlign: intendedTextAlign
|
||
|
});
|
||
|
}
|
||
|
return elGeoms;
|
||
|
};
|
||
|
StickyScroller.prototype.computeElDestinations = function (elGeoms, viewportWidth) {
|
||
|
var viewportLeft = this.scroller.getScrollFromLeft();
|
||
|
var viewportTop = this.scroller.getScrollTop();
|
||
|
var viewportRight = viewportLeft + viewportWidth;
|
||
|
return elGeoms.map(function (elGeom) {
|
||
|
var elWidth = elGeom.elWidth, elHeight = elGeom.elHeight, parentBound = elGeom.parentBound, naturalBound = elGeom.naturalBound;
|
||
|
var destLeft; // relative to canvas topleft
|
||
|
var destTop; // "
|
||
|
switch (elGeom.intendedTextAlign) {
|
||
|
case 'left':
|
||
|
destLeft = viewportLeft;
|
||
|
break;
|
||
|
case 'right':
|
||
|
destLeft = viewportRight - elWidth;
|
||
|
break;
|
||
|
case 'center':
|
||
|
destLeft = (viewportLeft + viewportRight) / 2 - elWidth / 2;
|
||
|
break;
|
||
|
}
|
||
|
destLeft = Math.min(destLeft, parentBound.right - elWidth);
|
||
|
destLeft = Math.max(destLeft, parentBound.left);
|
||
|
destTop = viewportTop;
|
||
|
destTop = Math.min(destTop, parentBound.bottom - elHeight);
|
||
|
destTop = Math.max(destTop, naturalBound.top); // better to use natural top for upper bound
|
||
|
return { left: destLeft, top: destTop };
|
||
|
});
|
||
|
};
|
||
|
return StickyScroller;
|
||
|
}());
|
||
|
function assignRelativePositions(els, elGeoms, elDestinations) {
|
||
|
els.forEach(function (el, i) {
|
||
|
var naturalBound = elGeoms[i].naturalBound;
|
||
|
core.applyStyle(el, {
|
||
|
position: 'relative',
|
||
|
left: elDestinations[i].left - naturalBound.left,
|
||
|
top: elDestinations[i].top - naturalBound.top
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
function assignStickyPositions(els, elGeoms, viewportWidth) {
|
||
|
els.forEach(function (el, i) {
|
||
|
var stickyLeft = 0;
|
||
|
if (elGeoms[i].intendedTextAlign === 'center') {
|
||
|
stickyLeft = (viewportWidth - elGeoms[i].elWidth) / 2;
|
||
|
// needs to be forced to left?
|
||
|
if (elGeoms[i].computedTextAlign === 'center') {
|
||
|
el.setAttribute('data-sticky-center', '') // remember for next queryElGeoms
|
||
|
;
|
||
|
el.parentNode.style.textAlign = 'left';
|
||
|
}
|
||
|
}
|
||
|
core.applyStyle(el, {
|
||
|
position: STICKY_PROP_VAL,
|
||
|
left: stickyLeft,
|
||
|
right: 0,
|
||
|
top: 0
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
function computeStickyPropVal() {
|
||
|
var el = core.htmlToElement('<div style="position:-webkit-sticky;position:sticky"></div>');
|
||
|
var val = el.style.position;
|
||
|
if (val.indexOf('sticky') !== -1) {
|
||
|
return val;
|
||
|
}
|
||
|
else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var TimeAxis = /** @class */ (function (_super) {
|
||
|
__extends(TimeAxis, _super);
|
||
|
function TimeAxis(headerContainerEl, bodyContainerEl) {
|
||
|
var _this = _super.call(this) || this;
|
||
|
_this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
|
||
|
_this.layout = new HeaderBodyLayout(headerContainerEl, bodyContainerEl, 'auto');
|
||
|
return _this;
|
||
|
}
|
||
|
TimeAxis.prototype.render = function (props, context) {
|
||
|
var tDateProfile = this.tDateProfile =
|
||
|
buildTimelineDateProfile(props.dateProfile, context.dateEnv, context.options, props.dateProfileGenerator); // TODO: cache
|
||
|
this.renderSkeleton(context);
|
||
|
this.header.receiveProps({
|
||
|
dateProfile: props.dateProfile,
|
||
|
tDateProfile: tDateProfile
|
||
|
}, context);
|
||
|
this.slats.receiveProps({
|
||
|
dateProfile: props.dateProfile,
|
||
|
tDateProfile: tDateProfile
|
||
|
}, context);
|
||
|
};
|
||
|
TimeAxis.prototype.destroy = function () {
|
||
|
this.renderSkeleton.unrender();
|
||
|
this.layout.destroy();
|
||
|
_super.prototype.destroy.call(this);
|
||
|
};
|
||
|
TimeAxis.prototype._renderSkeleton = function (context) {
|
||
|
var layout = this.layout;
|
||
|
var headerEnhancedScroller = layout.headerScroller.enhancedScroll;
|
||
|
var bodyEnhancedScroller = layout.bodyScroller.enhancedScroll;
|
||
|
// needs to go after layout, which has ScrollJoiner
|
||
|
this.headStickyScroller = new StickyScroller(headerEnhancedScroller, context.isRtl, false); // isVertical=false
|
||
|
this.bodyStickyScroller = new StickyScroller(bodyEnhancedScroller, context.isRtl, false); // isVertical=false
|
||
|
this.header = new TimelineHeader(headerEnhancedScroller.canvas.contentEl);
|
||
|
this.slats = new TimelineSlats(bodyEnhancedScroller.canvas.bgEl);
|
||
|
this.nowIndicator = new TimelineNowIndicator(headerEnhancedScroller.canvas.el, bodyEnhancedScroller.canvas.el);
|
||
|
};
|
||
|
TimeAxis.prototype._unrenderSkeleton = function () {
|
||
|
this.header.destroy();
|
||
|
this.slats.destroy();
|
||
|
this.nowIndicator.unrender();
|
||
|
this.headStickyScroller.destroy();
|
||
|
this.bodyStickyScroller.destroy();
|
||
|
};
|
||
|
// Now Indicator
|
||
|
// ------------------------------------------------------------------------------------------
|
||
|
TimeAxis.prototype.getNowIndicatorUnit = function (dateProfile, dateProfileGenerator) {
|
||
|
var context = this.context;
|
||
|
// yuck
|
||
|
var tDateProfile = this.tDateProfile =
|
||
|
buildTimelineDateProfile(dateProfile, context.dateEnv, context.options, dateProfileGenerator); // TODO: cache
|
||
|
if (tDateProfile.isTimeScale) {
|
||
|
return core.greatestDurationDenominator(tDateProfile.slotDuration).unit;
|
||
|
}
|
||
|
};
|
||
|
// will only execute if isTimeScale
|
||
|
TimeAxis.prototype.renderNowIndicator = function (date) {
|
||
|
if (core.rangeContainsMarker(this.tDateProfile.normalizedRange, date)) {
|
||
|
this.nowIndicator.render(this.dateToCoord(date), this.context.isRtl);
|
||
|
}
|
||
|
};
|
||
|
// will only execute if isTimeScale
|
||
|
TimeAxis.prototype.unrenderNowIndicator = function () {
|
||
|
this.nowIndicator.unrender();
|
||
|
};
|
||
|
// Sizing
|
||
|
// ------------------------------------------------------------------------------------------
|
||
|
TimeAxis.prototype.updateSize = function (isResize, totalHeight, isAuto) {
|
||
|
this.applySlotWidth(this.computeSlotWidth());
|
||
|
// adjusts gutters. do after slot widths set
|
||
|
this.layout.setHeight(totalHeight, isAuto);
|
||
|
// pretty much just queries coords. do last
|
||
|
this.slats.updateSize();
|
||
|
};
|
||
|
TimeAxis.prototype.updateStickyScrollers = function () {
|
||
|
this.headStickyScroller.updateSize();
|
||
|
this.bodyStickyScroller.updateSize();
|
||
|
};
|
||
|
TimeAxis.prototype.computeSlotWidth = function () {
|
||
|
var slotWidth = this.context.options.slotWidth || '';
|
||
|
if (slotWidth === '') {
|
||
|
slotWidth = this.computeDefaultSlotWidth(this.tDateProfile);
|
||
|
}
|
||
|
return slotWidth;
|
||
|
};
|
||
|
TimeAxis.prototype.computeDefaultSlotWidth = function (tDateProfile) {
|
||
|
var maxInnerWidth = 0; // TODO: harness core's `matchCellWidths` for this
|
||
|
this.header.innerEls.forEach(function (innerEl, i) {
|
||
|
maxInnerWidth = Math.max(maxInnerWidth, innerEl.getBoundingClientRect().width);
|
||
|
});
|
||
|
var headingCellWidth = Math.ceil(maxInnerWidth) + 1; // assume no padding, and one pixel border
|
||
|
// in TimelineView.defaults we ensured that labelInterval is an interval of slotDuration
|
||
|
// TODO: rename labelDuration?
|
||
|
var slotsPerLabel = core.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
|
||
|
var slotWidth = Math.ceil(headingCellWidth / slotsPerLabel);
|
||
|
var minWidth = window.getComputedStyle(this.header.slatColEls[0]).minWidth;
|
||
|
if (minWidth) {
|
||
|
minWidth = parseInt(minWidth, 10);
|
||
|
if (minWidth) {
|
||
|
slotWidth = Math.max(slotWidth, minWidth);
|
||
|
}
|
||
|
}
|
||
|
return slotWidth;
|
||
|
};
|
||
|
TimeAxis.prototype.applySlotWidth = function (slotWidth) {
|
||
|
var _a = this, layout = _a.layout, tDateProfile = _a.tDateProfile;
|
||
|
var containerWidth = '';
|
||
|
var containerMinWidth = '';
|
||
|
var nonLastSlotWidth = '';
|
||
|
if (slotWidth !== '') {
|
||
|
slotWidth = Math.round(slotWidth);
|
||
|
containerWidth = slotWidth * tDateProfile.slotDates.length;
|
||
|
containerMinWidth = '';
|
||
|
nonLastSlotWidth = slotWidth;
|
||
|
var availableWidth = layout.bodyScroller.enhancedScroll.getClientWidth();
|
||
|
if (availableWidth > containerWidth) {
|
||
|
containerMinWidth = availableWidth;
|
||
|
containerWidth = '';
|
||
|
nonLastSlotWidth = Math.floor(availableWidth / tDateProfile.slotDates.length);
|
||
|
}
|
||
|
}
|
||
|
layout.headerScroller.enhancedScroll.canvas.setWidth(containerWidth);
|
||
|
layout.headerScroller.enhancedScroll.canvas.setMinWidth(containerMinWidth);
|
||
|
layout.bodyScroller.enhancedScroll.canvas.setWidth(containerWidth);
|
||
|
layout.bodyScroller.enhancedScroll.canvas.setMinWidth(containerMinWidth);
|
||
|
if (nonLastSlotWidth !== '') {
|
||
|
this.header.slatColEls.slice(0, -1).concat(this.slats.slatColEls.slice(0, -1)).forEach(function (el) {
|
||
|
el.style.width = nonLastSlotWidth + 'px';
|
||
|
el.style.minWidth = '0'; // prevent the browser from using the actual min-width (issue #6338)
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
// returned value is between 0 and the number of snaps
|
||
|
TimeAxis.prototype.computeDateSnapCoverage = function (date) {
|
||
|
var tDateProfile = this.tDateProfile;
|
||
|
var dateEnv = this.context.dateEnv;
|
||
|
var snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
|
||
|
if (snapDiff < 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
else if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
|
||
|
return tDateProfile.snapCnt;
|
||
|
}
|
||
|
else {
|
||
|
var snapDiffInt = Math.floor(snapDiff);
|
||
|
var snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
|
||
|
if (core.isInt(snapCoverage)) { // not an in-between value
|
||
|
snapCoverage += snapDiff - snapDiffInt; // add the remainder
|
||
|
}
|
||
|
else {
|
||
|
// a fractional value, meaning the date is not visible
|
||
|
// always round up in this case. works for start AND end dates in a range.
|
||
|
snapCoverage = Math.ceil(snapCoverage);
|
||
|
}
|
||
|
return snapCoverage;
|
||
|
}
|
||
|
};
|
||
|
// for LTR, results range from 0 to width of area
|
||
|
// for RTL, results range from negative width of area to 0
|
||
|
TimeAxis.prototype.dateToCoord = function (date) {
|
||
|
var tDateProfile = this.tDateProfile;
|
||
|
var snapCoverage = this.computeDateSnapCoverage(date);
|
||
|
var slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
|
||
|
var slotIndex = Math.floor(slotCoverage);
|
||
|
slotIndex = Math.min(slotIndex, tDateProfile.slotCnt - 1);
|
||
|
var partial = slotCoverage - slotIndex;
|
||
|
var _a = this.slats, innerCoordCache = _a.innerCoordCache, outerCoordCache = _a.outerCoordCache;
|
||
|
if (this.context.isRtl) {
|
||
|
return (outerCoordCache.rights[slotIndex] -
|
||
|
(innerCoordCache.getWidth(slotIndex) * partial)) - outerCoordCache.originClientRect.width;
|
||
|
}
|
||
|
else {
|
||
|
return (outerCoordCache.lefts[slotIndex] +
|
||
|
(innerCoordCache.getWidth(slotIndex) * partial));
|
||
|
}
|
||
|
};
|
||
|
TimeAxis.prototype.rangeToCoords = function (range) {
|
||
|
if (this.context.isRtl) {
|
||
|
return { right: this.dateToCoord(range.start), left: this.dateToCoord(range.end) };
|
||
|
}
|
||
|
else {
|
||
|
return { left: this.dateToCoord(range.start), right: this.dateToCoord(range.end) };
|
||
|
}
|
||
|
};
|
||
|
// Scrolling
|
||
|
// ------------------------------------------------------------------------------------------
|
||
|
TimeAxis.prototype.computeDateScroll = function (duration) {
|
||
|
var _a = this.context, dateEnv = _a.dateEnv, isRtl = _a.isRtl;
|
||
|
var dateProfile = this.props.dateProfile;
|
||
|
var left = 0;
|
||
|
if (dateProfile) {
|
||
|
left = this.dateToCoord(dateEnv.add(core.startOfDay(dateProfile.activeRange.start), // startOfDay needed?
|
||
|
duration));
|
||
|
// hack to overcome the left borders of non-first slat
|
||
|
if (!isRtl && left) {
|
||
|
left += 1;
|
||
|
}
|
||
|
}
|
||
|
return { left: left };
|
||
|
};
|
||
|
TimeAxis.prototype.queryDateScroll = function () {
|
||
|
var enhancedScroll = this.layout.bodyScroller.enhancedScroll;
|
||
|
return {
|
||
|
left: enhancedScroll.getScrollLeft()
|
||
|
};
|
||
|
};
|
||
|
TimeAxis.prototype.applyDateScroll = function (scroll) {
|
||
|
// TODO: lame we have to update both. use the scrolljoiner instead maybe
|
||
|
this.layout.bodyScroller.enhancedScroll.setScrollLeft(scroll.left || 0);
|
||
|
this.layout.headerScroller.enhancedScroll.setScrollLeft(scroll.left || 0);
|
||
|
};
|
||
|
return TimeAxis;
|
||
|
}(core.Component));
|
||
|
|
||
|
var TimelineLaneEventRenderer = /** @class */ (function (_super) {
|
||
|
__extends(TimelineLaneEventRenderer, _super);
|
||
|
function TimelineLaneEventRenderer(masterContainerEl, timeAxis) {
|
||
|
var _this = _super.call(this) || this;
|
||
|
_this.masterContainerEl = masterContainerEl;
|
||
|
_this.timeAxis = timeAxis;
|
||
|
return _this;
|
||
|
}
|
||
|
TimelineLaneEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
|
||
|
var context = this.context;
|
||
|
var eventRange = seg.eventRange;
|
||
|
var eventDef = eventRange.def;
|
||
|
var eventUi = eventRange.ui;
|
||
|
var isDraggable = core.computeEventDraggable(context, eventDef, eventUi);
|
||
|
var isResizableFromStart = seg.isStart && core.computeEventStartResizable(context, eventDef, eventUi);
|
||
|
var isResizableFromEnd = seg.isEnd && core.computeEventEndResizable(context, eventDef, eventUi);
|
||
|
var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
|
||
|
classes.unshift('fc-timeline-event', 'fc-h-event');
|
||
|
var timeText = this.getTimeText(eventRange);
|
||
|
return '<a class="' + classes.join(' ') + '" style="' + core.cssToStr(this.getSkinCss(eventUi)) + '"' +
|
||
|
(eventDef.url ?
|
||
|
' href="' + core.htmlEscape(eventDef.url) + '"' :
|
||
|
'') +
|
||
|
'>' +
|
||
|
(timeText ?
|
||
|
'<span class="fc-time-wrap">' +
|
||
|
'<span class="fc-time">' +
|
||
|
core.htmlEscape(timeText) +
|
||
|
'</span>' +
|
||
|
'</span>'
|
||
|
:
|
||
|
'') +
|
||
|
'<span class="fc-title-wrap">' +
|
||
|
'<span class="fc-title fc-sticky">' +
|
||
|
(eventDef.title ? core.htmlEscape(eventDef.title) : ' ') +
|
||
|
'</span>' +
|
||
|
'</span>' +
|
||
|
(isResizableFromStart ?
|
||
|
'<div class="fc-resizer fc-start-resizer"></div>' :
|
||
|
'') +
|
||
|
(isResizableFromEnd ?
|
||
|
'<div class="fc-resizer fc-end-resizer"></div>' :
|
||
|
'') +
|
||
|
'</a>';
|
||
|
};
|
||
|
TimelineLaneEventRenderer.prototype.computeDisplayEventTime = function () {
|
||
|
return !this.timeAxis.tDateProfile.isTimeScale; // because times should be obvious via axis
|
||
|
};
|
||
|
TimelineLaneEventRenderer.prototype.computeDisplayEventEnd = function () {
|
||
|
return false;
|
||
|
};
|
||
|
// Computes a default event time formatting string if `timeFormat` is not explicitly defined
|
||
|
TimelineLaneEventRenderer.prototype.computeEventTimeFormat = function () {
|
||
|
return {
|
||
|
hour: 'numeric',
|
||
|
minute: '2-digit',
|
||
|
omitZeroMinute: true,
|
||
|
meridiem: 'narrow'
|
||
|
};
|
||
|
};
|
||
|
TimelineLaneEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
|
||
|
if (!this.el && this.masterContainerEl) {
|
||
|
this.el = core.createElement('div', { className: 'fc-event-container' });
|
||
|
if (mirrorInfo) {
|
||
|
this.el.classList.add('fc-mirror-container');
|
||
|
}
|
||
|
this.masterContainerEl.appendChild(this.el);
|
||
|
}
|
||
|
if (this.el) {
|
||
|
for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
|
||
|
var seg = segs_1[_i];
|
||
|
this.el.appendChild(seg.el);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
TimelineLaneEventRenderer.prototype.detachSegs = function (segs) {
|
||
|
for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
|
||
|
var seg = segs_2[_i];
|
||
|
core.removeElement(seg.el);
|
||
|
}
|
||
|
};
|
||
|
// computes AND assigns (assigns the left/right at least). bad
|
||
|
TimelineLaneEventRenderer.prototype.computeSegSizes = function (segs) {
|
||
|
var timeAxis = this.timeAxis;
|
||
|
for (var _i = 0, segs_3 = segs; _i < segs_3.length; _i++) {
|
||
|
var seg = segs_3[_i];
|
||
|
var coords = timeAxis.rangeToCoords(seg); // works because Seg has start/end
|
||
|
core.applyStyle(seg.el, {
|
||
|
left: (seg.left = coords.left),
|
||
|
right: -(seg.right = coords.right)
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
TimelineLaneEventRenderer.prototype.assignSegSizes = function (segs) {
|
||
|
if (!this.el) {
|
||
|
return;
|
||
|
}
|
||
|
// compute seg verticals
|
||
|
for (var _i = 0, segs_4 = segs; _i < segs_4.length; _i++) {
|
||
|
var seg = segs_4[_i];
|
||
|
seg.height = core.computeHeightAndMargins(seg.el);
|
||
|
}
|
||
|
this.buildSegLevels(segs); // populates above/below props for computeOffsetForSegs
|
||
|
var totalHeight = computeOffsetForSegs(segs); // also assigns seg.top
|
||
|
core.applyStyleProp(this.el, 'height', totalHeight);
|
||
|
// assign seg verticals
|
||
|
for (var _a = 0, segs_5 = segs; _a < segs_5.length; _a++) {
|
||
|
var seg = segs_5[_a];
|
||
|
core.applyStyleProp(seg.el, 'top', seg.top);
|
||
|
}
|
||
|
};
|
||
|
TimelineLaneEventRenderer.prototype.buildSegLevels = function (segs) {
|
||
|
var segLevels = [];
|
||
|
segs = this.sortEventSegs(segs);
|
||
|
for (var _i = 0, segs_6 = segs; _i < segs_6.length; _i++) {
|
||
|
var unplacedSeg = segs_6[_i];
|
||
|
unplacedSeg.above = [];
|
||
|
// determine the first level with no collisions
|
||
|
var level = 0; // level index
|
||
|
while (level < segLevels.length) {
|
||
|
var isLevelCollision = false;
|
||
|
// determine collisions
|
||
|
for (var _a = 0, _b = segLevels[level]; _a < _b.length; _a++) {
|
||
|
var placedSeg = _b[_a];
|
||
|
if (timeRowSegsCollide(unplacedSeg, placedSeg)) {
|
||
|
unplacedSeg.above.push(placedSeg);
|
||
|
isLevelCollision = true;
|
||
|
}
|
||
|
}
|
||
|
if (isLevelCollision) {
|
||
|
level += 1;
|
||
|
}
|
||
|
else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// insert into the first non-colliding level. create if necessary
|
||
|
(segLevels[level] || (segLevels[level] = []))
|
||
|
.push(unplacedSeg);
|
||
|
// record possible colliding segments below (TODO: automated test for this)
|
||
|
level += 1;
|
||
|
while (level < segLevels.length) {
|
||
|
for (var _c = 0, _d = segLevels[level]; _c < _d.length; _c++) {
|
||
|
var belowSeg = _d[_c];
|
||
|
if (timeRowSegsCollide(unplacedSeg, belowSeg)) {
|
||
|
belowSeg.above.push(unplacedSeg);
|
||
|
}
|
||
|
}
|
||
|
level += 1;
|
||
|
}
|
||
|
}
|
||
|
return segLevels;
|
||
|
};
|
||
|
return TimelineLaneEventRenderer;
|
||
|
}(core.FgEventRenderer));
|
||
|
function computeOffsetForSegs(segs) {
|
||
|
var max = 0;
|
||
|
for (var _i = 0, segs_7 = segs; _i < segs_7.length; _i++) {
|
||
|
var seg = segs_7[_i];
|
||
|
max = Math.max(max, computeOffsetForSeg(seg));
|
||
|
}
|
||
|
return max;
|
||
|
}
|
||
|
function computeOffsetForSeg(seg) {
|
||
|
if ((seg.top == null)) {
|
||
|
seg.top = computeOffsetForSegs(seg.above);
|
||
|
}
|
||
|
return seg.top + seg.height;
|
||
|
}
|
||
|
function timeRowSegsCollide(seg0, seg1) {
|
||
|
return (seg0.left < seg1.right) && (seg0.right > seg1.left);
|
||
|
}
|
||
|
|
||
|
var TimelineLaneFillRenderer = /** @class */ (function (_super) {
|
||
|
__extends(TimelineLaneFillRenderer, _super);
|
||
|
function TimelineLaneFillRenderer(masterContainerEl, timeAxis) {
|
||
|
var _this = _super.call(this) || this;
|
||
|
_this.masterContainerEl = masterContainerEl;
|
||
|
_this.timeAxis = timeAxis;
|
||
|
return _this;
|
||
|
}
|
||
|
TimelineLaneFillRenderer.prototype.attachSegs = function (type, segs) {
|
||
|
if (segs.length) {
|
||
|
var className = void 0;
|
||
|
if (type === 'businessHours') {
|
||
|
className = 'bgevent';
|
||
|
}
|
||
|
else {
|
||
|
className = type.toLowerCase();
|
||
|
}
|
||
|
// making a new container each time is OKAY
|
||
|
// all types of segs (background or business hours or whatever) are rendered in one pass
|
||
|
var containerEl = core.createElement('div', { className: 'fc-' + className + '-container' });
|
||
|
this.masterContainerEl.appendChild(containerEl);
|
||
|
for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
|
||
|
var seg = segs_1[_i];
|
||
|
containerEl.appendChild(seg.el);
|
||
|
}
|
||
|
return [containerEl]; // return value
|
||
|
}
|
||
|
};
|
||
|
TimelineLaneFillRenderer.prototype.computeSegSizes = function (segs) {
|
||
|
var timeAxis = this.timeAxis;
|
||
|
for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
|
||
|
var seg = segs_2[_i];
|
||
|
var coords = timeAxis.rangeToCoords(seg);
|
||
|
seg.left = coords.left;
|
||
|
seg.right = coords.right;
|
||
|
}
|
||
|
};
|
||
|
TimelineLaneFillRenderer.prototype.assignSegSizes = function (segs) {
|
||
|
for (var _i = 0, segs_3 = segs; _i < segs_3.length; _i++) {
|
||
|
var seg = segs_3[_i];
|
||
|
core.applyStyle(seg.el, {
|
||
|
left: seg.left,
|
||
|
right: -seg.right
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
return TimelineLaneFillRenderer;
|
||
|
}(core.FillRenderer));
|
||
|
|
||
|
var TimelineLane = /** @class */ (function (_super) {
|
||
|
__extends(TimelineLane, _super);
|
||
|
function TimelineLane(fgContainerEl, bgContainerEl, timeAxis) {
|
||
|
var _this = _super.call(this, bgContainerEl) || this;
|
||
|
_this.slicer = new TimelineLaneSlicer();
|
||
|
_this.renderEventDrag = core.memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag);
|
||
|
_this.renderEventResize = core.memoizeRendering(_this._renderEventResize, _this._unrenderEventResize);
|
||
|
_this.fgContainerEl = fgContainerEl;
|
||
|
_this.timeAxis = timeAxis;
|
||
|
var fillRenderer = _this.fillRenderer = new TimelineLaneFillRenderer(bgContainerEl, timeAxis);
|
||
|
var eventRenderer = _this.eventRenderer = new TimelineLaneEventRenderer(fgContainerEl, timeAxis);
|
||
|
_this.mirrorRenderer = new TimelineLaneEventRenderer(fgContainerEl, timeAxis);
|
||
|
_this.renderBusinessHours = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'));
|
||
|
_this.renderDateSelection = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'highlight'), fillRenderer.unrender.bind(fillRenderer, 'highlight'));
|
||
|
_this.renderBgEvents = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'));
|
||
|
_this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer));
|
||
|
_this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
|
||
|
return _this;
|
||
|
}
|
||
|
TimelineLane.prototype.render = function (props, context) {
|
||
|
var timeAxis = this.timeAxis;
|
||
|
var slicedProps = this.slicer.sliceProps(props, props.dateProfile, timeAxis.tDateProfile.isTimeScale ? null : props.nextDayThreshold, context.calendar, this, timeAxis);
|
||
|
this.renderBusinessHours(context, slicedProps.businessHourSegs);
|
||
|
this.renderDateSelection(context, slicedProps.dateSelectionSegs);
|
||
|
this.renderBgEvents(context, slicedProps.bgEventSegs);
|
||
|
this.renderFgEvents(context, slicedProps.fgEventSegs);
|
||
|
this.renderEventSelection(slicedProps.eventSelection);
|
||
|
this.renderEventDrag(slicedProps.eventDrag);
|
||
|
this.renderEventResize(slicedProps.eventResize);
|
||
|
};
|
||
|
TimelineLane.prototype.destroy = function () {
|
||
|
_super.prototype.destroy.call(this);
|
||
|
this.renderBusinessHours.unrender();
|
||
|
this.renderDateSelection.unrender();
|
||
|
this.renderBgEvents.unrender();
|
||
|
this.renderFgEvents.unrender();
|
||
|
this.renderEventSelection.unrender();
|
||
|
this.renderEventDrag.unrender();
|
||
|
this.renderEventResize.unrender();
|
||
|
};
|
||
|
TimelineLane.prototype._renderEventDrag = function (state) {
|
||
|
if (state) {
|
||
|
this.eventRenderer.hideByHash(state.affectedInstances);
|
||
|
this.mirrorRenderer.renderSegs(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
|
||
|
}
|
||
|
};
|
||
|
TimelineLane.prototype._unrenderEventDrag = function (state) {
|
||
|
if (state) {
|
||
|
this.eventRenderer.showByHash(state.affectedInstances);
|
||
|
this.mirrorRenderer.unrender(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
|
||
|
}
|
||
|
};
|
||
|
TimelineLane.prototype._renderEventResize = function (state) {
|
||
|
if (state) {
|
||
|
// HACK. eventRenderer and fillRenderer both use these segs. would compete over seg.el
|
||
|
var segsForHighlight = state.segs.map(function (seg) {
|
||
|
return __assign({}, seg);
|
||
|
});
|
||
|
this.eventRenderer.hideByHash(state.affectedInstances);
|
||
|
this.fillRenderer.renderSegs('highlight', this.context, segsForHighlight);
|
||
|
this.mirrorRenderer.renderSegs(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
|
||
|
}
|
||
|
};
|
||
|
TimelineLane.prototype._unrenderEventResize = function (state) {
|
||
|
if (state) {
|
||
|
this.eventRenderer.showByHash(state.affectedInstances);
|
||
|
this.fillRenderer.unrender('highlight', this.context);
|
||
|
this.mirrorRenderer.unrender(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
|
||
|
}
|
||
|
};
|
||
|
TimelineLane.prototype.updateSize = function (isResize) {
|
||
|
var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
|
||
|
fillRenderer.computeSizes(isResize);
|
||
|
eventRenderer.computeSizes(isResize);
|
||
|
mirrorRenderer.computeSizes(isResize);
|
||
|
fillRenderer.assignSizes(isResize);
|
||
|
eventRenderer.assignSizes(isResize);
|
||
|
mirrorRenderer.assignSizes(isResize);
|
||
|
};
|
||
|
return TimelineLane;
|
||
|
}(core.DateComponent));
|
||
|
var TimelineLaneSlicer = /** @class */ (function (_super) {
|
||
|
__extends(TimelineLaneSlicer, _super);
|
||
|
function TimelineLaneSlicer() {
|
||
|
return _super !== null && _super.apply(this, arguments) || this;
|
||
|
}
|
||
|
TimelineLaneSlicer.prototype.sliceRange = function (origRange, timeAxis) {
|
||
|
var tDateProfile = timeAxis.tDateProfile;
|
||
|
var _a = timeAxis.props, dateProfile = _a.dateProfile, dateProfileGenerator = _a.dateProfileGenerator;
|
||
|
var dateEnv = timeAxis.context.dateEnv;
|
||
|
var normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
|
||
|
var segs = [];
|
||
|
// protect against when the span is entirely in an invalid date region
|
||
|
if (timeAxis.computeDateSnapCoverage(normalRange.start) < timeAxis.computeDateSnapCoverage(normalRange.end)) {
|
||
|
// intersect the footprint's range with the grid's range
|
||
|
var slicedRange = core.intersectRanges(normalRange, tDateProfile.normalizedRange);
|
||
|
if (slicedRange) {
|
||
|
segs.push({
|
||
|
start: slicedRange.start,
|
||
|
end: slicedRange.end,
|
||
|
isStart: slicedRange.start.valueOf() === normalRange.start.valueOf() && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
|
||
|
isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf() && isValidDate(core.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator)
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
return segs;
|
||
|
};
|
||
|
return TimelineLaneSlicer;
|
||
|
}(core.Slicer));
|
||
|
|
||
|
var TimelineView = /** @class */ (function (_super) {
|
||
|
__extends(TimelineView, _super);
|
||
|
function TimelineView() {
|
||
|
var _this = _super !== null && _super.apply(this, arguments) || this;
|
||
|
_this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
|
||
|
_this.startInteractive = core.memoizeRendering(_this._startInteractive, _this._stopInteractive);
|
||
|
return _this;
|
||
|
}
|
||
|
TimelineView.prototype._startInteractive = function (timeAxisEl) {
|
||
|
this.context.calendar.registerInteractiveComponent(this, {
|
||
|
el: timeAxisEl
|
||
|
});
|
||
|
};
|
||
|
TimelineView.prototype._stopInteractive = function () {
|
||
|
this.context.calendar.unregisterInteractiveComponent(this);
|
||
|
};
|
||
|
TimelineView.prototype.render = function (props, context) {
|
||
|
_super.prototype.render.call(this, props, context); // flags for updateSize, addScroll. and _renderSkeleton/_unrenderSkeleton
|
||
|
this.renderSkeleton(this.context);
|
||
|
this.timeAxis.receiveProps({
|
||
|
dateProfileGenerator: props.dateProfileGenerator,
|
||
|
dateProfile: props.dateProfile
|
||
|
}, context);
|
||
|
this.startInteractive(this.timeAxis.slats.el);
|
||
|
this.lane.receiveProps(__assign({}, props, { nextDayThreshold: this.context.nextDayThreshold }), context);
|
||
|
this.startNowIndicator(props.dateProfile, props.dateProfileGenerator);
|
||
|
};
|
||
|
TimelineView.prototype.destroy = function () {
|
||
|
this.startInteractive.unrender(); // "unrender" a weird name
|
||
|
this.renderSkeleton.unrender();
|
||
|
_super.prototype.destroy.call(this);
|
||
|
};
|
||
|
TimelineView.prototype._renderSkeleton = function (context) {
|
||
|
this.el.classList.add('fc-timeline');
|
||
|
if (context.options.eventOverlap === false) {
|
||
|
this.el.classList.add('fc-no-overlap');
|
||
|
}
|
||
|
this.el.innerHTML = this.renderSkeletonHtml();
|
||
|
this.timeAxis = new TimeAxis(this.el.querySelector('thead .fc-time-area'), this.el.querySelector('tbody .fc-time-area'));
|
||
|
this.lane = new TimelineLane(this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.contentEl, this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.bgEl, this.timeAxis);
|
||
|
};
|
||
|
TimelineView.prototype._unrenderSkeleton = function () {
|
||
|
this.el.classList.remove('fc-timeline');
|
||
|
this.el.classList.remove('fc-no-overlap');
|
||
|
this.timeAxis.destroy();
|
||
|
this.lane.destroy();
|
||
|
};
|
||
|
TimelineView.prototype.renderSkeletonHtml = function () {
|
||
|
var theme = this.context.theme;
|
||
|
return "<table class=\"" + theme.getClass('tableGrid') + "\"> <thead class=\"fc-head\"> <tr> <td class=\"fc-time-area " + theme.getClass('widgetHeader') + "\"></td> </tr> </thead> <tbody class=\"fc-body\"> <tr> <td class=\"fc-time-area " + theme.getClass('widgetContent') + "\"></td> </tr> </tbody> </table>";
|
||
|
};
|
||
|
TimelineView.prototype.updateSize = function (isResize, totalHeight, isAuto) {
|
||
|
this.timeAxis.updateSize(isResize, totalHeight, isAuto);
|
||
|
this.lane.updateSize(isResize);
|
||
|
};
|
||
|
// Now Indicator
|
||
|
// ------------------------------------------------------------------------------------------
|
||
|
TimelineView.prototype.getNowIndicatorUnit = function (dateProfile, dateProfileGenerator) {
|
||
|
return this.timeAxis.getNowIndicatorUnit(dateProfile, dateProfileGenerator);
|
||
|
};
|
||
|
TimelineView.prototype.renderNowIndicator = function (date) {
|
||
|
this.timeAxis.renderNowIndicator(date);
|
||
|
};
|
||
|
TimelineView.prototype.unrenderNowIndicator = function () {
|
||
|
this.timeAxis.unrenderNowIndicator();
|
||
|
};
|
||
|
// Scroll System
|
||
|
// ------------------------------------------------------------------------------------------
|
||
|
TimelineView.prototype.computeDateScroll = function (duration) {
|
||
|
return this.timeAxis.computeDateScroll(duration);
|
||
|
};
|
||
|
TimelineView.prototype.applyScroll = function (scroll, isResize) {
|
||
|
_super.prototype.applyScroll.call(this, scroll, isResize); // will call applyDateScroll
|
||
|
var calendar = this.context.calendar;
|
||
|
// avoid updating stickyscroll too often
|
||
|
// TODO: repeat code as ResourceTimelineView::updateSize
|
||
|
if (isResize || calendar.isViewUpdated || calendar.isDatesUpdated || calendar.isEventsUpdated) {
|
||
|
this.timeAxis.updateStickyScrollers();
|
||
|
}
|
||
|
};
|
||
|
TimelineView.prototype.applyDateScroll = function (scroll) {
|
||
|
this.timeAxis.applyDateScroll(scroll);
|
||
|
};
|
||
|
TimelineView.prototype.queryScroll = function () {
|
||
|
var enhancedScroll = this.timeAxis.layout.bodyScroller.enhancedScroll;
|
||
|
return {
|
||
|
top: enhancedScroll.getScrollTop(),
|
||
|
left: enhancedScroll.getScrollLeft()
|
||
|
};
|
||
|
};
|
||
|
// Hit System
|
||
|
// ------------------------------------------------------------------------------------------
|
||
|
TimelineView.prototype.buildPositionCaches = function () {
|
||
|
this.timeAxis.slats.updateSize();
|
||
|
};
|
||
|
TimelineView.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) {
|
||
|
var slatHit = this.timeAxis.slats.positionToHit(positionLeft);
|
||
|
if (slatHit) {
|
||
|
return {
|
||
|
component: this,
|
||
|
dateSpan: slatHit.dateSpan,
|
||
|
rect: {
|
||
|
left: slatHit.left,
|
||
|
right: slatHit.right,
|
||
|
top: 0,
|
||
|
bottom: elHeight
|
||
|
},
|
||
|
dayEl: slatHit.dayEl,
|
||
|
layer: 0
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
return TimelineView;
|
||
|
}(core.View));
|
||
|
|
||
|
var main = core.createPlugin({
|
||
|
defaultView: 'timelineDay',
|
||
|
views: {
|
||
|
timeline: {
|
||
|
class: TimelineView,
|
||
|
eventResizableFromStart: true // how is this consumed for TimelineView tho?
|
||
|
},
|
||
|
timelineDay: {
|
||
|
type: 'timeline',
|
||
|
duration: { days: 1 }
|
||
|
},
|
||
|
timelineWeek: {
|
||
|
type: 'timeline',
|
||
|
duration: { weeks: 1 }
|
||
|
},
|
||
|
timelineMonth: {
|
||
|
type: 'timeline',
|
||
|
duration: { months: 1 }
|
||
|
},
|
||
|
timelineYear: {
|
||
|
type: 'timeline',
|
||
|
duration: { years: 1 }
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
exports.HeaderBodyLayout = HeaderBodyLayout;
|
||
|
exports.ScrollJoiner = ScrollJoiner;
|
||
|
exports.StickyScroller = StickyScroller;
|
||
|
exports.TimeAxis = TimeAxis;
|
||
|
exports.TimelineLane = TimelineLane;
|
||
|
exports.TimelineView = TimelineView;
|
||
|
exports.default = main;
|
||
|
|
||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||
|
|
||
|
}));
|