/* Grid-based Views: month, basicWeek, basicDay
-----------------------------------------------------------------------------*/
setDefaults({
weekMode: 'fixed'
});
views.month = function(element, options) {
return new Grid(element, options, {
render: function(date, delta) {
if (delta) {
addMonths(date, delta);
date.setDate(1);
}
// start/end
var start = this.start = cloneDate(date, true);
start.setDate(1);
this.end = addMonths(cloneDate(start), 1);
// visStart/visEnd
var visStart = this.visStart = cloneDate(start),
visEnd = this.visEnd = cloneDate(this.end),
nwe = options.weekends ? 0 : 1;
if (nwe) {
skipWeekend(visStart);
skipWeekend(visEnd, -1, true);
}
addDays(visStart, -((visStart.getDay() - Math.max(options.firstDay, nwe) + 7) % 7));
addDays(visEnd, (7 - visEnd.getDay() + Math.max(options.firstDay, nwe)) % 7);
// row count
var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7));
if (options.weekMode == 'fixed') {
addDays(visEnd, (6 - rowCnt) * 7);
rowCnt = 6;
}
// title
this.title = formatDate(
start,
this.option('titleFormat'),
options
);
// render
this.renderGrid(
rowCnt, options.weekends ? 7 : 5,
this.option('columnFormat'),
true
);
}
});
}
views.basicWeek = function(element, options) {
return new Grid(element, options, {
render: function(date, delta) {
if (delta) {
addDays(date, delta * 7);
}
var visStart = this.visStart = cloneDate(
this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7))
),
visEnd = this.visEnd = cloneDate(
this.end = addDays(cloneDate(visStart), 7)
);
if (!options.weekends) {
skipWeekend(visStart);
skipWeekend(visEnd, -1, true);
}
this.title = formatDates(
visStart,
addDays(cloneDate(visEnd), -1),
this.option('titleFormat'),
options
);
this.renderGrid(
1, options.weekends ? 7 : 5,
this.option('columnFormat'),
false
);
}
});
};
views.basicDay = function(element, options) {
return new Grid(element, options, {
render: function(date, delta) {
if (delta) {
addDays(date, delta);
if (!options.weekends) {
skipWeekend(date, delta < 0 ? -1 : 1);
}
}
this.title = formatDate(date, this.option('titleFormat'), options);
this.start = this.visStart = cloneDate(date, true);
this.end = this.visEnd = addDays(cloneDate(this.start), 1);
this.renderGrid(
1, 1,
this.option('columnFormat'),
false
);
}
});
}
// rendering bugs
var tdHeightBug;
function Grid(element, options, methods) {
var tm, firstDay,
nwe, // no weekends (int)
rtl, dis, dit, // day index sign / translate
viewWidth, viewHeight,
rowCnt, colCnt,
colWidth,
thead, tbody,
cachedEvents=[],
segmentContainer,
dayContentPositions = new HorizontalPositionCache(function(dayOfWeek) {
return tbody.find('td:eq(' + ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt) + ') div div')
}),
// ...
// initialize superclass
view = $.extend(this, viewMethods, methods, {
renderGrid: renderGrid,
renderEvents: renderEvents,
rerenderEvents: rerenderEvents,
clearEvents: clearEvents,
setHeight: setHeight,
setWidth: setWidth,
defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing
return cloneDate(event.start);
}
});
view.init(element, options);
/* Grid Rendering
-----------------------------------------------------------------------------*/
element.addClass('fc-grid');
if (element.disableSelection) {
element.disableSelection();
}
function renderGrid(r, c, colFormat, showNumbers) {
rowCnt = r;
colCnt = c;
// update option-derived variables
tm = options.theme ? 'ui' : 'fc';
nwe = options.weekends ? 0 : 1;
firstDay = options.firstDay;
if (rtl = options.isRTL) {
dis = -1;
dit = colCnt - 1;
}else{
dis = 1;
dit = 0;
}
var month = view.start.getMonth(),
today = clearTime(new Date()),
s, i, j, d = cloneDate(view.visStart);
if (!tbody) { // first time, build all cells from scratch
var table = $("
").appendTo(element);
s = "";
for (i=0; i" + formatDate(d, colFormat, options) + "";
addDays(d, 1);
if (nwe) {
skipWeekend(d);
}
}
thead = $(s + "
").appendTo(table);
s = "";
d = cloneDate(view.visStart);
for (i=0; i";
for (j=0; j1 && d.getMonth() != month ? ' fc-other-month' : '') +
(+d == +today ?
' fc-today '+tm+'-state-highlight' :
' fc-not-today') + "'>" +
(showNumbers ? "" + d.getDate() + "
" : '') +
"";
addDays(d, 1);
if (nwe) {
skipWeekend(d);
}
}
s += "";
}
tbody = $(s + "").appendTo(table);
tbody.find('td').click(dayClick);
segmentContainer = $("").appendTo(element);
}else{ // NOT first time, reuse as many cells as possible
clearEvents();
var prevRowCnt = tbody.find('tr').length;
if (rowCnt < prevRowCnt) {
tbody.find('tr:gt(' + (rowCnt-1) + ')').remove(); // remove extra rows
}
else if (rowCnt > prevRowCnt) { // needs to create new rows...
s = '';
for (i=prevRowCnt; i";
for (j=0; j" +
(showNumbers ? "" : '') +
"" +
"";
addDays(d, 1);
if (nwe) {
skipWeekend(d);
}
}
s += "";
}
tbody.append(s);
}
tbody.find('td.fc-new').removeClass('fc-new').click(dayClick);
// re-label and re-class existing cells
d = cloneDate(view.visStart);
tbody.find('td').each(function() {
var td = $(this);
if (rowCnt > 1) {
if (d.getMonth() == month) {
td.removeClass('fc-other-month');
}else{
td.addClass('fc-other-month');
}
}
if (+d == +today) {
td.removeClass('fc-not-today')
.addClass('fc-today')
.addClass(tm + '-state-highlight');
}else{
td.addClass('fc-not-today')
.removeClass('fc-today')
.removeClass(tm + '-state-highlight');
}
td.find('div.fc-day-number').text(d.getDate());
addDays(d, 1);
if (nwe) {
skipWeekend(d);
}
});
if (rowCnt == 1) { // more changes likely (week or day view)
// redo column header text and class
d = cloneDate(view.visStart);
thead.find('th').each(function() {
$(this).text(formatDate(d, colFormat, options));
this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
addDays(d, 1);
if (nwe) {
skipWeekend(d);
}
});
// redo cell day-of-weeks
d = cloneDate(view.visStart);
tbody.find('td').each(function() {
this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
addDays(d, 1);
if (nwe) {
skipWeekend(d);
}
});
}
}
};
function dayClick(ev) {
var n = parseInt(this.className.match(/fc\-day(\d+)/)[1]),
date = addDays(
cloneDate(view.visStart),
Math.floor(n/colCnt) * 7 + n % colCnt
);
view.trigger('dayClick', this, date, true, ev);
}
function setHeight(height) {
viewHeight = height;
var leftTDs = tbody.find('tr td:first-child'),
tbodyHeight = viewHeight - thead.height(),
rowHeight1, rowHeight2;
if (options.weekMode == 'variable') {
rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt==1 ? 2 : 6));
}else{
rowHeight1 = Math.floor(tbodyHeight / rowCnt);
rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1);
}
if (tdHeightBug == undefined) {
// bug in firefox where cell height includes padding
var tr = tbody.find('tr:first'),
td = tr.find('td:first');
td.height(rowHeight1);
tdHeightBug = rowHeight1 != td.height();
}
if (tdHeightBug) {
leftTDs.slice(0, -1).height(rowHeight1);
leftTDs.slice(-1).height(rowHeight2);
}else{
setOuterHeight(leftTDs.slice(0, -1), rowHeight1);
setOuterHeight(leftTDs.slice(-1), rowHeight2);
}
}
function setWidth(width) {
viewWidth = width;
dayContentPositions.clear();
setOuterWidth(
thead.find('th').slice(0, -1),
colWidth = Math.floor(viewWidth / colCnt)
);
}
/* Event Rendering
-----------------------------------------------------------------------------*/
function renderEvents(events) {
view.reportEvents(cachedEvents = events);
renderSegs(compileSegs(events));
}
function rerenderEvents(modifiedEventId) {
clearEvents();
renderSegs(compileSegs(cachedEvents), modifiedEventId);
}
function clearEvents() {
view._clearEvents(); // only clears the hashes
segmentContainer.empty();
}
function compileSegs(events) {
var d1 = cloneDate(view.visStart),
d2 = addDays(cloneDate(d1), colCnt),
visEventsEnds = $.map(events, visEventEnd),
i, row,
j, level,
k, seg,
segs=[];
for (i=0; i" +
"" +
(!event.allDay && seg.isStart ?
"" +
htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'), options)) +
""
:'') +
"" + htmlEscape(event.title) + "" +
"" +
((event.editable || event.editable == undefined && options.editable) && !options.disableResizing && $.fn.resizable ?
""
: '') +
"";
seg.left = left;
seg.outerWidth = right - left;
}
segmentContainer[0].innerHTML = html; // faster than html()
eventElements = segmentContainer.children();
// retrieve elements, run through eventRender callback, bind handlers
for (i=0; i div') // optimal selector?
.height(top + levelHeight);
}
// calculate row tops
for (rowI=0; rowI