Bumping old thread in case anyone else finds themselves searching for this.
I had a need for a horizontal timeline showing one time-slot for each month, with events grouped vertically by 1 kind of resource.
The scheduler would crash if a time-slot was set to take up more than 24 hours, but the performance hit of drawing 356 slots per resource was too great. Luckily, I only had to rewrite 1 function (_addContent) to make it work again. I also modified the function creating the slots (_addTimeSlotToCollection) so that it had the variable duration of its given month instead of a constant interval defined by majorTickLength / minorTickCount. (footnote: I'm very impressed with the versatility of the code to support my haphazard changes)
// Refer to kendo.scheduler.timelineview.js to see origial implementation.
var
TimelineYear = kendo.ui.TimelineView.extend({
nextDate() {
return
firstDayOfNextMonth(
this
.startDate());
},
previousDate() {
return
firstDayOfPreviousMonth(
this
.startDate());
},
options: {
selectedDateFormat:
"{0:D} - {1:D}"
},
name:
"TimelineYear"
,
calculateDateRange() {
//create a range of dates to be shown within the view
var
start = firstDayOfThisMonth(
this
.options.date);
var
dates = [];
for
(
var
idx = 0; idx < 12; idx++) {
if
(start.getDate() === 1)
dates.push(start);
start = firstDayOfNextMonth(start);
}
this
._render(dates);
},
// Use our own extension to TimeLineGroupedView where ticks longer than 1 day isn't a problem.
// This means grouping by dates isn't an option.
_getGroupedView() {
return
new
TimeLineGroupedYearView(
this
);
},
// This is the method that adds the internal time-slots. It has been copy-pasted and modified to make the slots end at the last day of the month instead of just spanning 1 tick
_addTimeSlotToCollection:
function
(group, cells, cellIndex, cellOffset, dateIndex, time, interval) {
var
cell = cells[cellIndex + cellOffset];
var
collection = group.getTimeSlotCollection(0);
var
currentDate =
this
._dates[dateIndex];
var
currentTime = Date.UTC(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
var
start = currentTime + time;
// This is the part of this function that I messed with
var
newEnd = lastDayOfThisMonth(
new
Date(start));
var
end = kendo.date.toUtcTime(newEnd);
cell.setAttribute(
'role'
,
'gridcell'
);
cell.setAttribute(
'aria-selected'
,
false
);
collection.addTimeSlot(cell, start, end,
true
);
},
});
// Refer to kendo.scheduler.timelineview.js to see origial implementation.
var
TimeLineGroupedYearView = kendo.ui.scheduler.TimelineGroupedView.extend({
init:
function
(view) {
var
that =
this
;
kendo.ui.scheduler.TimelineGroupedView.fn.init.call(that, view);
},
/*
* Method responsible for rendering the cells of the scheduler view.
* This is what would fail if the tick length exceeded one day.
* The original one was a whole bunch of for-loops, one of which wouldn't be entered if MS_PER_DAY/tick-length rounded to nearest integer was 0.
*
* So yeah, we are ignoring a lot of the options-API, and all the weird ways you can group the dates and events.
* Anything but a horizontal timeline of events, vertically grouped by resources will fail miserably unless this function is modified.
* This version also doesn't add the k-nonwork-hour and k-today classes, because they aren't relevant in a year view. */
_addContent:
function
(dates, columnCount, groupsCount, rowCount, start, end, slotTemplate, isVerticalGrouped) {
var
view =
this
._view;
var
html =
''
;
for
(
var
rowIdx = 0; rowIdx < rowCount; rowIdx++) {
html +=
'<tr>'
;
for
(
var
idx = 0, length = columnCount; idx < length; idx++) {
var
resources =
function
(groupIndex) {
return
function
() {
return
view._resourceBySlot({ groupIndex: groupIndex });
};
};
html +=
'<td>'
;
html += slotTemplate({
date: dates[idx],
resources: resources(rowIdx),
});
html +=
'</td>'
;
}
html +=
'</tr>'
;
}
return
html;
},
})
// Helper functions have been put into the global scope to save time. You can inline them or put them into your own system as you please.
function
firstDayOfNextMonth(date) {
let result =
new
Date(date.getTime());
result.setMonth(result.getMonth() + 1);
result.setDate(1);
return
result;
}
function
firstDayOfThisMonth(date){
let result =
new
Date(date.getTime());
result.setDate(1);
return
result;
}
function
firstDayOfPreviousMonth(date){
let result =
new
Date(date.getTime());
result.setMonth(result.getMonth() - 1);
result.setDate(1);
return
result;
}
/**
* Returns the first day of the month, unless it already is the first, in which case it returns the first day of the previous month.
* Used for navigation in the calendar.
*/
function
firstDayOfThisOrPreviousMonth(date){
if
(date.getDate() === 1)
return
firstDayOfPreviousMonth(date);
else
return
firstDayOfThisMonth(date);
}
function
lastDayOfThisMonth(date) {
return
new
Date(date.getFullYear(), date.getMonth() + 1, 0)
// month starts at 1, so zero should be the last day of the previous month etc
}