|
|
@@ -1,509 +1,281 @@
|
|
|
|
|
|
-/* Methods for rendering SEGMENTS, pieces of content that live on the view
|
|
|
- ( this file is no longer just for events )
|
|
|
-----------------------------------------------------------------------------------------------------------------------*/
|
|
|
+/*
|
|
|
+Only handles foreground segs.
|
|
|
+Does not own rendering. Use for low-level util methods by TimeGrid.
|
|
|
+*/
|
|
|
+var TimeGridEventRenderer = EventRenderUtils.extend({
|
|
|
|
|
|
-TimeGrid.mixin({
|
|
|
+ timeGrid: null,
|
|
|
|
|
|
- colContainerEls: null, // containers for each column
|
|
|
|
|
|
- // inner-containers for each column where different types of segs live
|
|
|
- fgContainerEls: null,
|
|
|
- bgContainerEls: null,
|
|
|
- helperContainerEls: null,
|
|
|
- highlightContainerEls: null,
|
|
|
- businessContainerEls: null,
|
|
|
+ constructor: function(timeGrid) {
|
|
|
+ EventRenderUtils.call(this, timeGrid);
|
|
|
|
|
|
- // arrays of different types of displayed segments
|
|
|
- bgSegs: null,
|
|
|
- helperSegs: null,
|
|
|
- highlightSegs: null,
|
|
|
- businessSegs: null,
|
|
|
-
|
|
|
-
|
|
|
- eventRenderUtilsClass: SegChronoComponentMixin.eventRenderUtilsClass.extend({
|
|
|
-
|
|
|
- timeGrid: null,
|
|
|
- fgSegs: null,
|
|
|
-
|
|
|
-
|
|
|
- constructor: function(timeGrid) {
|
|
|
- SegChronoComponentMixin.eventRenderUtilsClass.call(this, timeGrid);
|
|
|
- this.timeGrid = timeGrid;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Given an array of foreground segments, render a DOM element for each, computes position,
|
|
|
- // and attaches to the column inner-container elements.
|
|
|
- renderFgSegsIntoContainers: function(segs, containerEls) {
|
|
|
- var segsByCol;
|
|
|
- var col;
|
|
|
-
|
|
|
- segs = this.renderFgSegEls(segs);
|
|
|
- segsByCol = this.timeGrid.groupSegsByCol(segs);
|
|
|
-
|
|
|
- for (col = 0; col < this.timeGrid.colCnt; col++) {
|
|
|
- this.updateFgSegCoords(segsByCol[col]);
|
|
|
- }
|
|
|
-
|
|
|
- this.timeGrid.attachSegsByCol(segsByCol, containerEls);
|
|
|
-
|
|
|
- return segs;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- unrenderFgSegs: function() {
|
|
|
- this.timeGrid.unrenderNamedSegs('fgSegs');
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Computes a default event time formatting string if `timeFormat` is not explicitly defined
|
|
|
- computeEventTimeFormat: function() {
|
|
|
- return this.opt('noMeridiemTimeFormat'); // like "6:30" (no AM/PM)
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Computes a default `displayEventEnd` value if one is not expliclty defined
|
|
|
- computeDisplayEventEnd: function() {
|
|
|
- return true;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Renders the HTML for a single event segment's default rendering
|
|
|
- fgSegHtml: function(seg, disableResizing) {
|
|
|
- var view = this.view;
|
|
|
- var calendar = view.calendar;
|
|
|
- var componentFootprint = seg.footprint.componentFootprint;
|
|
|
- var isAllDay = componentFootprint.isAllDay;
|
|
|
- var eventDef = seg.footprint.eventDef;
|
|
|
- var isDraggable = view.isEventDefDraggable(eventDef);
|
|
|
- var isResizableFromStart = !disableResizing && seg.isStart && view.isEventDefResizableFromStart(eventDef);
|
|
|
- var isResizableFromEnd = !disableResizing && seg.isEnd && view.isEventDefResizableFromEnd(eventDef);
|
|
|
- var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd);
|
|
|
- var skinCss = cssToStr(this.getSkinCss(seg.footprint));
|
|
|
- var timeText;
|
|
|
- var fullTimeText; // more verbose time text. for the print stylesheet
|
|
|
- var startTimeText; // just the start time text
|
|
|
-
|
|
|
- classes.unshift('fc-time-grid-event', 'fc-v-event');
|
|
|
-
|
|
|
- // if the event appears to span more than one day...
|
|
|
- if (view.isMultiDayRange(componentFootprint.unzonedRange)) {
|
|
|
- // Don't display time text on segments that run entirely through a day.
|
|
|
- // That would appear as midnight-midnight and would look dumb.
|
|
|
- // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
|
|
|
- if (seg.isStart || seg.isEnd) {
|
|
|
- var zonedStart = calendar.msToMoment(seg.startMs);
|
|
|
- var zonedEnd = calendar.msToMoment(seg.endMs);
|
|
|
- timeText = this._getTimeText(zonedStart, zonedEnd, isAllDay);
|
|
|
- fullTimeText = this._getTimeText(zonedStart, zonedEnd, isAllDay, 'LT');
|
|
|
- startTimeText = this._getTimeText(zonedStart, zonedEnd, isAllDay, null, false); // displayEnd=false
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- // Display the normal time text for the *event's* times
|
|
|
- timeText = this.getTimeText(seg.footprint);
|
|
|
- fullTimeText = this.getTimeText(seg.footprint, 'LT');
|
|
|
- startTimeText = this.getTimeText(seg.footprint, null, false); // displayEnd=false
|
|
|
- }
|
|
|
-
|
|
|
- return '<a class="' + classes.join(' ') + '"' +
|
|
|
- (eventDef.url ?
|
|
|
- ' href="' + htmlEscape(eventDef.url) + '"' :
|
|
|
- ''
|
|
|
- ) +
|
|
|
- (skinCss ?
|
|
|
- ' style="' + skinCss + '"' :
|
|
|
- ''
|
|
|
- ) +
|
|
|
- '>' +
|
|
|
- '<div class="fc-content">' +
|
|
|
- (timeText ?
|
|
|
- '<div class="fc-time"' +
|
|
|
- ' data-start="' + htmlEscape(startTimeText) + '"' +
|
|
|
- ' data-full="' + htmlEscape(fullTimeText) + '"' +
|
|
|
- '>' +
|
|
|
- '<span>' + htmlEscape(timeText) + '</span>' +
|
|
|
- '</div>' :
|
|
|
- ''
|
|
|
- ) +
|
|
|
- (eventDef.title ?
|
|
|
- '<div class="fc-title">' +
|
|
|
- htmlEscape(eventDef.title) +
|
|
|
- '</div>' :
|
|
|
- ''
|
|
|
- ) +
|
|
|
- '</div>' +
|
|
|
- '<div class="fc-bg"/>' +
|
|
|
- /* TODO: write CSS for this
|
|
|
- (isResizableFromStart ?
|
|
|
- '<div class="fc-resizer fc-start-resizer" />' :
|
|
|
- ''
|
|
|
- ) +
|
|
|
- */
|
|
|
- (isResizableFromEnd ?
|
|
|
- '<div class="fc-resizer fc-end-resizer" />' :
|
|
|
- ''
|
|
|
- ) +
|
|
|
- '</a>';
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Given segments that are assumed to all live in the *same column*,
|
|
|
- // compute their verical/horizontal coordinates and assign to their elements.
|
|
|
- updateFgSegCoords: function(segs) {
|
|
|
- this.timeGrid.computeSegVerticals(segs); // horizontals relies on this
|
|
|
- this.computeFgSegHorizontals(segs); // compute horizontal coordinates, z-index's, and reorder the array
|
|
|
- this.timeGrid.assignSegVerticals(segs);
|
|
|
- this.assignFgSegHorizontals(segs);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
|
|
|
- // NOTE: Also reorders the given array by date!
|
|
|
- computeFgSegHorizontals: function(segs) {
|
|
|
- var levels;
|
|
|
- var level0;
|
|
|
- var i;
|
|
|
-
|
|
|
- this.sortEventSegs(segs); // order by certain criteria
|
|
|
- levels = buildSlotSegLevels(segs);
|
|
|
- computeForwardSlotSegs(levels);
|
|
|
-
|
|
|
- if ((level0 = levels[0])) {
|
|
|
-
|
|
|
- for (i = 0; i < level0.length; i++) {
|
|
|
- computeSlotSegPressures(level0[i]);
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < level0.length; i++) {
|
|
|
- this.computeFgSegForwardBack(level0[i], 0, 0);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
|
|
|
- // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
|
|
|
- // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
|
|
|
- //
|
|
|
- // The segment might be part of a "series", which means consecutive segments with the same pressure
|
|
|
- // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
|
|
|
- // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
|
|
|
- // coordinate of the first segment in the series.
|
|
|
- computeFgSegForwardBack: function(seg, seriesBackwardPressure, seriesBackwardCoord) {
|
|
|
- var forwardSegs = seg.forwardSegs;
|
|
|
- var i;
|
|
|
-
|
|
|
- if (seg.forwardCoord === undefined) { // not already computed
|
|
|
-
|
|
|
- if (!forwardSegs.length) {
|
|
|
-
|
|
|
- // if there are no forward segments, this segment should butt up against the edge
|
|
|
- seg.forwardCoord = 1;
|
|
|
- }
|
|
|
- else {
|
|
|
-
|
|
|
- // sort highest pressure first
|
|
|
- this.sortForwardSegs(forwardSegs);
|
|
|
-
|
|
|
- // this segment's forwardCoord will be calculated from the backwardCoord of the
|
|
|
- // highest-pressure forward segment.
|
|
|
- this.computeFgSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
|
|
|
- seg.forwardCoord = forwardSegs[0].backwardCoord;
|
|
|
- }
|
|
|
-
|
|
|
- // calculate the backwardCoord from the forwardCoord. consider the series
|
|
|
- seg.backwardCoord = seg.forwardCoord -
|
|
|
- (seg.forwardCoord - seriesBackwardCoord) / // available width for series
|
|
|
- (seriesBackwardPressure + 1); // # of segments in the series
|
|
|
-
|
|
|
- // use this segment's coordinates to computed the coordinates of the less-pressurized
|
|
|
- // forward segments
|
|
|
- for (i=0; i<forwardSegs.length; i++) {
|
|
|
- this.computeFgSegForwardBack(forwardSegs[i], 0, seg.forwardCoord);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- sortForwardSegs: function(forwardSegs) {
|
|
|
- forwardSegs.sort(proxy(this, 'compareForwardSegs'));
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // A cmp function for determining which forward segment to rely on more when computing coordinates.
|
|
|
- compareForwardSegs: function(seg1, seg2) {
|
|
|
- // put higher-pressure first
|
|
|
- return seg2.forwardPressure - seg1.forwardPressure ||
|
|
|
- // put segments that are closer to initial edge first (and favor ones with no coords yet)
|
|
|
- (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
|
|
|
- // do normal sorting...
|
|
|
- this.compareEventSegs(seg1, seg2);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Given foreground event segments that have already had their position coordinates computed,
|
|
|
- // assigns position-related CSS values to their elements.
|
|
|
- assignFgSegHorizontals: function(segs) {
|
|
|
- var i, seg;
|
|
|
-
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- seg = segs[i];
|
|
|
- seg.el.css(this.generateFgSegHorizontalCss(seg));
|
|
|
-
|
|
|
- // if the height is short, add a className for alternate styling
|
|
|
- if (seg.bottom - seg.top < 30) {
|
|
|
- seg.el.addClass('fc-short');
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Generates an object with CSS properties/values that should be applied to an event segment element.
|
|
|
- // Contains important positioning-related properties that should be applied to any event element, customized or not.
|
|
|
- generateFgSegHorizontalCss: function(seg) {
|
|
|
- var shouldOverlap = this.opt('slotEventOverlap');
|
|
|
- var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point
|
|
|
- var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point
|
|
|
- var props = this.timeGrid.generateSegVerticalCss(seg); // get top/bottom first
|
|
|
- var left; // amount of space from left edge, a fraction of the total width
|
|
|
- var right; // amount of space from right edge, a fraction of the total width
|
|
|
-
|
|
|
- if (shouldOverlap) {
|
|
|
- // double the width, but don't go beyond the maximum forward coordinate (1.0)
|
|
|
- forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2);
|
|
|
- }
|
|
|
-
|
|
|
- if (this.timeGrid.isRTL) {
|
|
|
- left = 1 - forwardCoord;
|
|
|
- right = backwardCoord;
|
|
|
- }
|
|
|
- else {
|
|
|
- left = backwardCoord;
|
|
|
- right = 1 - forwardCoord;
|
|
|
- }
|
|
|
-
|
|
|
- props.zIndex = seg.level + 1; // convert from 0-base to 1-based
|
|
|
- props.left = left * 100 + '%';
|
|
|
- props.right = right * 100 + '%';
|
|
|
-
|
|
|
- if (shouldOverlap && seg.forwardPressure) {
|
|
|
- // add padding to the edge so that forward stacked events don't cover the resizer's icon
|
|
|
- props[this.isRTL ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
|
|
|
- }
|
|
|
-
|
|
|
- return props;
|
|
|
- }
|
|
|
-
|
|
|
- }),
|
|
|
-
|
|
|
-
|
|
|
- // Renders the DOM that the view's content will live in
|
|
|
- renderContentSkeleton: function() {
|
|
|
- var cellHtml = '';
|
|
|
- var i;
|
|
|
- var skeletonEl;
|
|
|
-
|
|
|
- for (i = 0; i < this.colCnt; i++) {
|
|
|
- cellHtml +=
|
|
|
- '<td>' +
|
|
|
- '<div class="fc-content-col">' +
|
|
|
- '<div class="fc-event-container fc-helper-container"></div>' +
|
|
|
- '<div class="fc-event-container"></div>' +
|
|
|
- '<div class="fc-highlight-container"></div>' +
|
|
|
- '<div class="fc-bgevent-container"></div>' +
|
|
|
- '<div class="fc-business-container"></div>' +
|
|
|
- '</div>' +
|
|
|
- '</td>';
|
|
|
- }
|
|
|
-
|
|
|
- skeletonEl = $(
|
|
|
- '<div class="fc-content-skeleton">' +
|
|
|
- '<table>' +
|
|
|
- '<tr>' + cellHtml + '</tr>' +
|
|
|
- '</table>' +
|
|
|
- '</div>'
|
|
|
- );
|
|
|
-
|
|
|
- this.colContainerEls = skeletonEl.find('.fc-content-col');
|
|
|
- this.helperContainerEls = skeletonEl.find('.fc-helper-container');
|
|
|
- this.fgContainerEls = skeletonEl.find('.fc-event-container:not(.fc-helper-container)');
|
|
|
- this.bgContainerEls = skeletonEl.find('.fc-bgevent-container');
|
|
|
- this.highlightContainerEls = skeletonEl.find('.fc-highlight-container');
|
|
|
- this.businessContainerEls = skeletonEl.find('.fc-business-container');
|
|
|
-
|
|
|
- this.bookendCells(skeletonEl.find('tr')); // TODO: do this on string level
|
|
|
- this.el.append(skeletonEl);
|
|
|
+ this.timeGrid = timeGrid;
|
|
|
},
|
|
|
|
|
|
|
|
|
- /* Foreground Helper Events
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- renderHelperSegs: function(segs, sourceSeg) {
|
|
|
- var helperEls = [];
|
|
|
- var i, seg;
|
|
|
- var sourceEl;
|
|
|
+ // Given an array of foreground segments, render a DOM element for each, computes position,
|
|
|
+ // and attaches to the column inner-container elements.
|
|
|
+ renderFgSegsIntoContainers: function(segs, containerEls) {
|
|
|
+ var segsByCol;
|
|
|
+ var col;
|
|
|
|
|
|
- segs = this.eventRenderUtils.renderFgSegsIntoContainers(segs, this.helperContainerEls);
|
|
|
+ segs = this.renderFgSegEls(segs);
|
|
|
+ segsByCol = this.timeGrid.groupSegsByCol(segs);
|
|
|
|
|
|
- // Try to make the segment that is in the same row as sourceSeg look the same
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- seg = segs[i];
|
|
|
- if (sourceSeg && sourceSeg.col === seg.col) {
|
|
|
- sourceEl = sourceSeg.el;
|
|
|
- seg.el.css({
|
|
|
- left: sourceEl.css('left'),
|
|
|
- right: sourceEl.css('right'),
|
|
|
- 'margin-left': sourceEl.css('margin-left'),
|
|
|
- 'margin-right': sourceEl.css('margin-right')
|
|
|
- });
|
|
|
- }
|
|
|
- helperEls.push(seg.el[0]);
|
|
|
+ for (col = 0; col < this.timeGrid.colCnt; col++) {
|
|
|
+ this.updateFgSegCoords(segsByCol[col]);
|
|
|
}
|
|
|
|
|
|
- this.helperSegs = segs;
|
|
|
+ this.timeGrid.attachSegsByCol(segsByCol, containerEls);
|
|
|
|
|
|
- return $(helperEls); // must return rendered helpers
|
|
|
+ return segs;
|
|
|
},
|
|
|
|
|
|
|
|
|
- unrenderHelperSegs: function() {
|
|
|
- this.unrenderNamedSegs('helperSegs');
|
|
|
+ unrenderFgSegs: function() {
|
|
|
+ this.timeGrid.unrenderNamedSegs('fgSegs');
|
|
|
},
|
|
|
|
|
|
|
|
|
- /* Foreground Events
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- renderFgSegs: function(segs) {
|
|
|
- segs = this.eventRenderUtils.renderFgSegsIntoContainers(segs, this.fgContainerEls);
|
|
|
+ // Computes a default event time formatting string if `timeFormat` is not explicitly defined
|
|
|
+ computeEventTimeFormat: function() {
|
|
|
+ return this.opt('noMeridiemTimeFormat'); // like "6:30" (no AM/PM)
|
|
|
+ },
|
|
|
|
|
|
- this.fgSegs = segs;
|
|
|
|
|
|
- return segs; // needed for Grid::renderEvents
|
|
|
+ // Computes a default `displayEventEnd` value if one is not expliclty defined
|
|
|
+ computeDisplayEventEnd: function() {
|
|
|
+ return true;
|
|
|
},
|
|
|
|
|
|
|
|
|
- /* Background Events
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
+ // Renders the HTML for a single event segment's default rendering
|
|
|
+ fgSegHtml: function(seg, disableResizing) {
|
|
|
+ var view = this.view;
|
|
|
+ var calendar = view.calendar;
|
|
|
+ var componentFootprint = seg.footprint.componentFootprint;
|
|
|
+ var isAllDay = componentFootprint.isAllDay;
|
|
|
+ var eventDef = seg.footprint.eventDef;
|
|
|
+ var isDraggable = view.isEventDefDraggable(eventDef);
|
|
|
+ var isResizableFromStart = !disableResizing && seg.isStart && view.isEventDefResizableFromStart(eventDef);
|
|
|
+ var isResizableFromEnd = !disableResizing && seg.isEnd && view.isEventDefResizableFromEnd(eventDef);
|
|
|
+ var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd);
|
|
|
+ var skinCss = cssToStr(this.getSkinCss(seg.footprint));
|
|
|
+ var timeText;
|
|
|
+ var fullTimeText; // more verbose time text. for the print stylesheet
|
|
|
+ var startTimeText; // just the start time text
|
|
|
+
|
|
|
+ classes.unshift('fc-time-grid-event', 'fc-v-event');
|
|
|
+
|
|
|
+ // if the event appears to span more than one day...
|
|
|
+ if (view.isMultiDayRange(componentFootprint.unzonedRange)) {
|
|
|
+ // Don't display time text on segments that run entirely through a day.
|
|
|
+ // That would appear as midnight-midnight and would look dumb.
|
|
|
+ // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
|
|
|
+ if (seg.isStart || seg.isEnd) {
|
|
|
+ var zonedStart = calendar.msToMoment(seg.startMs);
|
|
|
+ var zonedEnd = calendar.msToMoment(seg.endMs);
|
|
|
+ timeText = this._getTimeText(zonedStart, zonedEnd, isAllDay);
|
|
|
+ fullTimeText = this._getTimeText(zonedStart, zonedEnd, isAllDay, 'LT');
|
|
|
+ startTimeText = this._getTimeText(zonedStart, zonedEnd, isAllDay, null, false); // displayEnd=false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // Display the normal time text for the *event's* times
|
|
|
+ timeText = this.getTimeText(seg.footprint);
|
|
|
+ fullTimeText = this.getTimeText(seg.footprint, 'LT');
|
|
|
+ startTimeText = this.getTimeText(seg.footprint, null, false); // displayEnd=false
|
|
|
+ }
|
|
|
|
|
|
- renderBgSegs: function(segs) {
|
|
|
- segs = this.fillSystem.buildSegEls('bgEvent', segs);
|
|
|
- this.updateSegVerticals(segs);
|
|
|
- this.attachSegsByCol(this.groupSegsByCol(segs), this.bgContainerEls);
|
|
|
- this.bgSegs = segs;
|
|
|
- return segs; // needed for Grid::renderEvents
|
|
|
+ return '<a class="' + classes.join(' ') + '"' +
|
|
|
+ (eventDef.url ?
|
|
|
+ ' href="' + htmlEscape(eventDef.url) + '"' :
|
|
|
+ ''
|
|
|
+ ) +
|
|
|
+ (skinCss ?
|
|
|
+ ' style="' + skinCss + '"' :
|
|
|
+ ''
|
|
|
+ ) +
|
|
|
+ '>' +
|
|
|
+ '<div class="fc-content">' +
|
|
|
+ (timeText ?
|
|
|
+ '<div class="fc-time"' +
|
|
|
+ ' data-start="' + htmlEscape(startTimeText) + '"' +
|
|
|
+ ' data-full="' + htmlEscape(fullTimeText) + '"' +
|
|
|
+ '>' +
|
|
|
+ '<span>' + htmlEscape(timeText) + '</span>' +
|
|
|
+ '</div>' :
|
|
|
+ ''
|
|
|
+ ) +
|
|
|
+ (eventDef.title ?
|
|
|
+ '<div class="fc-title">' +
|
|
|
+ htmlEscape(eventDef.title) +
|
|
|
+ '</div>' :
|
|
|
+ ''
|
|
|
+ ) +
|
|
|
+ '</div>' +
|
|
|
+ '<div class="fc-bg"/>' +
|
|
|
+ /* TODO: write CSS for this
|
|
|
+ (isResizableFromStart ?
|
|
|
+ '<div class="fc-resizer fc-start-resizer" />' :
|
|
|
+ ''
|
|
|
+ ) +
|
|
|
+ */
|
|
|
+ (isResizableFromEnd ?
|
|
|
+ '<div class="fc-resizer fc-end-resizer" />' :
|
|
|
+ ''
|
|
|
+ ) +
|
|
|
+ '</a>';
|
|
|
},
|
|
|
|
|
|
|
|
|
- unrenderBgSegs: function() {
|
|
|
- this.unrenderNamedSegs('bgSegs');
|
|
|
+ // Given segments that are assumed to all live in the *same column*,
|
|
|
+ // compute their verical/horizontal coordinates and assign to their elements.
|
|
|
+ updateFgSegCoords: function(segs) {
|
|
|
+ this.timeGrid.computeSegVerticals(segs); // horizontals relies on this
|
|
|
+ this.computeFgSegHorizontals(segs); // compute horizontal coordinates, z-index's, and reorder the array
|
|
|
+ this.timeGrid.assignSegVerticals(segs);
|
|
|
+ this.assignFgSegHorizontals(segs);
|
|
|
},
|
|
|
|
|
|
|
|
|
- /* Seg Rendering Utils
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
+ // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
|
|
|
+ // NOTE: Also reorders the given array by date!
|
|
|
+ computeFgSegHorizontals: function(segs) {
|
|
|
+ var levels;
|
|
|
+ var level0;
|
|
|
+ var i;
|
|
|
|
|
|
+ this.sortEventSegs(segs); // order by certain criteria
|
|
|
+ levels = buildSlotSegLevels(segs);
|
|
|
+ computeForwardSlotSegs(levels);
|
|
|
|
|
|
- // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col
|
|
|
- groupSegsByCol: function(segs) {
|
|
|
- var segsByCol = [];
|
|
|
- var i;
|
|
|
+ if ((level0 = levels[0])) {
|
|
|
|
|
|
- for (i = 0; i < this.colCnt; i++) {
|
|
|
- segsByCol.push([]);
|
|
|
- }
|
|
|
+ for (i = 0; i < level0.length; i++) {
|
|
|
+ computeSlotSegPressures(level0[i]);
|
|
|
+ }
|
|
|
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- segsByCol[segs[i].col].push(segs[i]);
|
|
|
+ for (i = 0; i < level0.length; i++) {
|
|
|
+ this.computeFgSegForwardBack(level0[i], 0, 0);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- return segsByCol;
|
|
|
},
|
|
|
|
|
|
|
|
|
- // Given segments grouped by column, insert the segments' elements into a parallel array of container
|
|
|
- // elements, each living within a column.
|
|
|
- attachSegsByCol: function(segsByCol, containerEls) {
|
|
|
- var col;
|
|
|
- var segs;
|
|
|
+ // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
|
|
|
+ // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
|
|
|
+ // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
|
|
|
+ //
|
|
|
+ // The segment might be part of a "series", which means consecutive segments with the same pressure
|
|
|
+ // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
|
|
|
+ // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
|
|
|
+ // coordinate of the first segment in the series.
|
|
|
+ computeFgSegForwardBack: function(seg, seriesBackwardPressure, seriesBackwardCoord) {
|
|
|
+ var forwardSegs = seg.forwardSegs;
|
|
|
var i;
|
|
|
|
|
|
- for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
|
|
|
- segs = segsByCol[col];
|
|
|
+ if (seg.forwardCoord === undefined) { // not already computed
|
|
|
+
|
|
|
+ if (!forwardSegs.length) {
|
|
|
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- containerEls.eq(col).append(segs[i].el);
|
|
|
+ // if there are no forward segments, this segment should butt up against the edge
|
|
|
+ seg.forwardCoord = 1;
|
|
|
}
|
|
|
- }
|
|
|
- },
|
|
|
+ else {
|
|
|
|
|
|
+ // sort highest pressure first
|
|
|
+ this.sortForwardSegs(forwardSegs);
|
|
|
|
|
|
- // Given the name of a property of `this` object, assumed to be an array of segments,
|
|
|
- // loops through each segment and removes from DOM. Will null-out the property afterwards.
|
|
|
- unrenderNamedSegs: function(propName) {
|
|
|
- var segs = this[propName];
|
|
|
- var i;
|
|
|
+ // this segment's forwardCoord will be calculated from the backwardCoord of the
|
|
|
+ // highest-pressure forward segment.
|
|
|
+ this.computeFgSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
|
|
|
+ seg.forwardCoord = forwardSegs[0].backwardCoord;
|
|
|
+ }
|
|
|
+
|
|
|
+ // calculate the backwardCoord from the forwardCoord. consider the series
|
|
|
+ seg.backwardCoord = seg.forwardCoord -
|
|
|
+ (seg.forwardCoord - seriesBackwardCoord) / // available width for series
|
|
|
+ (seriesBackwardPressure + 1); // # of segments in the series
|
|
|
|
|
|
- if (segs) {
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- segs[i].el.remove();
|
|
|
+ // use this segment's coordinates to computed the coordinates of the less-pressurized
|
|
|
+ // forward segments
|
|
|
+ for (i=0; i<forwardSegs.length; i++) {
|
|
|
+ this.computeFgSegForwardBack(forwardSegs[i], 0, seg.forwardCoord);
|
|
|
}
|
|
|
- this[propName] = null;
|
|
|
}
|
|
|
},
|
|
|
|
|
|
|
|
|
- /* Seg Position Utils
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
+ sortForwardSegs: function(forwardSegs) {
|
|
|
+ forwardSegs.sort(proxy(this, 'compareForwardSegs'));
|
|
|
+ },
|
|
|
|
|
|
|
|
|
- // Refreshes the CSS top/bottom coordinates for each segment element.
|
|
|
- // Works when called after initial render, after a window resize/zoom for example.
|
|
|
- updateSegVerticals: function(segs) {
|
|
|
- this.computeSegVerticals(segs);
|
|
|
- this.assignSegVerticals(segs);
|
|
|
+ // A cmp function for determining which forward segment to rely on more when computing coordinates.
|
|
|
+ compareForwardSegs: function(seg1, seg2) {
|
|
|
+ // put higher-pressure first
|
|
|
+ return seg2.forwardPressure - seg1.forwardPressure ||
|
|
|
+ // put segments that are closer to initial edge first (and favor ones with no coords yet)
|
|
|
+ (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
|
|
|
+ // do normal sorting...
|
|
|
+ this.compareEventSegs(seg1, seg2);
|
|
|
},
|
|
|
|
|
|
|
|
|
- // For each segment in an array, computes and assigns its top and bottom properties
|
|
|
- computeSegVerticals: function(segs) {
|
|
|
+ // Given foreground event segments that have already had their position coordinates computed,
|
|
|
+ // assigns position-related CSS values to their elements.
|
|
|
+ assignFgSegHorizontals: function(segs) {
|
|
|
var i, seg;
|
|
|
- var dayDate;
|
|
|
|
|
|
for (i = 0; i < segs.length; i++) {
|
|
|
seg = segs[i];
|
|
|
- dayDate = this.dayDates[seg.dayIndex];
|
|
|
+ seg.el.css(this.generateFgSegHorizontalCss(seg));
|
|
|
|
|
|
- seg.top = this.computeDateTop(seg.startMs, dayDate);
|
|
|
- seg.bottom = this.computeDateTop(seg.endMs, dayDate);
|
|
|
+ // if the height is short, add a className for alternate styling
|
|
|
+ if (seg.bottom - seg.top < 30) {
|
|
|
+ seg.el.addClass('fc-short');
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
|
|
|
|
|
|
- // Given segments that already have their top/bottom properties computed, applies those values to
|
|
|
- // the segments' elements.
|
|
|
- assignSegVerticals: function(segs) {
|
|
|
- var i, seg;
|
|
|
+ // Generates an object with CSS properties/values that should be applied to an event segment element.
|
|
|
+ // Contains important positioning-related properties that should be applied to any event element, customized or not.
|
|
|
+ generateFgSegHorizontalCss: function(seg) {
|
|
|
+ var shouldOverlap = this.opt('slotEventOverlap');
|
|
|
+ var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point
|
|
|
+ var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point
|
|
|
+ var props = this.timeGrid.generateSegVerticalCss(seg); // get top/bottom first
|
|
|
+ var left; // amount of space from left edge, a fraction of the total width
|
|
|
+ var right; // amount of space from right edge, a fraction of the total width
|
|
|
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- seg = segs[i];
|
|
|
- seg.el.css(this.generateSegVerticalCss(seg));
|
|
|
+ if (shouldOverlap) {
|
|
|
+ // double the width, but don't go beyond the maximum forward coordinate (1.0)
|
|
|
+ forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2);
|
|
|
}
|
|
|
- },
|
|
|
|
|
|
+ if (this.timeGrid.isRTL) {
|
|
|
+ left = 1 - forwardCoord;
|
|
|
+ right = backwardCoord;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ left = backwardCoord;
|
|
|
+ right = 1 - forwardCoord;
|
|
|
+ }
|
|
|
+
|
|
|
+ props.zIndex = seg.level + 1; // convert from 0-base to 1-based
|
|
|
+ props.left = left * 100 + '%';
|
|
|
+ props.right = right * 100 + '%';
|
|
|
+
|
|
|
+ if (shouldOverlap && seg.forwardPressure) {
|
|
|
+ // add padding to the edge so that forward stacked events don't cover the resizer's icon
|
|
|
+ props[this.isRTL ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
|
|
|
+ }
|
|
|
|
|
|
- // Generates an object with CSS properties for the top/bottom coordinates of a segment element
|
|
|
- generateSegVerticalCss: function(seg) {
|
|
|
- return {
|
|
|
- top: seg.top,
|
|
|
- bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container
|
|
|
- };
|
|
|
+ return props;
|
|
|
}
|
|
|
|
|
|
});
|