|
|
@@ -56,11 +56,132 @@ var TimeGrid = FC.TimeGrid = ChronoComponent.extend(CoordChronoComponentMixin, S
|
|
|
},
|
|
|
|
|
|
|
|
|
+ // Slices up the given span (unzoned start/end with other misc data) into an array of segments
|
|
|
+ componentFootprintToSegs: function(componentFootprint) {
|
|
|
+ var segs = this.sliceRangeByTimes(componentFootprint.unzonedRange);
|
|
|
+ var i;
|
|
|
+
|
|
|
+ for (i = 0; i < segs.length; i++) {
|
|
|
+ if (this.isRTL) {
|
|
|
+ segs[i].col = this.daysPerRow - 1 - segs[i].dayIndex;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ segs[i].col = segs[i].dayIndex;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return segs;
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ /* Date Handling
|
|
|
+ ------------------------------------------------------------------------------------------------------------------*/
|
|
|
+
|
|
|
+
|
|
|
+ rangeUpdated: function() {
|
|
|
+ var view = this.view;
|
|
|
+
|
|
|
+ this.updateDayTable();
|
|
|
+
|
|
|
+ // needs to go after updateDayTable because computeEventTimeFormat/computeDisplayEventEnd depends on colCnt.
|
|
|
+ // TODO: easy to forget. use listener.
|
|
|
+ this.eventRenderUtils.rangeUpdated();
|
|
|
+
|
|
|
+ this.dayRanges = this.dayDates.map(function(dayDate) {
|
|
|
+ return new UnzonedRange(
|
|
|
+ dayDate.clone().add(view.minTime),
|
|
|
+ dayDate.clone().add(view.maxTime)
|
|
|
+ );
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ sliceRangeByTimes: function(unzonedRange) {
|
|
|
+ var segs = [];
|
|
|
+ var segRange;
|
|
|
+ var dayIndex;
|
|
|
+
|
|
|
+ for (dayIndex = 0; dayIndex < this.daysPerRow; dayIndex++) {
|
|
|
+
|
|
|
+ segRange = unzonedRange.intersect(this.dayRanges[dayIndex]);
|
|
|
+
|
|
|
+ if (segRange) {
|
|
|
+ segs.push({
|
|
|
+ startMs: segRange.startMs,
|
|
|
+ endMs: segRange.endMs,
|
|
|
+ isStart: segRange.isStart,
|
|
|
+ isEnd: segRange.isEnd,
|
|
|
+ dayIndex: dayIndex
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return segs;
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ /* Options
|
|
|
+ ------------------------------------------------------------------------------------------------------------------*/
|
|
|
+
|
|
|
+
|
|
|
opt: function(name) {
|
|
|
return this.view.opt(name);
|
|
|
},
|
|
|
|
|
|
|
|
|
+ // Parses various options into properties of this object
|
|
|
+ processOptions: function() {
|
|
|
+ var slotDuration = this.opt('slotDuration');
|
|
|
+ var snapDuration = this.opt('snapDuration');
|
|
|
+ var input;
|
|
|
+
|
|
|
+ slotDuration = moment.duration(slotDuration);
|
|
|
+ snapDuration = snapDuration ? moment.duration(snapDuration) : slotDuration;
|
|
|
+
|
|
|
+ this.slotDuration = slotDuration;
|
|
|
+ this.snapDuration = snapDuration;
|
|
|
+ this.snapsPerSlot = slotDuration / snapDuration; // TODO: ensure an integer multiple?
|
|
|
+
|
|
|
+ // might be an array value (for TimelineView).
|
|
|
+ // if so, getting the most granular entry (the last one probably).
|
|
|
+ input = this.opt('slotLabelFormat');
|
|
|
+ if ($.isArray(input)) {
|
|
|
+ input = input[input.length - 1];
|
|
|
+ }
|
|
|
+
|
|
|
+ this.labelFormat = input ||
|
|
|
+ this.opt('smallTimeFormat'); // the computed default
|
|
|
+
|
|
|
+ input = this.opt('slotLabelInterval');
|
|
|
+ this.labelInterval = input ?
|
|
|
+ moment.duration(input) :
|
|
|
+ this.computeLabelInterval(slotDuration);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Computes an automatic value for slotLabelInterval
|
|
|
+ computeLabelInterval: function(slotDuration) {
|
|
|
+ var i;
|
|
|
+ var labelInterval;
|
|
|
+ var slotsPerLabel;
|
|
|
+
|
|
|
+ // find the smallest stock label interval that results in more than one slots-per-label
|
|
|
+ for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) {
|
|
|
+ labelInterval = moment.duration(AGENDA_STOCK_SUB_DURATIONS[i]);
|
|
|
+ slotsPerLabel = divideDurationByDuration(labelInterval, slotDuration);
|
|
|
+ if (isInt(slotsPerLabel) && slotsPerLabel > 1) {
|
|
|
+ return labelInterval;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return moment.duration(slotDuration); // fall back. clone
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ /* Date Rendering
|
|
|
+ ------------------------------------------------------------------------------------------------------------------*/
|
|
|
+
|
|
|
+
|
|
|
// Renders the time grid into `this.el`, which should already be assigned.
|
|
|
// Relies on the view's colCnt. In the future, this component should probably be self-sufficient.
|
|
|
renderDates: function() {
|
|
|
@@ -145,194 +266,143 @@ var TimeGrid = FC.TimeGrid = ChronoComponent.extend(CoordChronoComponentMixin, S
|
|
|
},
|
|
|
|
|
|
|
|
|
- /* Options
|
|
|
+ /* Content Skeleton
|
|
|
------------------------------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
- // Parses various options into properties of this object
|
|
|
- processOptions: function() {
|
|
|
- var slotDuration = this.opt('slotDuration');
|
|
|
- var snapDuration = this.opt('snapDuration');
|
|
|
- var input;
|
|
|
-
|
|
|
- slotDuration = moment.duration(slotDuration);
|
|
|
- snapDuration = snapDuration ? moment.duration(snapDuration) : slotDuration;
|
|
|
-
|
|
|
- this.slotDuration = slotDuration;
|
|
|
- this.snapDuration = snapDuration;
|
|
|
- this.snapsPerSlot = slotDuration / snapDuration; // TODO: ensure an integer multiple?
|
|
|
+ // Renders the DOM that the view's content will live in
|
|
|
+ renderContentSkeleton: function() {
|
|
|
+ var cellHtml = '';
|
|
|
+ var i;
|
|
|
+ var skeletonEl;
|
|
|
|
|
|
- // might be an array value (for TimelineView).
|
|
|
- // if so, getting the most granular entry (the last one probably).
|
|
|
- input = this.opt('slotLabelFormat');
|
|
|
- if ($.isArray(input)) {
|
|
|
- input = input[input.length - 1];
|
|
|
+ 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>';
|
|
|
}
|
|
|
|
|
|
- this.labelFormat = input ||
|
|
|
- this.opt('smallTimeFormat'); // the computed default
|
|
|
+ skeletonEl = $(
|
|
|
+ '<div class="fc-content-skeleton">' +
|
|
|
+ '<table>' +
|
|
|
+ '<tr>' + cellHtml + '</tr>' +
|
|
|
+ '</table>' +
|
|
|
+ '</div>'
|
|
|
+ );
|
|
|
|
|
|
- input = this.opt('slotLabelInterval');
|
|
|
- this.labelInterval = input ?
|
|
|
- moment.duration(input) :
|
|
|
- this.computeLabelInterval(slotDuration);
|
|
|
+ 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);
|
|
|
},
|
|
|
|
|
|
|
|
|
- // Computes an automatic value for slotLabelInterval
|
|
|
- computeLabelInterval: function(slotDuration) {
|
|
|
+ // 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;
|
|
|
- var labelInterval;
|
|
|
- var slotsPerLabel;
|
|
|
|
|
|
- // find the smallest stock label interval that results in more than one slots-per-label
|
|
|
- for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) {
|
|
|
- labelInterval = moment.duration(AGENDA_STOCK_SUB_DURATIONS[i]);
|
|
|
- slotsPerLabel = divideDurationByDuration(labelInterval, slotDuration);
|
|
|
- if (isInt(slotsPerLabel) && slotsPerLabel > 1) {
|
|
|
- return labelInterval;
|
|
|
- }
|
|
|
+ for (i = 0; i < this.colCnt; i++) {
|
|
|
+ segsByCol.push([]);
|
|
|
}
|
|
|
|
|
|
- return moment.duration(slotDuration); // fall back. clone
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Hit System
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- prepareHits: function() {
|
|
|
- this.colCoordCache.build();
|
|
|
- this.slatCoordCache.build();
|
|
|
- },
|
|
|
-
|
|
|
+ for (i = 0; i < segs.length; i++) {
|
|
|
+ segsByCol[segs[i].col].push(segs[i]);
|
|
|
+ }
|
|
|
|
|
|
- releaseHits: function() {
|
|
|
- this.colCoordCache.clear();
|
|
|
- // NOTE: don't clear slatCoordCache because we rely on it for computeTimeTop
|
|
|
+ return segsByCol;
|
|
|
},
|
|
|
|
|
|
|
|
|
- queryHit: function(leftOffset, topOffset) {
|
|
|
- var snapsPerSlot = this.snapsPerSlot;
|
|
|
- var colCoordCache = this.colCoordCache;
|
|
|
- var slatCoordCache = this.slatCoordCache;
|
|
|
-
|
|
|
- if (colCoordCache.isLeftInBounds(leftOffset) && slatCoordCache.isTopInBounds(topOffset)) {
|
|
|
- var colIndex = colCoordCache.getHorizontalIndex(leftOffset);
|
|
|
- var slatIndex = slatCoordCache.getVerticalIndex(topOffset);
|
|
|
+ // 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;
|
|
|
+ var i;
|
|
|
|
|
|
- if (colIndex != null && slatIndex != null) {
|
|
|
- var slatTop = slatCoordCache.getTopOffset(slatIndex);
|
|
|
- var slatHeight = slatCoordCache.getHeight(slatIndex);
|
|
|
- var partial = (topOffset - slatTop) / slatHeight; // floating point number between 0 and 1
|
|
|
- var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
|
|
|
- var snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
|
|
|
- var snapTop = slatTop + (localSnapIndex / snapsPerSlot) * slatHeight;
|
|
|
- var snapBottom = slatTop + ((localSnapIndex + 1) / snapsPerSlot) * slatHeight;
|
|
|
+ for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
|
|
|
+ segs = segsByCol[col];
|
|
|
|
|
|
- return {
|
|
|
- col: colIndex,
|
|
|
- snap: snapIndex,
|
|
|
- component: this, // needed unfortunately :(
|
|
|
- left: colCoordCache.getLeftOffset(colIndex),
|
|
|
- right: colCoordCache.getRightOffset(colIndex),
|
|
|
- top: snapTop,
|
|
|
- bottom: snapBottom
|
|
|
- };
|
|
|
+ for (i = 0; i < segs.length; i++) {
|
|
|
+ containerEls.eq(col).append(segs[i].el);
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
|
|
|
- getHitFootprint: function(hit) {
|
|
|
- var start = this.getCellDate(0, hit.col); // row=0
|
|
|
- var time = this.computeSnapTime(hit.snap); // pass in the snap-index
|
|
|
- var end;
|
|
|
-
|
|
|
- start.time(time);
|
|
|
- end = start.clone().add(this.snapDuration);
|
|
|
-
|
|
|
- return new ComponentFootprint(
|
|
|
- new UnzonedRange(start, end),
|
|
|
- false // all-day?
|
|
|
- );
|
|
|
- },
|
|
|
-
|
|
|
+ // 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;
|
|
|
|
|
|
- getHitEl: function(hit) {
|
|
|
- return this.colEls.eq(hit.col);
|
|
|
+ if (segs) {
|
|
|
+ for (i = 0; i < segs.length; i++) {
|
|
|
+ segs[i].el.remove();
|
|
|
+ }
|
|
|
+ this[propName] = null;
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
|
|
|
- /* Dates
|
|
|
+ /* Now Indicator
|
|
|
------------------------------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
- rangeUpdated: function() {
|
|
|
- var view = this.view;
|
|
|
-
|
|
|
- this.updateDayTable();
|
|
|
-
|
|
|
- // needs to go after updateDayTable because computeEventTimeFormat/computeDisplayEventEnd depends on colCnt.
|
|
|
- // TODO: easy to forget. use listener.
|
|
|
- this.eventRenderUtils.rangeUpdated();
|
|
|
-
|
|
|
- this.dayRanges = this.dayDates.map(function(dayDate) {
|
|
|
- return new UnzonedRange(
|
|
|
- dayDate.clone().add(view.minTime),
|
|
|
- dayDate.clone().add(view.maxTime)
|
|
|
- );
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // Given a row number of the grid, representing a "snap", returns a time (Duration) from its start-of-day
|
|
|
- computeSnapTime: function(snapIndex) {
|
|
|
- return moment.duration(this.view.minTime + this.snapDuration * snapIndex);
|
|
|
+ getNowIndicatorUnit: function() {
|
|
|
+ return 'minute'; // will refresh on the minute
|
|
|
},
|
|
|
|
|
|
|
|
|
- // Slices up the given span (unzoned start/end with other misc data) into an array of segments
|
|
|
- componentFootprintToSegs: function(componentFootprint) {
|
|
|
- var segs = this.sliceRangeByTimes(componentFootprint.unzonedRange);
|
|
|
+ renderNowIndicator: function(date) {
|
|
|
+ // seg system might be overkill, but it handles scenario where line needs to be rendered
|
|
|
+ // more than once because of columns with the same date (resources columns for example)
|
|
|
+ var segs = this.componentFootprintToSegs(
|
|
|
+ new ComponentFootprint(
|
|
|
+ new UnzonedRange(date, date.valueOf() + 1), // protect against null range
|
|
|
+ false // all-day
|
|
|
+ )
|
|
|
+ );
|
|
|
+ var top = this.computeDateTop(date, date);
|
|
|
+ var nodes = [];
|
|
|
var i;
|
|
|
|
|
|
+ // render lines within the columns
|
|
|
for (i = 0; i < segs.length; i++) {
|
|
|
- if (this.isRTL) {
|
|
|
- segs[i].col = this.daysPerRow - 1 - segs[i].dayIndex;
|
|
|
- }
|
|
|
- else {
|
|
|
- segs[i].col = segs[i].dayIndex;
|
|
|
- }
|
|
|
+ nodes.push($('<div class="fc-now-indicator fc-now-indicator-line"></div>')
|
|
|
+ .css('top', top)
|
|
|
+ .appendTo(this.colContainerEls.eq(segs[i].col))[0]);
|
|
|
}
|
|
|
|
|
|
- return segs;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- sliceRangeByTimes: function(unzonedRange) {
|
|
|
- var segs = [];
|
|
|
- var segRange;
|
|
|
- var dayIndex;
|
|
|
-
|
|
|
- for (dayIndex = 0; dayIndex < this.daysPerRow; dayIndex++) {
|
|
|
-
|
|
|
- segRange = unzonedRange.intersect(this.dayRanges[dayIndex]);
|
|
|
-
|
|
|
- if (segRange) {
|
|
|
- segs.push({
|
|
|
- startMs: segRange.startMs,
|
|
|
- endMs: segRange.endMs,
|
|
|
- isStart: segRange.isStart,
|
|
|
- isEnd: segRange.isEnd,
|
|
|
- dayIndex: dayIndex
|
|
|
- });
|
|
|
- }
|
|
|
+ // render an arrow over the axis
|
|
|
+ if (segs.length > 0) { // is the current time in view?
|
|
|
+ nodes.push($('<div class="fc-now-indicator fc-now-indicator-arrow"></div>')
|
|
|
+ .css('top', top)
|
|
|
+ .appendTo(this.el.find('.fc-content-skeleton'))[0]);
|
|
|
}
|
|
|
|
|
|
- return segs;
|
|
|
+ this.nowIndicatorEls = $(nodes);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ unrenderNowIndicator: function() {
|
|
|
+ if (this.nowIndicatorEls) {
|
|
|
+ this.nowIndicatorEls.remove();
|
|
|
+ this.nowIndicatorEls = null;
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
|
|
|
@@ -439,6 +509,80 @@ var TimeGrid = FC.TimeGrid = ChronoComponent.extend(CoordChronoComponentMixin, S
|
|
|
},
|
|
|
|
|
|
|
|
|
+ /* Hit System
|
|
|
+ ------------------------------------------------------------------------------------------------------------------*/
|
|
|
+
|
|
|
+
|
|
|
+ prepareHits: function() {
|
|
|
+ this.colCoordCache.build();
|
|
|
+ this.slatCoordCache.build();
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ releaseHits: function() {
|
|
|
+ this.colCoordCache.clear();
|
|
|
+ // NOTE: don't clear slatCoordCache because we rely on it for computeTimeTop
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ queryHit: function(leftOffset, topOffset) {
|
|
|
+ var snapsPerSlot = this.snapsPerSlot;
|
|
|
+ var colCoordCache = this.colCoordCache;
|
|
|
+ var slatCoordCache = this.slatCoordCache;
|
|
|
+
|
|
|
+ if (colCoordCache.isLeftInBounds(leftOffset) && slatCoordCache.isTopInBounds(topOffset)) {
|
|
|
+ var colIndex = colCoordCache.getHorizontalIndex(leftOffset);
|
|
|
+ var slatIndex = slatCoordCache.getVerticalIndex(topOffset);
|
|
|
+
|
|
|
+ if (colIndex != null && slatIndex != null) {
|
|
|
+ var slatTop = slatCoordCache.getTopOffset(slatIndex);
|
|
|
+ var slatHeight = slatCoordCache.getHeight(slatIndex);
|
|
|
+ var partial = (topOffset - slatTop) / slatHeight; // floating point number between 0 and 1
|
|
|
+ var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
|
|
|
+ var snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
|
|
|
+ var snapTop = slatTop + (localSnapIndex / snapsPerSlot) * slatHeight;
|
|
|
+ var snapBottom = slatTop + ((localSnapIndex + 1) / snapsPerSlot) * slatHeight;
|
|
|
+
|
|
|
+ return {
|
|
|
+ col: colIndex,
|
|
|
+ snap: snapIndex,
|
|
|
+ component: this, // needed unfortunately :(
|
|
|
+ left: colCoordCache.getLeftOffset(colIndex),
|
|
|
+ right: colCoordCache.getRightOffset(colIndex),
|
|
|
+ top: snapTop,
|
|
|
+ bottom: snapBottom
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ getHitFootprint: function(hit) {
|
|
|
+ var start = this.getCellDate(0, hit.col); // row=0
|
|
|
+ var time = this.computeSnapTime(hit.snap); // pass in the snap-index
|
|
|
+ var end;
|
|
|
+
|
|
|
+ start.time(time);
|
|
|
+ end = start.clone().add(this.snapDuration);
|
|
|
+
|
|
|
+ return new ComponentFootprint(
|
|
|
+ new UnzonedRange(start, end),
|
|
|
+ false // all-day?
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Given a row number of the grid, representing a "snap", returns a time (Duration) from its start-of-day
|
|
|
+ computeSnapTime: function(snapIndex) {
|
|
|
+ return moment.duration(this.view.minTime + this.snapDuration * snapIndex);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ getHitEl: function(hit) {
|
|
|
+ return this.colEls.eq(hit.col);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
/* Event Rendering
|
|
|
------------------------------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
@@ -468,7 +612,6 @@ var TimeGrid = FC.TimeGrid = ChronoComponent.extend(CoordChronoComponentMixin, S
|
|
|
},
|
|
|
|
|
|
|
|
|
-
|
|
|
/* Event Drag Visualization
|
|
|
------------------------------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
@@ -599,163 +742,6 @@ var TimeGrid = FC.TimeGrid = ChronoComponent.extend(CoordChronoComponentMixin, S
|
|
|
},
|
|
|
|
|
|
|
|
|
- /* Highlight
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- renderHighlightSegs: function(segs) {
|
|
|
- segs = this.fillSystem.buildSegEls('highlight', segs);
|
|
|
- this.updateSegVerticals(segs);
|
|
|
- this.attachSegsByCol(this.groupSegsByCol(segs), this.highlightContainerEls);
|
|
|
- this.highlightSegs = segs;
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- unrenderHighlightSegs: function() {
|
|
|
- this.unrenderNamedSegs('highlightSegs');
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Seg Rendering Utils
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- // 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);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // 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;
|
|
|
-
|
|
|
- for (i = 0; i < this.colCnt; i++) {
|
|
|
- segsByCol.push([]);
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- segsByCol[segs[i].col].push(segs[i]);
|
|
|
- }
|
|
|
-
|
|
|
- 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;
|
|
|
- var i;
|
|
|
-
|
|
|
- for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
|
|
|
- segs = segsByCol[col];
|
|
|
-
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- containerEls.eq(col).append(segs[i].el);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- // 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;
|
|
|
-
|
|
|
- if (segs) {
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- segs[i].el.remove();
|
|
|
- }
|
|
|
- this[propName] = null;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- /* Now Indicator
|
|
|
- ------------------------------------------------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
- getNowIndicatorUnit: function() {
|
|
|
- return 'minute'; // will refresh on the minute
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- renderNowIndicator: function(date) {
|
|
|
- // seg system might be overkill, but it handles scenario where line needs to be rendered
|
|
|
- // more than once because of columns with the same date (resources columns for example)
|
|
|
- var segs = this.componentFootprintToSegs(
|
|
|
- new ComponentFootprint(
|
|
|
- new UnzonedRange(date, date.valueOf() + 1), // protect against null range
|
|
|
- false // all-day
|
|
|
- )
|
|
|
- );
|
|
|
- var top = this.computeDateTop(date, date);
|
|
|
- var nodes = [];
|
|
|
- var i;
|
|
|
-
|
|
|
- // render lines within the columns
|
|
|
- for (i = 0; i < segs.length; i++) {
|
|
|
- nodes.push($('<div class="fc-now-indicator fc-now-indicator-line"></div>')
|
|
|
- .css('top', top)
|
|
|
- .appendTo(this.colContainerEls.eq(segs[i].col))[0]);
|
|
|
- }
|
|
|
-
|
|
|
- // render an arrow over the axis
|
|
|
- if (segs.length > 0) { // is the current time in view?
|
|
|
- nodes.push($('<div class="fc-now-indicator fc-now-indicator-arrow"></div>')
|
|
|
- .css('top', top)
|
|
|
- .appendTo(this.el.find('.fc-content-skeleton'))[0]);
|
|
|
- }
|
|
|
-
|
|
|
- this.nowIndicatorEls = $(nodes);
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
- unrenderNowIndicator: function() {
|
|
|
- if (this.nowIndicatorEls) {
|
|
|
- this.nowIndicatorEls.remove();
|
|
|
- this.nowIndicatorEls = null;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
/* Selection
|
|
|
------------------------------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
@@ -793,6 +779,19 @@ var TimeGrid = FC.TimeGrid = ChronoComponent.extend(CoordChronoComponentMixin, S
|
|
|
|
|
|
unrenderHighlight: function() {
|
|
|
this.unrenderHighlightSegs();
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ renderHighlightSegs: function(segs) {
|
|
|
+ segs = this.fillSystem.buildSegEls('highlight', segs);
|
|
|
+ this.updateSegVerticals(segs);
|
|
|
+ this.attachSegsByCol(this.groupSegsByCol(segs), this.highlightContainerEls);
|
|
|
+ this.highlightSegs = segs;
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ unrenderHighlightSegs: function() {
|
|
|
+ this.unrenderNamedSegs('highlightSegs');
|
|
|
}
|
|
|
|
|
|
});
|