/* An abstract class for the "basic" views, as well as month view. Renders one or more rows of day cells.
----------------------------------------------------------------------------------------------------------------------*/
// It is a manager for a DayGrid subcomponent, which does most of the heavy lifting.
// It is responsible for managing width/height.
var BasicView = FC.BasicView = View.extend({
scroller: null,
dayGridClass: DayGrid, // class the dayGrid will be instantiated from (overridable by subclasses)
dayGrid: null, // the main subcomponent that does most of the heavy lifting
dayNumbersVisible: false, // display day numbers on each day cell?
colWeekNumbersVisible: false, // display week numbers along the side?
cellWeekNumbersVisible: false, // display week numbers in day cell?
weekNumberWidth: null, // width of all the week-number cells running down the side
headContainerEl: null, // div that hold's the dayGrid's rendered date header
headRowEl: null, // the fake row element of the day-of-week header
constructor: function() {
View.apply(this, arguments);
this.dayGrid = this.instantiateDayGrid();
this.addChild(this.dayGrid);
this.scroller = new Scroller({
overflowX: 'hidden',
overflowY: 'auto'
});
},
// Generates the DayGrid object this view needs. Draws from this.dayGridClass
instantiateDayGrid: function() {
// generate a subclass on the fly with BasicView-specific behavior
// TODO: cache this subclass
var subclass = this.dayGridClass.extend(basicDayGridMethods);
return new subclass(this);
},
// Computes the date range that will be rendered.
buildRenderRange: function(currentUnzonedRange, currentRangeUnit, isRangeAllDay) {
var renderUnzonedRange = View.prototype.buildRenderRange.apply(this, arguments); // an UnzonedRange
var start = this.calendar.msToUtcMoment(renderUnzonedRange.startMs, isRangeAllDay);
var end = this.calendar.msToUtcMoment(renderUnzonedRange.endMs, isRangeAllDay);
// year and month views should be aligned with weeks. this is already done for week
if (/^(year|month)$/.test(currentRangeUnit)) {
start.startOf('week');
// make end-of-week if not already
if (end.weekday()) {
end.add(1, 'week').startOf('week'); // exclusively move backwards
}
}
return this.trimHiddenDays(new UnzonedRange(start, end));
},
handleDateProfileSet: function(dateProfile) {
this.dayGrid.breakOnWeeks = /year|month|week/.test(dateProfile.currentRangeUnit);
// needs breakOnWeeks. will populate dayGrid.rowCnt :(
View.prototype.handleDateProfileSet.apply(this, arguments);
this.dayNumbersVisible = this.dayGrid.rowCnt > 1; // TODO: make grid responsible
if (this.opt('weekNumbers')) {
if (this.opt('weekNumbersWithinDays')) {
this.cellWeekNumbersVisible = true;
this.colWeekNumbersVisible = false;
}
else {
this.cellWeekNumbersVisible = false;
this.colWeekNumbersVisible = true;
};
}
this.dayGrid.numbersVisible =
this.dayNumbersVisible ||
this.cellWeekNumbersVisible ||
this.colWeekNumbersVisible;
this.dayGrid.isRigid = this.hasRigidRows();
},
renderSkeleton: function() {
var dayGridContainerEl;
var dayGridEl;
this.el.addClass('fc-basic-view').html(this.renderSkeletonHtml());
this.scroller.render();
dayGridContainerEl = this.scroller.el.addClass('fc-day-grid-container');
dayGridEl = $('
').appendTo(dayGridContainerEl);
this.el.find('.fc-body > tr > td').append(dayGridContainerEl);
this.dayGrid.setElement(dayGridEl);
},
unrenderSkeleton: function() {
this.dayGrid.removeElement();
this.scroller.destroy();
},
// Renders the view into `this.el`, which should already be assigned
renderDates: function(dateProfile) {
this.renderHead();
},
// render the day-of-week headers
renderHead: function() {
this.headContainerEl =
this.el.find('.fc-head-container')
.html(this.dayGrid.renderHeadHtml());
this.headRowEl = this.headContainerEl.find('.fc-row');
},
// Builds the HTML skeleton for the view.
// The day-grid component will render inside of a container defined by this HTML.
renderSkeletonHtml: function() {
var theme = this.calendar.theme;
return '' +
'' +
'' +
'' +
'' +
'
' +
'' +
'' +
'' +
' | ' +
'
' +
'' +
'
';
},
// Generates an HTML attribute string for setting the width of the week number column, if it is known
weekNumberStyleAttr: function() {
if (this.weekNumberWidth !== null) {
return 'style="width:' + this.weekNumberWidth + 'px"';
}
return '';
},
// Determines whether each row should have a constant height
hasRigidRows: function() {
var eventLimit = this.opt('eventLimit');
return eventLimit && typeof eventLimit !== 'number';
},
/* Dimensions
------------------------------------------------------------------------------------------------------------------*/
// Refreshes the horizontal dimensions of the view
updateSize: function(totalHeight, isAuto, isResize) {
View.prototype.updateSize.apply(this, arguments);
if (this.colWeekNumbersVisible) {
// Make sure all week number cells running down the side have the same width.
// Record the width for cells created later.
this.weekNumberWidth = matchCellWidths(
this.el.find('.fc-week-number')
);
}
var eventLimit = this.opt('eventLimit');
var scrollerHeight;
var scrollbarWidths;
// reset all heights to be natural
this.scroller.clear();
uncompensateScroll(this.headRowEl);
this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
// is the event limit a constant level number?
if (eventLimit && typeof eventLimit === 'number') {
this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
}
// distribute the height to the rows
// (totalHeight is a "recommended" value if isAuto)
scrollerHeight = this.computeScrollerHeight(totalHeight);
this.setGridHeight(scrollerHeight, isAuto);
// is the event limit dynamically calculated?
if (eventLimit && typeof eventLimit !== 'number') {
this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
}
if (!isAuto) { // should we force dimensions of the scroll container?
this.scroller.setHeight(scrollerHeight);
scrollbarWidths = this.scroller.getScrollbarWidths();
if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
compensateScroll(this.headRowEl, scrollbarWidths);
// doing the scrollbar compensation might have created text overflow which created more height. redo
scrollerHeight = this.computeScrollerHeight(totalHeight);
this.scroller.setHeight(scrollerHeight);
}
// guarantees the same scrollbar widths
this.scroller.lockOverflow(scrollbarWidths);
}
},
// given a desired total height of the view, returns what the height of the scroller should be
computeScrollerHeight: function(totalHeight) {
return totalHeight -
subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
},
// Sets the height of just the DayGrid component in this view
setGridHeight: function(height, isAuto) {
if (isAuto) {
undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding
}
else {
distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows
}
},
/* Scroll
------------------------------------------------------------------------------------------------------------------*/
computeInitialDateScroll: function() {
return { top: 0 };
},
queryDateScroll: function() {
return { top: this.scroller.getScrollTop() };
},
applyDateScroll: function(scroll) {
if (scroll.top !== undefined) {
this.scroller.setScrollTop(scroll.top);
}
}
});
// Methods that will customize the rendering behavior of the BasicView's dayGrid
var basicDayGridMethods = {
// Generates the HTML that will go before the day-of week header cells
renderHeadIntroHtml: function() {
var view = this.view;
if (view.colWeekNumbersVisible) {
return '' +
'';
}
return '';
},
// Generates the HTML that will go before content-skeleton cells that display the day/week numbers
renderNumberIntroHtml: function(row) {
var view = this.view;
var weekStart = this.getCellDate(row, 0);
if (view.colWeekNumbersVisible) {
return '' +
'' +
view.buildGotoAnchorHtml( // aside from link, important for matchCellWidths
{ date: weekStart, type: 'week', forceOff: this.colCnt === 1 },
weekStart.format('w') // inner HTML
) +
' | ';
}
return '';
},
// Generates the HTML that goes before the day bg cells for each day-row
renderBgIntroHtml: function() {
var view = this.view;
if (view.colWeekNumbersVisible) {
return ' | ';
}
return '';
},
// Generates the HTML that goes before every other type of row generated by DayGrid.
// Affects helper-skeleton and highlight-skeleton rows.
renderIntroHtml: function() {
var view = this.view;
if (view.colWeekNumbersVisible) {
return ' | ';
}
return '';
}
};