main.js 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401
  1. /*!
  2. FullCalendar Time Grid Plugin v4.4.0
  3. Docs & License: https://fullcalendar.io/
  4. (c) 2019 Adam Shaw
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core'), require('@fullcalendar/daygrid')) :
  8. typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core', '@fullcalendar/daygrid'], factory) :
  9. (global = global || self, factory(global.FullCalendarTimeGrid = {}, global.FullCalendar, global.FullCalendarDayGrid));
  10. }(this, function (exports, core, daygrid) { 'use strict';
  11. /*! *****************************************************************************
  12. Copyright (c) Microsoft Corporation. All rights reserved.
  13. Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  14. this file except in compliance with the License. You may obtain a copy of the
  15. License at http://www.apache.org/licenses/LICENSE-2.0
  16. THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17. KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  18. WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  19. MERCHANTABLITY OR NON-INFRINGEMENT.
  20. See the Apache Version 2.0 License for specific language governing permissions
  21. and limitations under the License.
  22. ***************************************************************************** */
  23. /* global Reflect, Promise */
  24. var extendStatics = function(d, b) {
  25. extendStatics = Object.setPrototypeOf ||
  26. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  27. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  28. return extendStatics(d, b);
  29. };
  30. function __extends(d, b) {
  31. extendStatics(d, b);
  32. function __() { this.constructor = d; }
  33. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  34. }
  35. var __assign = function() {
  36. __assign = Object.assign || function __assign(t) {
  37. for (var s, i = 1, n = arguments.length; i < n; i++) {
  38. s = arguments[i];
  39. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
  40. }
  41. return t;
  42. };
  43. return __assign.apply(this, arguments);
  44. };
  45. /*
  46. Only handles foreground segs.
  47. Does not own rendering. Use for low-level util methods by TimeGrid.
  48. */
  49. var TimeGridEventRenderer = /** @class */ (function (_super) {
  50. __extends(TimeGridEventRenderer, _super);
  51. function TimeGridEventRenderer(timeGrid) {
  52. var _this = _super.call(this) || this;
  53. _this.timeGrid = timeGrid;
  54. return _this;
  55. }
  56. TimeGridEventRenderer.prototype.renderSegs = function (context, segs, mirrorInfo) {
  57. _super.prototype.renderSegs.call(this, context, segs, mirrorInfo);
  58. // TODO: dont do every time. memoize
  59. this.fullTimeFormat = core.createFormatter({
  60. hour: 'numeric',
  61. minute: '2-digit',
  62. separator: this.context.options.defaultRangeSeparator
  63. });
  64. };
  65. // Given an array of foreground segments, render a DOM element for each, computes position,
  66. // and attaches to the column inner-container elements.
  67. TimeGridEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
  68. var segsByCol = this.timeGrid.groupSegsByCol(segs);
  69. // order the segs within each column
  70. // TODO: have groupSegsByCol do this?
  71. for (var col = 0; col < segsByCol.length; col++) {
  72. segsByCol[col] = this.sortEventSegs(segsByCol[col]);
  73. }
  74. this.segsByCol = segsByCol;
  75. this.timeGrid.attachSegsByCol(segsByCol, this.timeGrid.fgContainerEls);
  76. };
  77. TimeGridEventRenderer.prototype.detachSegs = function (segs) {
  78. segs.forEach(function (seg) {
  79. core.removeElement(seg.el);
  80. });
  81. this.segsByCol = null;
  82. };
  83. TimeGridEventRenderer.prototype.computeSegSizes = function (allSegs) {
  84. var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
  85. var colCnt = timeGrid.colCnt;
  86. timeGrid.computeSegVerticals(allSegs); // horizontals relies on this
  87. if (segsByCol) {
  88. for (var col = 0; col < colCnt; col++) {
  89. this.computeSegHorizontals(segsByCol[col]); // compute horizontal coordinates, z-index's, and reorder the array
  90. }
  91. }
  92. };
  93. TimeGridEventRenderer.prototype.assignSegSizes = function (allSegs) {
  94. var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
  95. var colCnt = timeGrid.colCnt;
  96. timeGrid.assignSegVerticals(allSegs); // horizontals relies on this
  97. if (segsByCol) {
  98. for (var col = 0; col < colCnt; col++) {
  99. this.assignSegCss(segsByCol[col]);
  100. }
  101. }
  102. };
  103. // Computes a default event time formatting string if `eventTimeFormat` is not explicitly defined
  104. TimeGridEventRenderer.prototype.computeEventTimeFormat = function () {
  105. return {
  106. hour: 'numeric',
  107. minute: '2-digit',
  108. meridiem: false
  109. };
  110. };
  111. // Computes a default `displayEventEnd` value if one is not expliclty defined
  112. TimeGridEventRenderer.prototype.computeDisplayEventEnd = function () {
  113. return true;
  114. };
  115. // Renders the HTML for a single event segment's default rendering
  116. TimeGridEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
  117. var eventRange = seg.eventRange;
  118. var eventDef = eventRange.def;
  119. var eventUi = eventRange.ui;
  120. var allDay = eventDef.allDay;
  121. var isDraggable = core.computeEventDraggable(this.context, eventDef, eventUi);
  122. var isResizableFromStart = seg.isStart && core.computeEventStartResizable(this.context, eventDef, eventUi);
  123. var isResizableFromEnd = seg.isEnd && core.computeEventEndResizable(this.context, eventDef, eventUi);
  124. var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
  125. var skinCss = core.cssToStr(this.getSkinCss(eventUi));
  126. var timeText;
  127. var fullTimeText; // more verbose time text. for the print stylesheet
  128. var startTimeText; // just the start time text
  129. classes.unshift('fc-time-grid-event');
  130. // if the event appears to span more than one day...
  131. if (core.isMultiDayRange(eventRange.range)) {
  132. // Don't display time text on segments that run entirely through a day.
  133. // That would appear as midnight-midnight and would look dumb.
  134. // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
  135. if (seg.isStart || seg.isEnd) {
  136. var unzonedStart = seg.start;
  137. var unzonedEnd = seg.end;
  138. timeText = this._getTimeText(unzonedStart, unzonedEnd, allDay); // TODO: give the timezones
  139. fullTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, this.fullTimeFormat);
  140. startTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, null, false); // displayEnd=false
  141. }
  142. }
  143. else {
  144. // Display the normal time text for the *event's* times
  145. timeText = this.getTimeText(eventRange);
  146. fullTimeText = this.getTimeText(eventRange, this.fullTimeFormat);
  147. startTimeText = this.getTimeText(eventRange, null, false); // displayEnd=false
  148. }
  149. return '<a class="' + classes.join(' ') + '"' +
  150. (eventDef.url ?
  151. ' href="' + core.htmlEscape(eventDef.url) + '"' :
  152. '') +
  153. (skinCss ?
  154. ' style="' + skinCss + '"' :
  155. '') +
  156. '>' +
  157. '<div class="fc-content">' +
  158. (timeText ?
  159. '<div class="fc-time"' +
  160. ' data-start="' + core.htmlEscape(startTimeText) + '"' +
  161. ' data-full="' + core.htmlEscape(fullTimeText) + '"' +
  162. '>' +
  163. '<span>' + core.htmlEscape(timeText) + '</span>' +
  164. '</div>' :
  165. '') +
  166. (eventDef.title ?
  167. '<div class="fc-title">' +
  168. core.htmlEscape(eventDef.title) +
  169. '</div>' :
  170. '') +
  171. '</div>' +
  172. /* TODO: write CSS for this
  173. (isResizableFromStart ?
  174. '<div class="fc-resizer fc-start-resizer"></div>' :
  175. ''
  176. ) +
  177. */
  178. (isResizableFromEnd ?
  179. '<div class="fc-resizer fc-end-resizer"></div>' :
  180. '') +
  181. '</a>';
  182. };
  183. // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
  184. // Assumed the segs are already ordered.
  185. // NOTE: Also reorders the given array by date!
  186. TimeGridEventRenderer.prototype.computeSegHorizontals = function (segs) {
  187. var levels;
  188. var level0;
  189. var i;
  190. levels = buildSlotSegLevels(segs);
  191. computeForwardSlotSegs(levels);
  192. if ((level0 = levels[0])) {
  193. for (i = 0; i < level0.length; i++) {
  194. computeSlotSegPressures(level0[i]);
  195. }
  196. for (i = 0; i < level0.length; i++) {
  197. this.computeSegForwardBack(level0[i], 0, 0);
  198. }
  199. }
  200. };
  201. // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
  202. // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
  203. // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
  204. //
  205. // The segment might be part of a "series", which means consecutive segments with the same pressure
  206. // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
  207. // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
  208. // coordinate of the first segment in the series.
  209. TimeGridEventRenderer.prototype.computeSegForwardBack = function (seg, seriesBackwardPressure, seriesBackwardCoord) {
  210. var forwardSegs = seg.forwardSegs;
  211. var i;
  212. if (seg.forwardCoord === undefined) { // not already computed
  213. if (!forwardSegs.length) {
  214. // if there are no forward segments, this segment should butt up against the edge
  215. seg.forwardCoord = 1;
  216. }
  217. else {
  218. // sort highest pressure first
  219. this.sortForwardSegs(forwardSegs);
  220. // this segment's forwardCoord will be calculated from the backwardCoord of the
  221. // highest-pressure forward segment.
  222. this.computeSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
  223. seg.forwardCoord = forwardSegs[0].backwardCoord;
  224. }
  225. // calculate the backwardCoord from the forwardCoord. consider the series
  226. seg.backwardCoord = seg.forwardCoord -
  227. (seg.forwardCoord - seriesBackwardCoord) / // available width for series
  228. (seriesBackwardPressure + 1); // # of segments in the series
  229. // use this segment's coordinates to computed the coordinates of the less-pressurized
  230. // forward segments
  231. for (i = 0; i < forwardSegs.length; i++) {
  232. this.computeSegForwardBack(forwardSegs[i], 0, seg.forwardCoord);
  233. }
  234. }
  235. };
  236. TimeGridEventRenderer.prototype.sortForwardSegs = function (forwardSegs) {
  237. var objs = forwardSegs.map(buildTimeGridSegCompareObj);
  238. var specs = [
  239. // put higher-pressure first
  240. { field: 'forwardPressure', order: -1 },
  241. // put segments that are closer to initial edge first (and favor ones with no coords yet)
  242. { field: 'backwardCoord', order: 1 }
  243. ].concat(this.context.eventOrderSpecs);
  244. objs.sort(function (obj0, obj1) {
  245. return core.compareByFieldSpecs(obj0, obj1, specs);
  246. });
  247. return objs.map(function (c) {
  248. return c._seg;
  249. });
  250. };
  251. // Given foreground event segments that have already had their position coordinates computed,
  252. // assigns position-related CSS values to their elements.
  253. TimeGridEventRenderer.prototype.assignSegCss = function (segs) {
  254. for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
  255. var seg = segs_1[_i];
  256. core.applyStyle(seg.el, this.generateSegCss(seg));
  257. if (seg.level > 0) {
  258. seg.el.classList.add('fc-time-grid-event-inset');
  259. }
  260. // if the event is short that the title will be cut off,
  261. // attach a className that condenses the title into the time area.
  262. if (seg.eventRange.def.title && seg.bottom - seg.top < 30) {
  263. seg.el.classList.add('fc-short'); // TODO: "condensed" is a better name
  264. }
  265. }
  266. };
  267. // Generates an object with CSS properties/values that should be applied to an event segment element.
  268. // Contains important positioning-related properties that should be applied to any event element, customized or not.
  269. TimeGridEventRenderer.prototype.generateSegCss = function (seg) {
  270. var shouldOverlap = this.context.options.slotEventOverlap;
  271. var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point
  272. var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point
  273. var props = this.timeGrid.generateSegVerticalCss(seg); // get top/bottom first
  274. var isRtl = this.context.isRtl;
  275. var left; // amount of space from left edge, a fraction of the total width
  276. var right; // amount of space from right edge, a fraction of the total width
  277. if (shouldOverlap) {
  278. // double the width, but don't go beyond the maximum forward coordinate (1.0)
  279. forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2);
  280. }
  281. if (isRtl) {
  282. left = 1 - forwardCoord;
  283. right = backwardCoord;
  284. }
  285. else {
  286. left = backwardCoord;
  287. right = 1 - forwardCoord;
  288. }
  289. props.zIndex = seg.level + 1; // convert from 0-base to 1-based
  290. props.left = left * 100 + '%';
  291. props.right = right * 100 + '%';
  292. if (shouldOverlap && seg.forwardPressure) {
  293. // add padding to the edge so that forward stacked events don't cover the resizer's icon
  294. props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
  295. }
  296. return props;
  297. };
  298. return TimeGridEventRenderer;
  299. }(core.FgEventRenderer));
  300. // Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is
  301. // left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date.
  302. function buildSlotSegLevels(segs) {
  303. var levels = [];
  304. var i;
  305. var seg;
  306. var j;
  307. for (i = 0; i < segs.length; i++) {
  308. seg = segs[i];
  309. // go through all the levels and stop on the first level where there are no collisions
  310. for (j = 0; j < levels.length; j++) {
  311. if (!computeSlotSegCollisions(seg, levels[j]).length) {
  312. break;
  313. }
  314. }
  315. seg.level = j;
  316. (levels[j] || (levels[j] = [])).push(seg);
  317. }
  318. return levels;
  319. }
  320. // For every segment, figure out the other segments that are in subsequent
  321. // levels that also occupy the same vertical space. Accumulate in seg.forwardSegs
  322. function computeForwardSlotSegs(levels) {
  323. var i;
  324. var level;
  325. var j;
  326. var seg;
  327. var k;
  328. for (i = 0; i < levels.length; i++) {
  329. level = levels[i];
  330. for (j = 0; j < level.length; j++) {
  331. seg = level[j];
  332. seg.forwardSegs = [];
  333. for (k = i + 1; k < levels.length; k++) {
  334. computeSlotSegCollisions(seg, levels[k], seg.forwardSegs);
  335. }
  336. }
  337. }
  338. }
  339. // Figure out which path forward (via seg.forwardSegs) results in the longest path until
  340. // the furthest edge is reached. The number of segments in this path will be seg.forwardPressure
  341. function computeSlotSegPressures(seg) {
  342. var forwardSegs = seg.forwardSegs;
  343. var forwardPressure = 0;
  344. var i;
  345. var forwardSeg;
  346. if (seg.forwardPressure === undefined) { // not already computed
  347. for (i = 0; i < forwardSegs.length; i++) {
  348. forwardSeg = forwardSegs[i];
  349. // figure out the child's maximum forward path
  350. computeSlotSegPressures(forwardSeg);
  351. // either use the existing maximum, or use the child's forward pressure
  352. // plus one (for the forwardSeg itself)
  353. forwardPressure = Math.max(forwardPressure, 1 + forwardSeg.forwardPressure);
  354. }
  355. seg.forwardPressure = forwardPressure;
  356. }
  357. }
  358. // Find all the segments in `otherSegs` that vertically collide with `seg`.
  359. // Append into an optionally-supplied `results` array and return.
  360. function computeSlotSegCollisions(seg, otherSegs, results) {
  361. if (results === void 0) { results = []; }
  362. for (var i = 0; i < otherSegs.length; i++) {
  363. if (isSlotSegCollision(seg, otherSegs[i])) {
  364. results.push(otherSegs[i]);
  365. }
  366. }
  367. return results;
  368. }
  369. // Do these segments occupy the same vertical space?
  370. function isSlotSegCollision(seg1, seg2) {
  371. return seg1.bottom > seg2.top && seg1.top < seg2.bottom;
  372. }
  373. function buildTimeGridSegCompareObj(seg) {
  374. var obj = core.buildSegCompareObj(seg);
  375. obj.forwardPressure = seg.forwardPressure;
  376. obj.backwardCoord = seg.backwardCoord;
  377. return obj;
  378. }
  379. var TimeGridMirrorRenderer = /** @class */ (function (_super) {
  380. __extends(TimeGridMirrorRenderer, _super);
  381. function TimeGridMirrorRenderer() {
  382. return _super !== null && _super.apply(this, arguments) || this;
  383. }
  384. TimeGridMirrorRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
  385. this.segsByCol = this.timeGrid.groupSegsByCol(segs);
  386. this.timeGrid.attachSegsByCol(this.segsByCol, this.timeGrid.mirrorContainerEls);
  387. this.sourceSeg = mirrorInfo.sourceSeg;
  388. };
  389. TimeGridMirrorRenderer.prototype.generateSegCss = function (seg) {
  390. var props = _super.prototype.generateSegCss.call(this, seg);
  391. var sourceSeg = this.sourceSeg;
  392. if (sourceSeg && sourceSeg.col === seg.col) {
  393. var sourceSegProps = _super.prototype.generateSegCss.call(this, sourceSeg);
  394. props.left = sourceSegProps.left;
  395. props.right = sourceSegProps.right;
  396. props.marginLeft = sourceSegProps.marginLeft;
  397. props.marginRight = sourceSegProps.marginRight;
  398. }
  399. return props;
  400. };
  401. return TimeGridMirrorRenderer;
  402. }(TimeGridEventRenderer));
  403. var TimeGridFillRenderer = /** @class */ (function (_super) {
  404. __extends(TimeGridFillRenderer, _super);
  405. function TimeGridFillRenderer(timeGrid) {
  406. var _this = _super.call(this) || this;
  407. _this.timeGrid = timeGrid;
  408. return _this;
  409. }
  410. TimeGridFillRenderer.prototype.attachSegs = function (type, segs) {
  411. var timeGrid = this.timeGrid;
  412. var containerEls;
  413. // TODO: more efficient lookup
  414. if (type === 'bgEvent') {
  415. containerEls = timeGrid.bgContainerEls;
  416. }
  417. else if (type === 'businessHours') {
  418. containerEls = timeGrid.businessContainerEls;
  419. }
  420. else if (type === 'highlight') {
  421. containerEls = timeGrid.highlightContainerEls;
  422. }
  423. timeGrid.attachSegsByCol(timeGrid.groupSegsByCol(segs), containerEls);
  424. return segs.map(function (seg) {
  425. return seg.el;
  426. });
  427. };
  428. TimeGridFillRenderer.prototype.computeSegSizes = function (segs) {
  429. this.timeGrid.computeSegVerticals(segs);
  430. };
  431. TimeGridFillRenderer.prototype.assignSegSizes = function (segs) {
  432. this.timeGrid.assignSegVerticals(segs);
  433. };
  434. return TimeGridFillRenderer;
  435. }(core.FillRenderer));
  436. /* A component that renders one or more columns of vertical time slots
  437. ----------------------------------------------------------------------------------------------------------------------*/
  438. // potential nice values for the slot-duration and interval-duration
  439. // from largest to smallest
  440. var AGENDA_STOCK_SUB_DURATIONS = [
  441. { hours: 1 },
  442. { minutes: 30 },
  443. { minutes: 15 },
  444. { seconds: 30 },
  445. { seconds: 15 }
  446. ];
  447. var TimeGrid = /** @class */ (function (_super) {
  448. __extends(TimeGrid, _super);
  449. function TimeGrid(el, renderProps) {
  450. var _this = _super.call(this, el) || this;
  451. _this.isSlatSizesDirty = false;
  452. _this.isColSizesDirty = false;
  453. _this.processOptions = core.memoize(_this._processOptions);
  454. _this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton);
  455. _this.renderSlats = core.memoizeRendering(_this._renderSlats, null, [_this.renderSkeleton]);
  456. _this.renderColumns = core.memoizeRendering(_this._renderColumns, _this._unrenderColumns, [_this.renderSkeleton]);
  457. _this.renderProps = renderProps;
  458. var renderColumns = _this.renderColumns;
  459. var eventRenderer = _this.eventRenderer = new TimeGridEventRenderer(_this);
  460. var fillRenderer = _this.fillRenderer = new TimeGridFillRenderer(_this);
  461. _this.mirrorRenderer = new TimeGridMirrorRenderer(_this);
  462. _this.renderBusinessHours = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [renderColumns]);
  463. _this.renderDateSelection = core.memoizeRendering(_this._renderDateSelection, _this._unrenderDateSelection, [renderColumns]);
  464. _this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderColumns]);
  465. _this.renderBgEvents = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [renderColumns]);
  466. _this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
  467. _this.renderEventDrag = core.memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag, [renderColumns]);
  468. _this.renderEventResize = core.memoizeRendering(_this._renderEventResize, _this._unrenderEventResize, [renderColumns]);
  469. return _this;
  470. }
  471. /* Options
  472. ------------------------------------------------------------------------------------------------------------------*/
  473. // Parses various options into properties of this object
  474. // MUST have context already set
  475. TimeGrid.prototype._processOptions = function (options) {
  476. var slotDuration = options.slotDuration, snapDuration = options.snapDuration;
  477. var snapsPerSlot;
  478. var input;
  479. slotDuration = core.createDuration(slotDuration);
  480. snapDuration = snapDuration ? core.createDuration(snapDuration) : slotDuration;
  481. snapsPerSlot = core.wholeDivideDurations(slotDuration, snapDuration);
  482. if (snapsPerSlot === null) {
  483. snapDuration = slotDuration;
  484. snapsPerSlot = 1;
  485. // TODO: say warning?
  486. }
  487. this.slotDuration = slotDuration;
  488. this.snapDuration = snapDuration;
  489. this.snapsPerSlot = snapsPerSlot;
  490. // might be an array value (for TimelineView).
  491. // if so, getting the most granular entry (the last one probably).
  492. input = options.slotLabelFormat;
  493. if (Array.isArray(input)) {
  494. input = input[input.length - 1];
  495. }
  496. this.labelFormat = core.createFormatter(input || {
  497. hour: 'numeric',
  498. minute: '2-digit',
  499. omitZeroMinute: true,
  500. meridiem: 'short'
  501. });
  502. input = options.slotLabelInterval;
  503. this.labelInterval = input ?
  504. core.createDuration(input) :
  505. this.computeLabelInterval(slotDuration);
  506. };
  507. // Computes an automatic value for slotLabelInterval
  508. TimeGrid.prototype.computeLabelInterval = function (slotDuration) {
  509. var i;
  510. var labelInterval;
  511. var slotsPerLabel;
  512. // find the smallest stock label interval that results in more than one slots-per-label
  513. for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) {
  514. labelInterval = core.createDuration(AGENDA_STOCK_SUB_DURATIONS[i]);
  515. slotsPerLabel = core.wholeDivideDurations(labelInterval, slotDuration);
  516. if (slotsPerLabel !== null && slotsPerLabel > 1) {
  517. return labelInterval;
  518. }
  519. }
  520. return slotDuration; // fall back
  521. };
  522. /* Rendering
  523. ------------------------------------------------------------------------------------------------------------------*/
  524. TimeGrid.prototype.render = function (props, context) {
  525. this.processOptions(context.options);
  526. var cells = props.cells;
  527. this.colCnt = cells.length;
  528. this.renderSkeleton(context.theme);
  529. this.renderSlats(props.dateProfile);
  530. this.renderColumns(props.cells, props.dateProfile);
  531. this.renderBusinessHours(context, props.businessHourSegs);
  532. this.renderDateSelection(props.dateSelectionSegs);
  533. this.renderFgEvents(context, props.fgEventSegs);
  534. this.renderBgEvents(context, props.bgEventSegs);
  535. this.renderEventSelection(props.eventSelection);
  536. this.renderEventDrag(props.eventDrag);
  537. this.renderEventResize(props.eventResize);
  538. };
  539. TimeGrid.prototype.destroy = function () {
  540. _super.prototype.destroy.call(this);
  541. // should unrender everything else too
  542. this.renderSlats.unrender();
  543. this.renderColumns.unrender();
  544. this.renderSkeleton.unrender();
  545. };
  546. TimeGrid.prototype.updateSize = function (isResize) {
  547. var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
  548. if (isResize || this.isSlatSizesDirty) {
  549. this.buildSlatPositions();
  550. this.isSlatSizesDirty = false;
  551. }
  552. if (isResize || this.isColSizesDirty) {
  553. this.buildColPositions();
  554. this.isColSizesDirty = false;
  555. }
  556. fillRenderer.computeSizes(isResize);
  557. eventRenderer.computeSizes(isResize);
  558. mirrorRenderer.computeSizes(isResize);
  559. fillRenderer.assignSizes(isResize);
  560. eventRenderer.assignSizes(isResize);
  561. mirrorRenderer.assignSizes(isResize);
  562. };
  563. TimeGrid.prototype._renderSkeleton = function (theme) {
  564. var el = this.el;
  565. el.innerHTML =
  566. '<div class="fc-bg"></div>' +
  567. '<div class="fc-slats"></div>' +
  568. '<hr class="fc-divider ' + theme.getClass('widgetHeader') + '" style="display:none" />';
  569. this.rootBgContainerEl = el.querySelector('.fc-bg');
  570. this.slatContainerEl = el.querySelector('.fc-slats');
  571. this.bottomRuleEl = el.querySelector('.fc-divider');
  572. };
  573. TimeGrid.prototype._renderSlats = function (dateProfile) {
  574. var theme = this.context.theme;
  575. this.slatContainerEl.innerHTML =
  576. '<table class="' + theme.getClass('tableGrid') + '">' +
  577. this.renderSlatRowHtml(dateProfile) +
  578. '</table>';
  579. this.slatEls = core.findElements(this.slatContainerEl, 'tr');
  580. this.slatPositions = new core.PositionCache(this.el, this.slatEls, false, true // vertical
  581. );
  582. this.isSlatSizesDirty = true;
  583. };
  584. // Generates the HTML for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL.
  585. TimeGrid.prototype.renderSlatRowHtml = function (dateProfile) {
  586. var _a = this.context, dateEnv = _a.dateEnv, theme = _a.theme, isRtl = _a.isRtl;
  587. var html = '';
  588. var dayStart = core.startOfDay(dateProfile.renderRange.start);
  589. var slotTime = dateProfile.minTime;
  590. var slotIterator = core.createDuration(0);
  591. var slotDate; // will be on the view's first day, but we only care about its time
  592. var isLabeled;
  593. var axisHtml;
  594. // Calculate the time for each slot
  595. while (core.asRoughMs(slotTime) < core.asRoughMs(dateProfile.maxTime)) {
  596. slotDate = dateEnv.add(dayStart, slotTime);
  597. isLabeled = core.wholeDivideDurations(slotIterator, this.labelInterval) !== null;
  598. axisHtml =
  599. '<td class="fc-axis fc-time ' + theme.getClass('widgetContent') + '">' +
  600. (isLabeled ?
  601. '<span>' + // for matchCellWidths
  602. core.htmlEscape(dateEnv.format(slotDate, this.labelFormat)) +
  603. '</span>' :
  604. '') +
  605. '</td>';
  606. html +=
  607. '<tr data-time="' + core.formatIsoTimeString(slotDate) + '"' +
  608. (isLabeled ? '' : ' class="fc-minor"') +
  609. '>' +
  610. (!isRtl ? axisHtml : '') +
  611. '<td class="' + theme.getClass('widgetContent') + '"></td>' +
  612. (isRtl ? axisHtml : '') +
  613. '</tr>';
  614. slotTime = core.addDurations(slotTime, this.slotDuration);
  615. slotIterator = core.addDurations(slotIterator, this.slotDuration);
  616. }
  617. return html;
  618. };
  619. TimeGrid.prototype._renderColumns = function (cells, dateProfile) {
  620. var _a = this.context, calendar = _a.calendar, view = _a.view, isRtl = _a.isRtl, theme = _a.theme, dateEnv = _a.dateEnv;
  621. var bgRow = new daygrid.DayBgRow(this.context);
  622. this.rootBgContainerEl.innerHTML =
  623. '<table class="' + theme.getClass('tableGrid') + '">' +
  624. bgRow.renderHtml({
  625. cells: cells,
  626. dateProfile: dateProfile,
  627. renderIntroHtml: this.renderProps.renderBgIntroHtml
  628. }) +
  629. '</table>';
  630. this.colEls = core.findElements(this.el, '.fc-day, .fc-disabled-day');
  631. for (var col = 0; col < this.colCnt; col++) {
  632. calendar.publiclyTrigger('dayRender', [
  633. {
  634. date: dateEnv.toDate(cells[col].date),
  635. el: this.colEls[col],
  636. view: view
  637. }
  638. ]);
  639. }
  640. if (isRtl) {
  641. this.colEls.reverse();
  642. }
  643. this.colPositions = new core.PositionCache(this.el, this.colEls, true, // horizontal
  644. false);
  645. this.renderContentSkeleton();
  646. this.isColSizesDirty = true;
  647. };
  648. TimeGrid.prototype._unrenderColumns = function () {
  649. this.unrenderContentSkeleton();
  650. };
  651. /* Content Skeleton
  652. ------------------------------------------------------------------------------------------------------------------*/
  653. // Renders the DOM that the view's content will live in
  654. TimeGrid.prototype.renderContentSkeleton = function () {
  655. var isRtl = this.context.isRtl;
  656. var parts = [];
  657. var skeletonEl;
  658. parts.push(this.renderProps.renderIntroHtml());
  659. for (var i = 0; i < this.colCnt; i++) {
  660. parts.push('<td>' +
  661. '<div class="fc-content-col">' +
  662. '<div class="fc-event-container fc-mirror-container"></div>' +
  663. '<div class="fc-event-container"></div>' +
  664. '<div class="fc-highlight-container"></div>' +
  665. '<div class="fc-bgevent-container"></div>' +
  666. '<div class="fc-business-container"></div>' +
  667. '</div>' +
  668. '</td>');
  669. }
  670. if (isRtl) {
  671. parts.reverse();
  672. }
  673. skeletonEl = this.contentSkeletonEl = core.htmlToElement('<div class="fc-content-skeleton">' +
  674. '<table>' +
  675. '<tr>' + parts.join('') + '</tr>' +
  676. '</table>' +
  677. '</div>');
  678. this.colContainerEls = core.findElements(skeletonEl, '.fc-content-col');
  679. this.mirrorContainerEls = core.findElements(skeletonEl, '.fc-mirror-container');
  680. this.fgContainerEls = core.findElements(skeletonEl, '.fc-event-container:not(.fc-mirror-container)');
  681. this.bgContainerEls = core.findElements(skeletonEl, '.fc-bgevent-container');
  682. this.highlightContainerEls = core.findElements(skeletonEl, '.fc-highlight-container');
  683. this.businessContainerEls = core.findElements(skeletonEl, '.fc-business-container');
  684. if (isRtl) {
  685. this.colContainerEls.reverse();
  686. this.mirrorContainerEls.reverse();
  687. this.fgContainerEls.reverse();
  688. this.bgContainerEls.reverse();
  689. this.highlightContainerEls.reverse();
  690. this.businessContainerEls.reverse();
  691. }
  692. this.el.appendChild(skeletonEl);
  693. };
  694. TimeGrid.prototype.unrenderContentSkeleton = function () {
  695. core.removeElement(this.contentSkeletonEl);
  696. };
  697. // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col
  698. TimeGrid.prototype.groupSegsByCol = function (segs) {
  699. var segsByCol = [];
  700. var i;
  701. for (i = 0; i < this.colCnt; i++) {
  702. segsByCol.push([]);
  703. }
  704. for (i = 0; i < segs.length; i++) {
  705. segsByCol[segs[i].col].push(segs[i]);
  706. }
  707. return segsByCol;
  708. };
  709. // Given segments grouped by column, insert the segments' elements into a parallel array of container
  710. // elements, each living within a column.
  711. TimeGrid.prototype.attachSegsByCol = function (segsByCol, containerEls) {
  712. var col;
  713. var segs;
  714. var i;
  715. for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
  716. segs = segsByCol[col];
  717. for (i = 0; i < segs.length; i++) {
  718. containerEls[col].appendChild(segs[i].el);
  719. }
  720. }
  721. };
  722. /* Now Indicator
  723. ------------------------------------------------------------------------------------------------------------------*/
  724. TimeGrid.prototype.getNowIndicatorUnit = function () {
  725. return 'minute'; // will refresh on the minute
  726. };
  727. TimeGrid.prototype.renderNowIndicator = function (segs, date) {
  728. // HACK: if date columns not ready for some reason (scheduler)
  729. if (!this.colContainerEls) {
  730. return;
  731. }
  732. var top = this.computeDateTop(date);
  733. var nodes = [];
  734. var i;
  735. // render lines within the columns
  736. for (i = 0; i < segs.length; i++) {
  737. var lineEl = core.createElement('div', { className: 'fc-now-indicator fc-now-indicator-line' });
  738. lineEl.style.top = top + 'px';
  739. this.colContainerEls[segs[i].col].appendChild(lineEl);
  740. nodes.push(lineEl);
  741. }
  742. // render an arrow over the axis
  743. if (segs.length > 0) { // is the current time in view?
  744. var arrowEl = core.createElement('div', { className: 'fc-now-indicator fc-now-indicator-arrow' });
  745. arrowEl.style.top = top + 'px';
  746. this.contentSkeletonEl.appendChild(arrowEl);
  747. nodes.push(arrowEl);
  748. }
  749. this.nowIndicatorEls = nodes;
  750. };
  751. TimeGrid.prototype.unrenderNowIndicator = function () {
  752. if (this.nowIndicatorEls) {
  753. this.nowIndicatorEls.forEach(core.removeElement);
  754. this.nowIndicatorEls = null;
  755. }
  756. };
  757. /* Coordinates
  758. ------------------------------------------------------------------------------------------------------------------*/
  759. TimeGrid.prototype.getTotalSlatHeight = function () {
  760. return this.slatContainerEl.getBoundingClientRect().height;
  761. };
  762. // Computes the top coordinate, relative to the bounds of the grid, of the given date.
  763. // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
  764. TimeGrid.prototype.computeDateTop = function (when, startOfDayDate) {
  765. if (!startOfDayDate) {
  766. startOfDayDate = core.startOfDay(when);
  767. }
  768. return this.computeTimeTop(core.createDuration(when.valueOf() - startOfDayDate.valueOf()));
  769. };
  770. // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration).
  771. TimeGrid.prototype.computeTimeTop = function (duration) {
  772. var len = this.slatEls.length;
  773. var dateProfile = this.props.dateProfile;
  774. var slatCoverage = (duration.milliseconds - core.asRoughMs(dateProfile.minTime)) / core.asRoughMs(this.slotDuration); // floating-point value of # of slots covered
  775. var slatIndex;
  776. var slatRemainder;
  777. // compute a floating-point number for how many slats should be progressed through.
  778. // from 0 to number of slats (inclusive)
  779. // constrained because minTime/maxTime might be customized.
  780. slatCoverage = Math.max(0, slatCoverage);
  781. slatCoverage = Math.min(len, slatCoverage);
  782. // an integer index of the furthest whole slat
  783. // from 0 to number slats (*exclusive*, so len-1)
  784. slatIndex = Math.floor(slatCoverage);
  785. slatIndex = Math.min(slatIndex, len - 1);
  786. // how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition.
  787. // could be 1.0 if slatCoverage is covering *all* the slots
  788. slatRemainder = slatCoverage - slatIndex;
  789. return this.slatPositions.tops[slatIndex] +
  790. this.slatPositions.getHeight(slatIndex) * slatRemainder;
  791. };
  792. // For each segment in an array, computes and assigns its top and bottom properties
  793. TimeGrid.prototype.computeSegVerticals = function (segs) {
  794. var options = this.context.options;
  795. var eventMinHeight = options.timeGridEventMinHeight;
  796. var i;
  797. var seg;
  798. var dayDate;
  799. for (i = 0; i < segs.length; i++) {
  800. seg = segs[i];
  801. dayDate = this.props.cells[seg.col].date;
  802. seg.top = this.computeDateTop(seg.start, dayDate);
  803. seg.bottom = Math.max(seg.top + eventMinHeight, this.computeDateTop(seg.end, dayDate));
  804. }
  805. };
  806. // Given segments that already have their top/bottom properties computed, applies those values to
  807. // the segments' elements.
  808. TimeGrid.prototype.assignSegVerticals = function (segs) {
  809. var i;
  810. var seg;
  811. for (i = 0; i < segs.length; i++) {
  812. seg = segs[i];
  813. core.applyStyle(seg.el, this.generateSegVerticalCss(seg));
  814. }
  815. };
  816. // Generates an object with CSS properties for the top/bottom coordinates of a segment element
  817. TimeGrid.prototype.generateSegVerticalCss = function (seg) {
  818. return {
  819. top: seg.top,
  820. bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container
  821. };
  822. };
  823. /* Sizing
  824. ------------------------------------------------------------------------------------------------------------------*/
  825. TimeGrid.prototype.buildPositionCaches = function () {
  826. this.buildColPositions();
  827. this.buildSlatPositions();
  828. };
  829. TimeGrid.prototype.buildColPositions = function () {
  830. this.colPositions.build();
  831. };
  832. TimeGrid.prototype.buildSlatPositions = function () {
  833. this.slatPositions.build();
  834. };
  835. /* Hit System
  836. ------------------------------------------------------------------------------------------------------------------*/
  837. TimeGrid.prototype.positionToHit = function (positionLeft, positionTop) {
  838. var dateEnv = this.context.dateEnv;
  839. var _a = this, snapsPerSlot = _a.snapsPerSlot, slatPositions = _a.slatPositions, colPositions = _a.colPositions;
  840. var colIndex = colPositions.leftToIndex(positionLeft);
  841. var slatIndex = slatPositions.topToIndex(positionTop);
  842. if (colIndex != null && slatIndex != null) {
  843. var slatTop = slatPositions.tops[slatIndex];
  844. var slatHeight = slatPositions.getHeight(slatIndex);
  845. var partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1
  846. var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
  847. var snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
  848. var dayDate = this.props.cells[colIndex].date;
  849. var time = core.addDurations(this.props.dateProfile.minTime, core.multiplyDuration(this.snapDuration, snapIndex));
  850. var start = dateEnv.add(dayDate, time);
  851. var end = dateEnv.add(start, this.snapDuration);
  852. return {
  853. col: colIndex,
  854. dateSpan: {
  855. range: { start: start, end: end },
  856. allDay: false
  857. },
  858. dayEl: this.colEls[colIndex],
  859. relativeRect: {
  860. left: colPositions.lefts[colIndex],
  861. right: colPositions.rights[colIndex],
  862. top: slatTop,
  863. bottom: slatTop + slatHeight
  864. }
  865. };
  866. }
  867. };
  868. /* Event Drag Visualization
  869. ------------------------------------------------------------------------------------------------------------------*/
  870. TimeGrid.prototype._renderEventDrag = function (state) {
  871. if (state) {
  872. this.eventRenderer.hideByHash(state.affectedInstances);
  873. if (state.isEvent) {
  874. this.mirrorRenderer.renderSegs(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
  875. }
  876. else {
  877. this.fillRenderer.renderSegs('highlight', this.context, state.segs);
  878. }
  879. }
  880. };
  881. TimeGrid.prototype._unrenderEventDrag = function (state) {
  882. if (state) {
  883. this.eventRenderer.showByHash(state.affectedInstances);
  884. if (state.isEvent) {
  885. this.mirrorRenderer.unrender(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
  886. }
  887. else {
  888. this.fillRenderer.unrender('highlight', this.context);
  889. }
  890. }
  891. };
  892. /* Event Resize Visualization
  893. ------------------------------------------------------------------------------------------------------------------*/
  894. TimeGrid.prototype._renderEventResize = function (state) {
  895. if (state) {
  896. this.eventRenderer.hideByHash(state.affectedInstances);
  897. this.mirrorRenderer.renderSegs(this.context, state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
  898. }
  899. };
  900. TimeGrid.prototype._unrenderEventResize = function (state) {
  901. if (state) {
  902. this.eventRenderer.showByHash(state.affectedInstances);
  903. this.mirrorRenderer.unrender(this.context, state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
  904. }
  905. };
  906. /* Selection
  907. ------------------------------------------------------------------------------------------------------------------*/
  908. // Renders a visual indication of a selection. Overrides the default, which was to simply render a highlight.
  909. TimeGrid.prototype._renderDateSelection = function (segs) {
  910. if (segs) {
  911. if (this.context.options.selectMirror) {
  912. this.mirrorRenderer.renderSegs(this.context, segs, { isSelecting: true });
  913. }
  914. else {
  915. this.fillRenderer.renderSegs('highlight', this.context, segs);
  916. }
  917. }
  918. };
  919. TimeGrid.prototype._unrenderDateSelection = function (segs) {
  920. if (segs) {
  921. if (this.context.options.selectMirror) {
  922. this.mirrorRenderer.unrender(this.context, segs, { isSelecting: true });
  923. }
  924. else {
  925. this.fillRenderer.unrender('highlight', this.context);
  926. }
  927. }
  928. };
  929. return TimeGrid;
  930. }(core.DateComponent));
  931. var AllDaySplitter = /** @class */ (function (_super) {
  932. __extends(AllDaySplitter, _super);
  933. function AllDaySplitter() {
  934. return _super !== null && _super.apply(this, arguments) || this;
  935. }
  936. AllDaySplitter.prototype.getKeyInfo = function () {
  937. return {
  938. allDay: {},
  939. timed: {}
  940. };
  941. };
  942. AllDaySplitter.prototype.getKeysForDateSpan = function (dateSpan) {
  943. if (dateSpan.allDay) {
  944. return ['allDay'];
  945. }
  946. else {
  947. return ['timed'];
  948. }
  949. };
  950. AllDaySplitter.prototype.getKeysForEventDef = function (eventDef) {
  951. if (!eventDef.allDay) {
  952. return ['timed'];
  953. }
  954. else if (core.hasBgRendering(eventDef)) {
  955. return ['timed', 'allDay'];
  956. }
  957. else {
  958. return ['allDay'];
  959. }
  960. };
  961. return AllDaySplitter;
  962. }(core.Splitter));
  963. var TIMEGRID_ALL_DAY_EVENT_LIMIT = 5;
  964. var WEEK_HEADER_FORMAT = core.createFormatter({ week: 'short' });
  965. /* An abstract class for all timegrid-related views. Displays one more columns with time slots running vertically.
  966. ----------------------------------------------------------------------------------------------------------------------*/
  967. // Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on).
  968. // Responsible for managing width/height.
  969. var AbstractTimeGridView = /** @class */ (function (_super) {
  970. __extends(AbstractTimeGridView, _super);
  971. function AbstractTimeGridView() {
  972. var _this = _super !== null && _super.apply(this, arguments) || this;
  973. _this.splitter = new AllDaySplitter();
  974. _this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
  975. /* Header Render Methods
  976. ------------------------------------------------------------------------------------------------------------------*/
  977. // Generates the HTML that will go before the day-of week header cells
  978. _this.renderHeadIntroHtml = function () {
  979. var _a = _this.context, theme = _a.theme, dateEnv = _a.dateEnv, options = _a.options;
  980. var range = _this.props.dateProfile.renderRange;
  981. var dayCnt = core.diffDays(range.start, range.end);
  982. var weekText;
  983. if (options.weekNumbers) {
  984. weekText = dateEnv.format(range.start, WEEK_HEADER_FORMAT);
  985. return '' +
  986. '<th class="fc-axis fc-week-number ' + theme.getClass('widgetHeader') + '" ' + _this.axisStyleAttr() + '>' +
  987. core.buildGotoAnchorHtml(// aside from link, important for matchCellWidths
  988. options, dateEnv, { date: range.start, type: 'week', forceOff: dayCnt > 1 }, core.htmlEscape(weekText) // inner HTML
  989. ) +
  990. '</th>';
  991. }
  992. else {
  993. return '<th class="fc-axis ' + theme.getClass('widgetHeader') + '" ' + _this.axisStyleAttr() + '></th>';
  994. }
  995. };
  996. /* Time Grid Render Methods
  997. ------------------------------------------------------------------------------------------------------------------*/
  998. // Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column.
  999. _this.renderTimeGridBgIntroHtml = function () {
  1000. var theme = _this.context.theme;
  1001. return '<td class="fc-axis ' + theme.getClass('widgetContent') + '" ' + _this.axisStyleAttr() + '></td>';
  1002. };
  1003. // Generates the HTML that goes before all other types of cells.
  1004. // Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
  1005. _this.renderTimeGridIntroHtml = function () {
  1006. return '<td class="fc-axis" ' + _this.axisStyleAttr() + '></td>';
  1007. };
  1008. /* Day Grid Render Methods
  1009. ------------------------------------------------------------------------------------------------------------------*/
  1010. // Generates the HTML that goes before the all-day cells
  1011. _this.renderDayGridBgIntroHtml = function () {
  1012. var _a = _this.context, theme = _a.theme, options = _a.options;
  1013. return '' +
  1014. '<td class="fc-axis ' + theme.getClass('widgetContent') + '" ' + _this.axisStyleAttr() + '>' +
  1015. '<span>' + // needed for matchCellWidths
  1016. core.getAllDayHtml(options) +
  1017. '</span>' +
  1018. '</td>';
  1019. };
  1020. // Generates the HTML that goes before all other types of cells.
  1021. // Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
  1022. _this.renderDayGridIntroHtml = function () {
  1023. return '<td class="fc-axis" ' + _this.axisStyleAttr() + '></td>';
  1024. };
  1025. return _this;
  1026. }
  1027. AbstractTimeGridView.prototype.render = function (props, context) {
  1028. _super.prototype.render.call(this, props, context);
  1029. this.renderSkeleton(context);
  1030. };
  1031. AbstractTimeGridView.prototype.destroy = function () {
  1032. _super.prototype.destroy.call(this);
  1033. this.renderSkeleton.unrender();
  1034. };
  1035. AbstractTimeGridView.prototype._renderSkeleton = function (context) {
  1036. this.el.classList.add('fc-timeGrid-view');
  1037. this.el.innerHTML = this.renderSkeletonHtml();
  1038. this.scroller = new core.ScrollComponent('hidden', // overflow x
  1039. 'auto' // overflow y
  1040. );
  1041. var timeGridWrapEl = this.scroller.el;
  1042. this.el.querySelector('.fc-body > tr > td').appendChild(timeGridWrapEl);
  1043. timeGridWrapEl.classList.add('fc-time-grid-container');
  1044. var timeGridEl = core.createElement('div', { className: 'fc-time-grid' });
  1045. timeGridWrapEl.appendChild(timeGridEl);
  1046. this.timeGrid = new TimeGrid(timeGridEl, {
  1047. renderBgIntroHtml: this.renderTimeGridBgIntroHtml,
  1048. renderIntroHtml: this.renderTimeGridIntroHtml
  1049. });
  1050. if (context.options.allDaySlot) { // should we display the "all-day" area?
  1051. this.dayGrid = new daygrid.DayGrid(// the all-day subcomponent of this view
  1052. this.el.querySelector('.fc-day-grid'), {
  1053. renderNumberIntroHtml: this.renderDayGridIntroHtml,
  1054. renderBgIntroHtml: this.renderDayGridBgIntroHtml,
  1055. renderIntroHtml: this.renderDayGridIntroHtml,
  1056. colWeekNumbersVisible: false,
  1057. cellWeekNumbersVisible: false
  1058. });
  1059. // have the day-grid extend it's coordinate area over the <hr> dividing the two grids
  1060. var dividerEl = this.el.querySelector('.fc-divider');
  1061. this.dayGrid.bottomCoordPadding = dividerEl.getBoundingClientRect().height;
  1062. }
  1063. };
  1064. AbstractTimeGridView.prototype._unrenderSkeleton = function () {
  1065. this.el.classList.remove('fc-timeGrid-view');
  1066. this.timeGrid.destroy();
  1067. if (this.dayGrid) {
  1068. this.dayGrid.destroy();
  1069. }
  1070. this.scroller.destroy();
  1071. };
  1072. /* Rendering
  1073. ------------------------------------------------------------------------------------------------------------------*/
  1074. // Builds the HTML skeleton for the view.
  1075. // The day-grid and time-grid components will render inside containers defined by this HTML.
  1076. AbstractTimeGridView.prototype.renderSkeletonHtml = function () {
  1077. var _a = this.context, theme = _a.theme, options = _a.options;
  1078. return '' +
  1079. '<table class="' + theme.getClass('tableGrid') + '">' +
  1080. (options.columnHeader ?
  1081. '<thead class="fc-head">' +
  1082. '<tr>' +
  1083. '<td class="fc-head-container ' + theme.getClass('widgetHeader') + '">&nbsp;</td>' +
  1084. '</tr>' +
  1085. '</thead>' :
  1086. '') +
  1087. '<tbody class="fc-body">' +
  1088. '<tr>' +
  1089. '<td class="' + theme.getClass('widgetContent') + '">' +
  1090. (options.allDaySlot ?
  1091. '<div class="fc-day-grid"></div>' +
  1092. '<hr class="fc-divider ' + theme.getClass('widgetHeader') + '" />' :
  1093. '') +
  1094. '</td>' +
  1095. '</tr>' +
  1096. '</tbody>' +
  1097. '</table>';
  1098. };
  1099. /* Now Indicator
  1100. ------------------------------------------------------------------------------------------------------------------*/
  1101. AbstractTimeGridView.prototype.getNowIndicatorUnit = function () {
  1102. return this.timeGrid.getNowIndicatorUnit();
  1103. };
  1104. // subclasses should implement
  1105. // renderNowIndicator(date: DateMarker) {
  1106. // }
  1107. AbstractTimeGridView.prototype.unrenderNowIndicator = function () {
  1108. this.timeGrid.unrenderNowIndicator();
  1109. };
  1110. /* Dimensions
  1111. ------------------------------------------------------------------------------------------------------------------*/
  1112. AbstractTimeGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
  1113. _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first
  1114. this.timeGrid.updateSize(isResize);
  1115. if (this.dayGrid) {
  1116. this.dayGrid.updateSize(isResize);
  1117. }
  1118. };
  1119. // Adjusts the vertical dimensions of the view to the specified values
  1120. AbstractTimeGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
  1121. var _this = this;
  1122. var eventLimit;
  1123. var scrollerHeight;
  1124. var scrollbarWidths;
  1125. // make all axis cells line up
  1126. this.axisWidth = core.matchCellWidths(core.findElements(this.el, '.fc-axis'));
  1127. // hack to give the view some height prior to timeGrid's columns being rendered
  1128. // TODO: separate setting height from scroller VS timeGrid.
  1129. if (!this.timeGrid.colEls) {
  1130. if (!isAuto) {
  1131. scrollerHeight = this.computeScrollerHeight(viewHeight);
  1132. this.scroller.setHeight(scrollerHeight);
  1133. }
  1134. return;
  1135. }
  1136. // set of fake row elements that must compensate when scroller has scrollbars
  1137. var noScrollRowEls = core.findElements(this.el, '.fc-row').filter(function (node) {
  1138. return !_this.scroller.el.contains(node);
  1139. });
  1140. // reset all dimensions back to the original state
  1141. this.timeGrid.bottomRuleEl.style.display = 'none'; // will be shown later if this <hr> is necessary
  1142. this.scroller.clear(); // sets height to 'auto' and clears overflow
  1143. noScrollRowEls.forEach(core.uncompensateScroll);
  1144. // limit number of events in the all-day area
  1145. if (this.dayGrid) {
  1146. this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
  1147. eventLimit = this.context.options.eventLimit;
  1148. if (eventLimit && typeof eventLimit !== 'number') {
  1149. eventLimit = TIMEGRID_ALL_DAY_EVENT_LIMIT; // make sure "auto" goes to a real number
  1150. }
  1151. if (eventLimit) {
  1152. this.dayGrid.limitRows(eventLimit);
  1153. }
  1154. }
  1155. if (!isAuto) { // should we force dimensions of the scroll container?
  1156. scrollerHeight = this.computeScrollerHeight(viewHeight);
  1157. this.scroller.setHeight(scrollerHeight);
  1158. scrollbarWidths = this.scroller.getScrollbarWidths();
  1159. if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
  1160. // make the all-day and header rows lines up
  1161. noScrollRowEls.forEach(function (rowEl) {
  1162. core.compensateScroll(rowEl, scrollbarWidths);
  1163. });
  1164. // the scrollbar compensation might have changed text flow, which might affect height, so recalculate
  1165. // and reapply the desired height to the scroller.
  1166. scrollerHeight = this.computeScrollerHeight(viewHeight);
  1167. this.scroller.setHeight(scrollerHeight);
  1168. }
  1169. // guarantees the same scrollbar widths
  1170. this.scroller.lockOverflow(scrollbarWidths);
  1171. // if there's any space below the slats, show the horizontal rule.
  1172. // this won't cause any new overflow, because lockOverflow already called.
  1173. if (this.timeGrid.getTotalSlatHeight() < scrollerHeight) {
  1174. this.timeGrid.bottomRuleEl.style.display = '';
  1175. }
  1176. }
  1177. };
  1178. // given a desired total height of the view, returns what the height of the scroller should be
  1179. AbstractTimeGridView.prototype.computeScrollerHeight = function (viewHeight) {
  1180. return viewHeight -
  1181. core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
  1182. };
  1183. /* Scroll
  1184. ------------------------------------------------------------------------------------------------------------------*/
  1185. // Computes the initial pre-configured scroll state prior to allowing the user to change it
  1186. AbstractTimeGridView.prototype.computeDateScroll = function (duration) {
  1187. var top = this.timeGrid.computeTimeTop(duration);
  1188. // zoom can give weird floating-point values. rather scroll a little bit further
  1189. top = Math.ceil(top);
  1190. if (top) {
  1191. top++; // to overcome top border that slots beyond the first have. looks better
  1192. }
  1193. return { top: top };
  1194. };
  1195. AbstractTimeGridView.prototype.queryDateScroll = function () {
  1196. return { top: this.scroller.getScrollTop() };
  1197. };
  1198. AbstractTimeGridView.prototype.applyDateScroll = function (scroll) {
  1199. if (scroll.top !== undefined) {
  1200. this.scroller.setScrollTop(scroll.top);
  1201. }
  1202. };
  1203. // Generates an HTML attribute string for setting the width of the axis, if it is known
  1204. AbstractTimeGridView.prototype.axisStyleAttr = function () {
  1205. if (this.axisWidth != null) {
  1206. return 'style="width:' + this.axisWidth + 'px"';
  1207. }
  1208. return '';
  1209. };
  1210. return AbstractTimeGridView;
  1211. }(core.View));
  1212. AbstractTimeGridView.prototype.usesMinMaxTime = true; // indicates that minTime/maxTime affects rendering
  1213. var SimpleTimeGrid = /** @class */ (function (_super) {
  1214. __extends(SimpleTimeGrid, _super);
  1215. function SimpleTimeGrid(timeGrid) {
  1216. var _this = _super.call(this, timeGrid.el) || this;
  1217. _this.buildDayRanges = core.memoize(buildDayRanges);
  1218. _this.slicer = new TimeGridSlicer();
  1219. _this.timeGrid = timeGrid;
  1220. return _this;
  1221. }
  1222. SimpleTimeGrid.prototype.firstContext = function (context) {
  1223. context.calendar.registerInteractiveComponent(this, {
  1224. el: this.timeGrid.el
  1225. });
  1226. };
  1227. SimpleTimeGrid.prototype.destroy = function () {
  1228. _super.prototype.destroy.call(this);
  1229. this.context.calendar.unregisterInteractiveComponent(this);
  1230. };
  1231. SimpleTimeGrid.prototype.render = function (props, context) {
  1232. var dateEnv = this.context.dateEnv;
  1233. var dateProfile = props.dateProfile, dayTable = props.dayTable;
  1234. var dayRanges = this.dayRanges = this.buildDayRanges(dayTable, dateProfile, dateEnv);
  1235. this.timeGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, null, context.calendar, this.timeGrid, dayRanges), { dateProfile: dateProfile, cells: dayTable.cells[0] }), context);
  1236. };
  1237. SimpleTimeGrid.prototype.renderNowIndicator = function (date) {
  1238. this.timeGrid.renderNowIndicator(this.slicer.sliceNowDate(date, this.timeGrid, this.dayRanges), date);
  1239. };
  1240. SimpleTimeGrid.prototype.buildPositionCaches = function () {
  1241. this.timeGrid.buildPositionCaches();
  1242. };
  1243. SimpleTimeGrid.prototype.queryHit = function (positionLeft, positionTop) {
  1244. var rawHit = this.timeGrid.positionToHit(positionLeft, positionTop);
  1245. if (rawHit) {
  1246. return {
  1247. component: this.timeGrid,
  1248. dateSpan: rawHit.dateSpan,
  1249. dayEl: rawHit.dayEl,
  1250. rect: {
  1251. left: rawHit.relativeRect.left,
  1252. right: rawHit.relativeRect.right,
  1253. top: rawHit.relativeRect.top,
  1254. bottom: rawHit.relativeRect.bottom
  1255. },
  1256. layer: 0
  1257. };
  1258. }
  1259. };
  1260. return SimpleTimeGrid;
  1261. }(core.DateComponent));
  1262. function buildDayRanges(dayTable, dateProfile, dateEnv) {
  1263. var ranges = [];
  1264. for (var _i = 0, _a = dayTable.headerDates; _i < _a.length; _i++) {
  1265. var date = _a[_i];
  1266. ranges.push({
  1267. start: dateEnv.add(date, dateProfile.minTime),
  1268. end: dateEnv.add(date, dateProfile.maxTime)
  1269. });
  1270. }
  1271. return ranges;
  1272. }
  1273. var TimeGridSlicer = /** @class */ (function (_super) {
  1274. __extends(TimeGridSlicer, _super);
  1275. function TimeGridSlicer() {
  1276. return _super !== null && _super.apply(this, arguments) || this;
  1277. }
  1278. TimeGridSlicer.prototype.sliceRange = function (range, dayRanges) {
  1279. var segs = [];
  1280. for (var col = 0; col < dayRanges.length; col++) {
  1281. var segRange = core.intersectRanges(range, dayRanges[col]);
  1282. if (segRange) {
  1283. segs.push({
  1284. start: segRange.start,
  1285. end: segRange.end,
  1286. isStart: segRange.start.valueOf() === range.start.valueOf(),
  1287. isEnd: segRange.end.valueOf() === range.end.valueOf(),
  1288. col: col
  1289. });
  1290. }
  1291. }
  1292. return segs;
  1293. };
  1294. return TimeGridSlicer;
  1295. }(core.Slicer));
  1296. var TimeGridView = /** @class */ (function (_super) {
  1297. __extends(TimeGridView, _super);
  1298. function TimeGridView() {
  1299. var _this = _super !== null && _super.apply(this, arguments) || this;
  1300. _this.buildDayTable = core.memoize(buildDayTable);
  1301. return _this;
  1302. }
  1303. TimeGridView.prototype.render = function (props, context) {
  1304. _super.prototype.render.call(this, props, context); // for flags for updateSize. also _renderSkeleton/_unrenderSkeleton
  1305. var _a = this.props, dateProfile = _a.dateProfile, dateProfileGenerator = _a.dateProfileGenerator;
  1306. var nextDayThreshold = context.nextDayThreshold;
  1307. var dayTable = this.buildDayTable(dateProfile, dateProfileGenerator);
  1308. var splitProps = this.splitter.splitProps(props);
  1309. if (this.header) {
  1310. this.header.receiveProps({
  1311. dateProfile: dateProfile,
  1312. dates: dayTable.headerDates,
  1313. datesRepDistinctDays: true,
  1314. renderIntroHtml: this.renderHeadIntroHtml
  1315. }, context);
  1316. }
  1317. this.simpleTimeGrid.receiveProps(__assign({}, splitProps['timed'], { dateProfile: dateProfile,
  1318. dayTable: dayTable }), context);
  1319. if (this.simpleDayGrid) {
  1320. this.simpleDayGrid.receiveProps(__assign({}, splitProps['allDay'], { dateProfile: dateProfile,
  1321. dayTable: dayTable,
  1322. nextDayThreshold: nextDayThreshold, isRigid: false }), context);
  1323. }
  1324. this.startNowIndicator(dateProfile, dateProfileGenerator);
  1325. };
  1326. TimeGridView.prototype._renderSkeleton = function (context) {
  1327. _super.prototype._renderSkeleton.call(this, context);
  1328. if (context.options.columnHeader) {
  1329. this.header = new core.DayHeader(this.el.querySelector('.fc-head-container'));
  1330. }
  1331. this.simpleTimeGrid = new SimpleTimeGrid(this.timeGrid);
  1332. if (this.dayGrid) {
  1333. this.simpleDayGrid = new daygrid.SimpleDayGrid(this.dayGrid);
  1334. }
  1335. };
  1336. TimeGridView.prototype._unrenderSkeleton = function () {
  1337. _super.prototype._unrenderSkeleton.call(this);
  1338. if (this.header) {
  1339. this.header.destroy();
  1340. }
  1341. this.simpleTimeGrid.destroy();
  1342. if (this.simpleDayGrid) {
  1343. this.simpleDayGrid.destroy();
  1344. }
  1345. };
  1346. TimeGridView.prototype.renderNowIndicator = function (date) {
  1347. this.simpleTimeGrid.renderNowIndicator(date);
  1348. };
  1349. return TimeGridView;
  1350. }(AbstractTimeGridView));
  1351. function buildDayTable(dateProfile, dateProfileGenerator) {
  1352. var daySeries = new core.DaySeries(dateProfile.renderRange, dateProfileGenerator);
  1353. return new core.DayTable(daySeries, false);
  1354. }
  1355. var main = core.createPlugin({
  1356. defaultView: 'timeGridWeek',
  1357. views: {
  1358. timeGrid: {
  1359. class: TimeGridView,
  1360. allDaySlot: true,
  1361. slotDuration: '00:30:00',
  1362. slotEventOverlap: true // a bad name. confused with overlap/constraint system
  1363. },
  1364. timeGridDay: {
  1365. type: 'timeGrid',
  1366. duration: { days: 1 }
  1367. },
  1368. timeGridWeek: {
  1369. type: 'timeGrid',
  1370. duration: { weeks: 1 }
  1371. }
  1372. }
  1373. });
  1374. exports.AbstractTimeGridView = AbstractTimeGridView;
  1375. exports.TimeGrid = TimeGrid;
  1376. exports.TimeGridSlicer = TimeGridSlicer;
  1377. exports.TimeGridView = TimeGridView;
  1378. exports.buildDayRanges = buildDayRanges;
  1379. exports.buildDayTable = buildDayTable;
  1380. exports.default = main;
  1381. Object.defineProperty(exports, '__esModule', { value: true });
  1382. }));