UNPKG

unserver-unify

Version:

1,133 lines (1,114 loc) 263 kB
/*! * FullCalendar Scheduler v1.9.1 * Docs & License: https://fullcalendar.io/scheduler/ * (c) 2017 Adam Shaw */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("fullcalendar"), require("jquery"), require("moment")); else if(typeof define === 'function' && define.amd) define(["fullcalendar", "jquery", "moment"], factory); else { var a = typeof exports === 'object' ? factory(require("fullcalendar"), require("jquery"), require("moment")) : factory(root["FullCalendar"], root["jQuery"], root["moment"]); for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; } })(this, function(__WEBPACK_EXTERNAL_MODULE_0__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_15__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 36); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_0__; /***/ }), /* 1 */ /***/ (function(module, exports) { /* derived from: https://github.com/Microsoft/tslib/blob/v1.6.0/tslib.js only include the helpers we need, to keep down filesize */ var 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]; }; exports.__extends = function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; /***/ }), /* 2 */ /***/ (function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_2__; /***/ }), /* 3 */, /* 4 */, /* 5 */, /* 6 */ /***/ (function(module, exports, __webpack_require__) { Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = __webpack_require__(1); var fullcalendar_1 = __webpack_require__(0); var ResourceComponentFootprint = /** @class */ (function (_super) { tslib_1.__extends(ResourceComponentFootprint, _super); function ResourceComponentFootprint(unzonedRange, isAllDay, resourceId) { var _this = _super.call(this, unzonedRange, isAllDay) || this; _this.resourceId = resourceId; return _this; } ResourceComponentFootprint.prototype.toLegacy = function (calendar) { var obj = _super.prototype.toLegacy.call(this, calendar); obj.resourceId = this.resourceId; return obj; }; return ResourceComponentFootprint; }(fullcalendar_1.ComponentFootprint)); exports.default = ResourceComponentFootprint; /***/ }), /* 7 */, /* 8 */, /* 9 */ /***/ (function(module, exports, __webpack_require__) { Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = __webpack_require__(1); var $ = __webpack_require__(2); var fullcalendar_1 = __webpack_require__(0); var ResourceViewMixin = /** @class */ (function (_super) { tslib_1.__extends(ResourceViewMixin, _super); function ResourceViewMixin() { return _super !== null && _super.apply(this, arguments) || this; } ResourceViewMixin.mixInto = function (destClass) { var _this = this; fullcalendar_1.Mixin.mixInto.call(this, destClass); [ 'bindBaseRenderHandlers', 'queryScroll', 'applyScroll', 'triggerDayClick', 'triggerSelect', 'triggerExternalDrop', 'handleResourceAdd', 'handleResourceRemove' ].forEach(function (methodName) { destClass.prototype[methodName] = _this.prototype[methodName]; }); }; ResourceViewMixin.prototype.initResourceView = function () { var _this = this; // new task var resourceDeps = ['hasResources']; if (!this.canHandleSpecificResources) { resourceDeps.push('displayingDates'); } this.watch('displayingResources', resourceDeps, function () { _this.requestResourcesRender(_this.get('currentResources')); }, function () { _this.requestResourcesUnrender(); }); // start relying on displayingResources this.watch('displayingBusinessHours', [ 'businessHourGenerator', 'displayingResources', 'displayingDates' ], function (deps) { _this.requestBusinessHoursRender(deps.businessHourGenerator); }, function () { _this.requestBusinessHoursUnrender(); }); // start relying on resource displaying rather than just current resources this.watch('displayingEvents', ['displayingResources', 'hasEvents'], function () { _this.requestEventsRender(_this.get('currentEvents')); }, function () { _this.requestEventsUnrender(); }); }; // Logic: base render trigger should fire when BOTH the resources and dates have rendered, // but the unrender trigger should fire after ONLY the dates are about to be unrendered. ResourceViewMixin.prototype.bindBaseRenderHandlers = function () { var isResourcesRendered = false; var isDatesRendered = false; this.on('resourcesRendered', function () { if (!isResourcesRendered) { isResourcesRendered = true; if (isDatesRendered) { this.whenSizeUpdated(this.triggerViewRender); } } }); this.on('datesRendered', function () { if (!isDatesRendered) { isDatesRendered = true; if (isResourcesRendered) { this.whenSizeUpdated(this.triggerViewRender); } } }); this.on('before:resourcesUnrendered', function () { if (isResourcesRendered) { isResourcesRendered = false; } }); this.on('before:datesUnrendered', function () { if (isDatesRendered) { isDatesRendered = false; this.triggerViewDestroy(); } }); }; // Scroll // ---------------------------------------------------------------------------------------------- ResourceViewMixin.prototype.queryScroll = function () { var scroll = fullcalendar_1.View.prototype.queryScroll.apply(this, arguments); if (this.isResourcesRendered) { $.extend(scroll, this.queryResourceScroll()); } return scroll; }; ResourceViewMixin.prototype.applyScroll = function (scroll) { fullcalendar_1.View.prototype.applyScroll.apply(this, arguments); if (this.isResourcesRendered) { this.applyResourceScroll(scroll); } }; ResourceViewMixin.prototype.queryResourceScroll = function () { return {}; // subclasses must implement }; ResourceViewMixin.prototype.applyResourceScroll = function () { // subclasses must implement }; // Rendering Utils // ---------------------------------------------------------------------------------------------- ResourceViewMixin.prototype.getResourceText = function (resource) { return this.getResourceTextFunc()(resource); }; ResourceViewMixin.prototype.getResourceTextFunc = function () { if (this.resourceTextFunc) { return this.resourceTextFunc; } else { var func = this.opt('resourceText'); if (typeof func !== 'function') { func = function (resource) { return resource.title || resource.id; }; } this.resourceTextFunc = func; return func; } }; // Resource Change Handling // ---------------------------------------------------------------------------------------------- ResourceViewMixin.prototype.handleResourceAdd = function (resource) { this.requestResourceRender(resource); }; ResourceViewMixin.prototype.handleResourceRemove = function (resource) { this.requestResourceUnrender(resource); }; // Resource Rendering // ---------------------------------------------------------------------------------------------- ResourceViewMixin.prototype.requestResourcesRender = function (resources) { var _this = this; this.requestRender(function () { _this.executeResourcesRender(resources); }, 'resource', 'init'); }; ResourceViewMixin.prototype.requestResourcesUnrender = function () { var _this = this; this.requestRender(function () { _this.executeResourcesUnrender(); }, 'resource', 'destroy'); }; ResourceViewMixin.prototype.requestResourceRender = function (resource) { var _this = this; this.requestRender(function () { _this.executeResourceRender(resource); }, 'resource', 'add'); }; ResourceViewMixin.prototype.requestResourceUnrender = function (resource) { var _this = this; this.requestRender(function () { _this.executeResourceUnrender(resource); }, 'resource', 'remove'); }; // Resource High-level Rendering/Unrendering // ---------------------------------------------------------------------------------------------- ResourceViewMixin.prototype.executeResourcesRender = function (resources) { this.renderResources(resources); this.isResourcesRendered = true; this.trigger('resourcesRendered'); }; ResourceViewMixin.prototype.executeResourcesUnrender = function () { this.trigger('before:resourcesUnrendered'); this.unrenderResources(); this.isResourcesRendered = false; }; ResourceViewMixin.prototype.executeResourceRender = function (resource) { this.renderResource(resource); }; ResourceViewMixin.prototype.executeResourceUnrender = function (resource) { this.unrenderResource(resource); }; // Triggering // ---------------------------------------------------------------------------------------------- /* footprint is a ResourceComponentFootprint */ ResourceViewMixin.prototype.triggerDayClick = function (footprint, dayEl, ev) { var dateProfile = this.calendar.footprintToDateProfile(footprint); this.publiclyTrigger('dayClick', { context: dayEl, args: [ dateProfile.start, ev, this, footprint.resourceId ? this.calendar.resourceManager.getResourceById(footprint.resourceId) : null ] }); }; /* footprint is a ResourceComponentFootprint */ ResourceViewMixin.prototype.triggerSelect = function (footprint, ev) { var dateProfile = this.calendar.footprintToDateProfile(footprint); this.publiclyTrigger('select', { context: this, args: [ dateProfile.start, dateProfile.end, ev, this, footprint.resourceId ? this.calendar.resourceManager.getResourceById(footprint.resourceId) : null ] }); }; // override the view's default trigger in order to provide a resourceId to the `drop` event // TODO: make more DRY with core ResourceViewMixin.prototype.triggerExternalDrop = function (singleEventDef, isEvent, el, ev, ui) { // trigger 'drop' regardless of whether element represents an event this.publiclyTrigger('drop', { context: el[0], args: [ singleEventDef.dateProfile.start.clone(), ev, ui, singleEventDef.getResourceIds()[0], this ] }); if (isEvent) { // signal an external event landed this.publiclyTrigger('eventReceive', { context: this, args: [ singleEventDef.buildInstance().toLegacy(), this ] }); } }; return ResourceViewMixin; }(fullcalendar_1.Mixin)); exports.default = ResourceViewMixin; ResourceViewMixin.prototype.isResourcesRendered = false; /***/ }), /* 10 */, /* 11 */, /* 12 */, /* 13 */ /***/ (function(module, exports, __webpack_require__) { Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = __webpack_require__(1); var $ = __webpack_require__(2); var fullcalendar_1 = __webpack_require__(0); var ResourceDayTableMixin_1 = __webpack_require__(21); var ResourceComponentFootprint_1 = __webpack_require__(6); var ResourceDayGrid = /** @class */ (function (_super) { tslib_1.__extends(ResourceDayGrid, _super); function ResourceDayGrid(view) { var _this = _super.call(this, view) || this; _this.isResourceFootprintsEnabled = true; return _this; } ResourceDayGrid.prototype.renderDates = function (dateProfile) { this.dateProfile = dateProfile; }; ResourceDayGrid.prototype.renderResources = function (resources) { this.registerResources(resources); this.renderGrid(); if (this.headContainerEl) { this.processHeadResourceEls(this.headContainerEl); } }; // TODO: make DRY with ResourceTimeGrid ResourceDayGrid.prototype.getHitFootprint = function (hit) { var plainFootprint = _super.prototype.getHitFootprint.call(this, hit); return new ResourceComponentFootprint_1.default(plainFootprint.unzonedRange, plainFootprint.isAllDay, this.getColResource(hit.col).id); }; ResourceDayGrid.prototype.componentFootprintToSegs = function (componentFootprint) { var resourceCnt = this.resourceCnt; var genericSegs = this.datesAboveResources ? this.sliceRangeByDay(componentFootprint.unzonedRange) : // each day-per-resource will need its own column this.sliceRangeByRow(componentFootprint.unzonedRange); var resourceSegs = []; for (var _i = 0, genericSegs_1 = genericSegs; _i < genericSegs_1.length; _i++) { var seg = genericSegs_1[_i]; for (var resourceIndex = 0; resourceIndex < resourceCnt; resourceIndex++) { var resourceObj = this.flattenedResources[resourceIndex]; if (!(componentFootprint instanceof ResourceComponentFootprint_1.default) || (componentFootprint.resourceId === resourceObj.id)) { var copy = $.extend({}, seg); copy.resource = resourceObj; if (this.isRTL) { copy.leftCol = this.indicesToCol(resourceIndex, seg.lastRowDayIndex); copy.rightCol = this.indicesToCol(resourceIndex, seg.firstRowDayIndex); } else { copy.leftCol = this.indicesToCol(resourceIndex, seg.firstRowDayIndex); copy.rightCol = this.indicesToCol(resourceIndex, seg.lastRowDayIndex); } resourceSegs.push(copy); } } } return resourceSegs; }; return ResourceDayGrid; }(fullcalendar_1.DayGrid)); exports.default = ResourceDayGrid; ResourceDayTableMixin_1.default.mixInto(ResourceDayGrid); /***/ }), /* 14 */ /***/ (function(module, exports, __webpack_require__) { Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = __webpack_require__(1); var $ = __webpack_require__(2); var moment = __webpack_require__(15); var fullcalendar_1 = __webpack_require__(0); var ClippedScroller_1 = __webpack_require__(24); var ScrollerCanvas_1 = __webpack_require__(25); var ScrollJoiner_1 = __webpack_require__(16); var ScrollFollower_1 = __webpack_require__(26); var TimelineEventRenderer_1 = __webpack_require__(17); var TimelineFillRenderer_1 = __webpack_require__(28); var TimelineHelperRenderer_1 = __webpack_require__(29); var TimelineEventDragging_1 = __webpack_require__(40); var TimelineEventResizing_1 = __webpack_require__(41); var TimelineView_defaults_1 = __webpack_require__(42); var TimelineView = /** @class */ (function (_super) { tslib_1.__extends(TimelineView, _super); function TimelineView(calendar, viewSpec) { var _this = _super.call(this, calendar, viewSpec) || this; _this.emphasizeWeeks = false; _this.isTimeBodyScrolled = false; _this.slotWidth = _this.opt('slotWidth'); return _this; } // Footprints // ------------------------------------------------------------------------------------------------------------------ /* TODO: avoid using Moments. use slat system somehow THEN, can have componentFootprintToSegs handle this on its own */ TimelineView.prototype.normalizeComponentFootprint = function (componentFootprint) { var adjustedEnd; var adjustedStart; var unzonedRange = componentFootprint.unzonedRange; if (this.isTimeScale) { adjustedStart = this.normalizeGridDate(unzonedRange.getStart()); adjustedEnd = this.normalizeGridDate(unzonedRange.getEnd()); } else { var dayRange = this.computeDayRange(unzonedRange); if (this.largeUnit) { adjustedStart = dayRange.start.clone().startOf(this.largeUnit); adjustedEnd = dayRange.end.clone().startOf(this.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 (!adjustedEnd.isSame(dayRange.end) || !adjustedEnd.isAfter(adjustedStart)) { adjustedEnd.add(this.slotDuration); } } else { adjustedStart = dayRange.start; adjustedEnd = dayRange.end; } } return new fullcalendar_1.ComponentFootprint(new fullcalendar_1.UnzonedRange(adjustedStart, adjustedEnd), !this.isTimeScale // isAllDay ); }; TimelineView.prototype.componentFootprintToSegs = function (footprint) { var footprintStart = footprint.unzonedRange.getStart(); var footprintEnd = footprint.unzonedRange.getEnd(); var normalFootprint = this.normalizeComponentFootprint(footprint); var segs = []; // protect against when the span is entirely in an invalid date region if (this.computeDateSnapCoverage(footprintStart) < this.computeDateSnapCoverage(footprintEnd)) { // intersect the footprint's range with the grid'd range var segRange = normalFootprint.unzonedRange.intersect(this.normalizedUnzonedRange); if (segRange) { var segStart = segRange.getStart(); var segEnd = segRange.getEnd(); segs.push({ start: segStart, end: segEnd, isStart: segRange.isStart && this.isValidDate(segStart), isEnd: segRange.isEnd && this.isValidDate(segEnd.clone().subtract(1)) }); } } // TODO: what if month slots? should round it to nearest month // TODO: dragging/resizing in this situation? deltas for dragging/resizing breaks down return segs; }; // Date Computation // ------------------------------------------------------------------------------------------------------------------ /* Makes the given date consistent with isTimeScale/largeUnit, so, either removes the times, ensures a time, or makes it the startOf largeUnit. Strips all timezones. Returns new copy. TODO: should maybe be called "normalizeRangeDate". */ TimelineView.prototype.normalizeGridDate = function (date) { var normalDate; if (this.isTimeScale) { normalDate = date.clone(); if (!normalDate.hasTime()) { normalDate.time(0); } } else { normalDate = date.clone().stripTime(); if (this.largeUnit) { normalDate.startOf(this.largeUnit); } } return normalDate; }; TimelineView.prototype.isValidDate = function (date) { if (this.isHiddenDay(date)) { return false; } else if (this.isTimeScale) { // determine if the time is within minTime/maxTime, which may have wacky values var ms = date.time() - this.dateProfile.minTime; // milliseconds since minTime ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock return ms < this.timeWindowMs; // before the maxTime? } else { return true; } }; TimelineView.prototype.updateGridDates = function () { var snapIndex = -1; var snapDiff = 0; // index of the diff :( var snapDiffToIndex = []; var snapIndexToDiff = []; var date = this.normalizedUnzonedStart.clone(); while (date < this.normalizedUnzonedEnd) { if (this.isValidDate(date)) { snapIndex++; snapDiffToIndex.push(snapIndex); snapIndexToDiff.push(snapDiff); } else { snapDiffToIndex.push(snapIndex + 0.5); } date.add(this.snapDuration); snapDiff++; } this.snapDiffToIndex = snapDiffToIndex; this.snapIndexToDiff = snapIndexToDiff; this.snapCnt = snapIndex + 1; // is always one behind this.slotCnt = this.snapCnt / this.snapsPerSlot; }; // Skeleton Rendering // ------------------------------------------------------------------------------------------------------------------ TimelineView.prototype.renderSkeleton = function () { this.el.addClass('fc-timeline'); if (this.opt('eventOverlap') === false) { this.el.addClass('fc-no-overlap'); } this.el.html(this.renderSkeletonHtml()); this.timeHeadEl = this.el.find('thead .fc-time-area'); this.timeBodyEl = this.el.find('tbody .fc-time-area'); this.timeHeadScroller = new ClippedScroller_1.default({ overflowX: 'clipped-scroll', overflowY: 'hidden' }); this.timeHeadScroller.canvas = new ScrollerCanvas_1.default(); this.timeHeadScroller.render(); this.timeHeadScroller.el.appendTo(this.timeHeadEl); this.timeBodyScroller = new ClippedScroller_1.default(); this.timeBodyScroller.canvas = new ScrollerCanvas_1.default(); this.timeBodyScroller.render(); this.timeBodyScroller.el.appendTo(this.timeBodyEl); this.isTimeBodyScrolled = false; // because if the grid has been rerendered, it will get a zero scroll this.timeBodyScroller.on('scroll', fullcalendar_1.proxy(this, 'handleTimeBodyScrolled')); this.slatContainerEl = $('<div class="fc-slats"/>').appendTo(this.timeBodyScroller.canvas.bgEl); this.segContainerEl = $('<div class="fc-event-container"/>').appendTo(this.timeBodyScroller.canvas.contentEl); this.bgSegContainerEl = this.timeBodyScroller.canvas.bgEl; this.timeBodyBoundCache = new fullcalendar_1.CoordCache({ els: this.timeBodyScroller.canvas.el, isHorizontal: true, isVertical: true }); this.timeScrollJoiner = new ScrollJoiner_1.default('horizontal', [this.timeHeadScroller, this.timeBodyScroller]); // the date/time text on the top axis that stays put while scrolling happens this.headDateFollower = new ScrollFollower_1.default(this.timeHeadScroller, true); // allowPointerEvents=true // the event titles that stay put while scrolling happens this.eventTitleFollower = new ScrollFollower_1.default(this.timeBodyScroller); this.eventTitleFollower.minTravel = 50; // if (this.isRTL) { this.eventTitleFollower.containOnNaturalRight = true; } else { this.eventTitleFollower.containOnNaturalLeft = true; } _super.prototype.renderSkeleton.call(this); }; TimelineView.prototype.renderSkeletonHtml = function () { var theme = this.calendar.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.unrenderSkeleton = function () { this.handleTimeBodyScrolled(0); _super.prototype.unrenderSkeleton.call(this); }; // Date Rendering // ------------------------------------------------------------------------------------------------------------------ TimelineView.prototype.renderDates = function (dateProfile) { TimelineView_defaults_1.initScaleProps(this); this.timeWindowMs = dateProfile.maxTime - dateProfile.minTime; // makes sure zone is stripped this.normalizedUnzonedStart = this.normalizeGridDate(dateProfile.renderUnzonedRange.getStart()); this.normalizedUnzonedEnd = this.normalizeGridDate(dateProfile.renderUnzonedRange.getEnd()); // apply minTime/maxTime // TODO: move towards .time(), but didn't play well with negatives. // TODO: View should be responsible. if (this.isTimeScale) { this.normalizedUnzonedStart.add(dateProfile.minTime); this.normalizedUnzonedEnd.subtract(1, 'day').add(dateProfile.maxTime); } this.normalizedUnzonedRange = new fullcalendar_1.UnzonedRange(this.normalizedUnzonedStart, this.normalizedUnzonedEnd); var slotDates = []; var date = this.normalizedUnzonedStart.clone(); this.calendar.localizeMoment(date); while (date < this.normalizedUnzonedEnd) { if (this.isValidDate(date)) { slotDates.push(date.clone()); } date.add(this.slotDuration); } this.slotDates = slotDates; this.updateGridDates(); var slatHtmlRes = this.renderSlatHtml(); this.timeHeadScroller.canvas.contentEl.html(slatHtmlRes.headHtml); this.timeHeadColEls = this.timeHeadScroller.canvas.contentEl.find('col'); this.slatContainerEl.html(slatHtmlRes.bodyHtml); this.slatColEls = this.slatContainerEl.find('col'); this.slatEls = this.slatContainerEl.find('td'); this.slatCoordCache = new fullcalendar_1.CoordCache({ els: this.slatEls, isHorizontal: true }); // for the inner divs within the slats // used for event rendering and scrollTime, to disregard slat border this.slatInnerCoordCache = new fullcalendar_1.CoordCache({ els: this.slatEls.find('> div'), isHorizontal: true, // we use this coord cache for getPosition* for event rendering. // workaround for .fc-content's negative margins. offsetParent: this.timeBodyScroller.canvas.el }); for (var i = 0; i < this.slotDates.length; i++) { date = this.slotDates[i]; this.publiclyTrigger('dayRender', { context: this, args: [date, this.slatEls.eq(i), this] }); } if (this.headDateFollower) { this.headDateFollower.setSpriteEls(this.timeHeadEl.find('tr:not(:last-child) .fc-cell-text')); } }; TimelineView.prototype.unrenderDates = function () { if (this.headDateFollower) { this.headDateFollower.clearSprites(); } this.timeHeadScroller.canvas.contentEl.empty(); this.slatContainerEl.empty(); // clear the widths, // for no jupiness when navigating this.timeHeadScroller.canvas.clearWidth(); this.timeBodyScroller.canvas.clearWidth(); }; TimelineView.prototype.renderSlatHtml = function () { var cell; var date; var rowCells; var format; var theme = this.calendar.theme; var labelInterval = this.labelInterval; var formats = this.headerFormats; var cellRows = formats.map(function (format) { return []; }); // indexed by row,col var leadingCell = null; var prevWeekNumber = null; var slotDates = this.slotDates; var slotCells = []; // meta var rowUnits = formats.map(function (format) { return (fullcalendar_1.queryMostGranularFormatUnit(format)); }); for (var _i = 0, slotDates_1 = slotDates; _i < slotDates_1.length; _i++) { date = slotDates_1[_i]; var weekNumber = date.week(); var isWeekStart = this.emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber); for (var row = 0; row < formats.length; row++) { format = formats[row]; rowCells = cellRows[row]; 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 = date.format(format); if (!leadingCell || (leadingCell.text !== text)) { newCell = this.buildCellObject(date, text, rowUnits[row]); } else { leadingCell.colspan += 1; } } else { if (!leadingCell || fullcalendar_1.isInt(fullcalendar_1.divideRangeByDuration(this.normalizedUnzonedStart, date, labelInterval))) { var text = date.format(format); newCell = this.buildCellObject(date, text, rowUnits[row]); } else { leadingCell.colspan += 1; } } if (newCell) { newCell.weekStart = isWeekStart; rowCells.push(newCell); } } slotCells.push({ weekStart: isWeekStart }); prevWeekNumber = weekNumber; } var isChrono = labelInterval > this.slotDuration; var isSingleDay = this.slotDuration.as('days') === 1; var html = '<table class="' + theme.getClass('tableGrid') + '">'; html += '<colgroup>'; for (var _a = 0, slotDates_2 = slotDates; _a < slotDates_2.length; _a++) { date = slotDates_2[_a]; html += '<col/>'; } html += '</colgroup>'; html += '<tbody>'; for (var i = 0; i < cellRows.length; i++) { rowCells = cellRows[i]; var isLast = i === (cellRows.length - 1); html += '<tr' + (isChrono && isLast ? ' class="fc-chrono"' : '') + '>'; for (var _b = 0, rowCells_1 = rowCells; _b < rowCells_1.length; _b++) { cell = rowCells_1[_b]; var headerCellClassNames = [theme.getClass('widgetHeader')]; if (cell.weekStart) { headerCellClassNames.push('fc-em-cell'); } if (isSingleDay) { headerCellClassNames = headerCellClassNames.concat(this.getDayClasses(cell.date, true) // adds "today" class and other day-based classes ); } html += '<th class="' + headerCellClassNames.join(' ') + '"' + ' data-date="' + cell.date.format() + '"' + (cell.colspan > 1 ? ' colspan="' + cell.colspan + '"' : '') + '>' + '<div class="fc-cell-content">' + cell.spanHtml + '</div>' + '</th>'; } html += '</tr>'; } html += '</tbody></table>'; var slatHtml = '<table class="' + theme.getClass('tableGrid') + '">'; slatHtml += '<colgroup>'; for (var _c = 0, slotCells_1 = slotCells; _c < slotCells_1.length; _c++) { cell = slotCells_1[_c]; slatHtml += '<col/>'; } slatHtml += '</colgroup>'; slatHtml += '<tbody><tr>'; for (var i = 0; i < slotCells.length; i++) { cell = slotCells[i]; date = slotDates[i]; slatHtml += this.slatCellHtml(date, cell.weekStart); } slatHtml += '</tr></tbody></table>'; return { headHtml: html, bodyHtml: slatHtml }; }; TimelineView.prototype.buildCellObject = function (date, text, rowUnit) { date = date.clone(); // ensure our own reference var spanHtml = this.buildGotoAnchorHtml({ date: date, type: rowUnit, forceOff: !rowUnit }, { 'class': 'fc-cell-text' }, fullcalendar_1.htmlEscape(text)); return { text: text, spanHtml: spanHtml, date: date, colspan: 1 }; }; TimelineView.prototype.slatCellHtml = function (date, isEm) { var classes; var theme = this.calendar.theme; if (this.isTimeScale) { classes = []; classes.push(fullcalendar_1.isInt(fullcalendar_1.divideRangeByDuration(this.normalizedUnzonedStart, date, this.labelInterval)) ? 'fc-major' : 'fc-minor'); } else { classes = this.getDayClasses(date); classes.push('fc-day'); } classes.unshift(theme.getClass('widgetContent')); if (isEm) { classes.push('fc-em-cell'); } return '<td class="' + classes.join(' ') + '"' + ' data-date="' + date.format() + '"' + '><div /></td>'; }; // Business Hours // ------------------------------------------------------------------------------------------------------------------ TimelineView.prototype.renderBusinessHours = function (businessHourPayload) { if (!this.largeUnit) { return _super.prototype.renderBusinessHours.call(this, businessHourPayload); } }; // Now Indicator // ------------------------------------------------------------------------------------------------------------------ TimelineView.prototype.getNowIndicatorUnit = function () { // TODO: converge with largeUnit. precompute if (this.isTimeScale) { return fullcalendar_1.computeGreatestUnit(this.slotDuration); } }; // will only execute if isTimeScale TimelineView.prototype.renderNowIndicator = function (date) { var nodes = []; date = this.normalizeGridDate(date); if (this.normalizedUnzonedRange.containsDate(date)) { var coord = this.dateToCoord(date); var css = this.isRTL ? { right: -coord } : { left: coord }; nodes.push($("<div class='fc-now-indicator fc-now-indicator-arrow'></div>") .css(css) .appendTo(this.timeHeadScroller.canvas.el)[0]); nodes.push($("<div class='fc-now-indicator fc-now-indicator-line'></div>") .css(css) .appendTo(this.timeBodyScroller.canvas.el)[0]); } this.nowIndicatorEls = $(nodes); }; // will only execute if isTimeScale TimelineView.prototype.unrenderNowIndicator = function () { if (this.nowIndicatorEls) { this.nowIndicatorEls.remove(); this.nowIndicatorEls = null; } }; // Sizing // ------------------------------------------------------------------------------------------------------------------ TimelineView.prototype.updateSize = function (totalHeight, isAuto, isResize) { var bodyHeight; var containerMinWidth; var containerWidth; var nonLastSlotWidth; if (isAuto) { bodyHeight = 'auto'; } else { bodyHeight = totalHeight - this.headHeight() - this.queryMiscHeight(); } this.timeBodyScroller.setHeight(bodyHeight); // reason for this complicated method is that things went wrong when: // slots/headers didn't fill content area and needed to be stretched // cells wouldn't align (rounding issues with available width calculated // differently because of padding VS scrollbar trick) var isDatesRendered = this.timeHeadColEls; // TODO: refactor use of this if (isDatesRendered) { var slotWidth = Math.round(this.slotWidth || (this.slotWidth = this.computeSlotWidth())); containerWidth = slotWidth * this.slotDates.length; containerMinWidth = ''; nonLastSlotWidth = slotWidth; var availableWidth = this.timeBodyScroller.getClientWidth(); if (availableWidth > containerWidth) { containerMinWidth = availableWidth; containerWidth = ''; nonLastSlotWidth = Math.floor(availableWidth / this.slotDates.length); } } else { containerWidth = ''; containerMinWidth = ''; } this.timeHeadScroller.canvas.setWidth(containerWidth); this.timeHeadScroller.canvas.setMinWidth(containerMinWidth); this.timeBodyScroller.canvas.setWidth(containerWidth); this.timeBodyScroller.canvas.setMinWidth(containerMinWidth); if (isDatesRendered) { this.timeHeadColEls.slice(0, -1).add(this.slatColEls.slice(0, -1)) .css('width', nonLastSlotWidth); } this.timeHeadScroller.updateSize(); this.timeBodyScroller.updateSize(); this.timeScrollJoiner.update(); if (isDatesRendered) { this.buildCoords(); // TODO: left/right CSS assignment also happens earlier in renderFgSegs this.updateSegPositions(); // this updateSize method is triggered by callers who don't always subsequently call updateNowIndicator, // and updateSize always has the risk of changing horizontal spacing which will affect nowIndicator positioning, // so always call it here too. will often rerender twice unfortunately. // TODO: more closely integrate updateSize with updateNowIndicator this.updateNowIndicator(); } if (this.headDateFollower) { this.headDateFollower.update(); } if (this.eventTitleFollower) { this.eventTitleFollower.update(); } }; TimelineView.prototype.queryMiscHeight = function () { return this.el.outerHeight() - this.timeHeadScroller.el.outerHeight() - this.timeBodyScroller.el.outerHeight(); }; TimelineView.prototype.computeSlotWidth = function () { var maxInnerWidth = 0; // TODO: harness core's `matchCellWidths` for this var innerEls = this.timeHeadEl.find('tr:last-child th .fc-cell-text'); // TODO: cache innerEls.each(function (i, node) { var innerWidth = $(node).outerWidth(); return maxInnerWidth = Math.max(maxInnerWidth, innerWidth); }); var headerWidth = maxInnerWidth + 1; // assume no padding, and one pixel border var slotsPerLabel = fullcalendar_1.divideDurationByDuration(this.labelInterval, this.slotDuration); // TODO: rename labelDuration? var slotWidth = Math.ceil(headerWidth / slotsPerLabel); var minWidth = this.timeHeadColEls.eq(0).css('min-width'); if (minWidth) { minWidth = parseInt(minWidth, 10); if (minWidth) { slotWidth = Math.max(slotWidth, minWidth); } } return slotWidth; }; // Coordinates // ------------------------------------------------------------------------------------------------------------------ TimelineView.prototype.buildCoords = function () { this.timeBodyBoundCache.build(); this.slatCoordCache.build(); this.slatInnerCoordCache.build(); }; // returned value is between 0 and the number of snaps TimelineView.prototype.computeDateSnapCoverage = function (date) { var snapDiff = fullcalendar_1.divideRangeByDuration(this.normalizedUnzonedStart, date, this.snapDuration); if (snapDiff < 0) { return 0; } else if (snapDiff >= this.snapDiffToIndex.length) { return this.snapCnt; } else { var snapDiffInt = Math.floor(snapDiff); var snapCoverage = this.snapDiffToIndex[snapDiffInt]; if (fullcalendar_1.isInt(snapCoverage)) { 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 TimelineView.prototype.dateToCoord = function (date) { var snapCoverage = this.computeDateSnapCoverage(date); var slotCoverage = snapCoverage / this.snapsPerSlot; var slotIndex = Math.floor(slotCoverage); slotIndex = Math.min(slotIndex, this.slotCnt - 1); var partial = slotCoverage - slotIndex; var coordCache = this.slatInnerCoordCache; if (this.isRTL) { return (coordCache.getRightPosition(slotIndex) - (coordCache.getWidth(slotIndex) * partial)) - this.timeBodyBoundCache.getWidth(0); } else { return (coordCache.getLeftPosition(slotIndex) + (coordCache.getWidth(slotIndex) * partial)); } }; TimelineView.prototype.rangeToCoords = function (range) { if (this.isRTL) { return { right: this.dateToCoord(range.start), left: this.dateToCoord(range.end) }; } else { return { left: this.dateToCoord(range.start), right: this.dateToCoord(range.end) }; } }; // a getter / setter TimelineView.prototype.headHeight = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var table = this.timeHeadScroller.canvas.contentEl.find('table'); return table.height.apply(table, args); }; // this needs to be called if v scrollbars appear on body container. or zooming TimelineView.prototype.updateSegPositions = function () { var segs = [].concat(this.getEventSegs(), this.getBusinessHourSegs()); for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) { var seg = segs_1[_i]; var coords = this.rangeToCoords(seg); seg.el.css({ left: (seg.left = coords.left), right: -(seg.right = coords.right) }); } }; // Scrolling // --------------------------------------------------------------------------------- TimelineView.prototype.handleTimeBodyScrolled = function (top) { if (top) { if (!this.isTimeBodyScrolled) { this.isTimeBodyScrolled = true; this.el.addClass('fc-scrolled'); } } else { if (this.isTimeBodyScrolled) { this.isTimeBodyScrolled = false; this.el.removeClass('fc-scrolled'); } } }; TimelineView.prototype.computeInitialDateScroll = function () { var unzonedRange = this.get('dateProfile').activeUnzonedRange; var left = 0; if (this.isTimeScale) { var scrollTime = this.opt('scrollTime'); if (scrollTime) { scrollTime = moment.duration(scrollTime); left = this.dateToCoord(unzonedRange.getStart().time(scrollTime)); // TODO: fix this for RTL } } return { left: left }; }; TimelineView.prototype.queryDateScroll = function () { return { left: this.timeBodyScroller.getScrollLeft() }; }; TimelineView.prototype.applyDateScroll = function (scroll) { if (scroll.left != null) { // TODO: workaround for FF. the ScrollJoiner sibling won't react fast enough // to override the native initial crappy scroll that FF applies. // TODO: have the ScrollJoiner handle this // Similar code in ResourceTimelineView::setScroll this.timeHeadScroller.setScrollLeft(scroll.left); this.timeBodyScroller.setScrollLeft(scroll.left); } }; // Hit System // ------------------------------------------------------------------------------------------------------------------ TimelineView.prototype.prepareHits = function () { this.buildCoords(); }; // FYI: we don't want to clear the slatCoordCache in releaseHits() // because those coordinates are needed for dateToCoord() TimelineView.prototype.queryHit = function (leftOffset, topOffset) { var snapsPerSlot = this.snapsPerSlot; var slatCoordCache = this.slatCoordCache; var timeBodyBoundCache = this.timeBodyBoundCache; // within scroll container's content rectangle? if (timeBodyBoundCache.isPointInBounds(leftOffset, topOffset)) { var slatIndex = slatCoordCache.getHorizontalIndex(leftOffset); if (slatIndex != null) { var localSnapIndex = void 0; var partial = void 0; var snapIndex = void 0; var snapLeft = void 0; var snapRight = void 0; var slatWidth = slatCoordCache.getWidth(slatIndex); if (this.isRTL) {