@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,533 lines • 79 kB
JavaScript
//#region ../src/kendo.scheduler.view.js
const __meta__ = {
id: "scheduler.view",
name: "Scheduler View",
category: "web",
description: "The Scheduler Common View",
depends: ["core", "toolbar"],
hidden: true
};
kendo.ui.scheduler = {};
(function($) {
var kendo = window.kendo, ui = kendo.ui, getDate = kendo.date.getDate, Widget = ui.Widget, outerHeight = kendo._outerHeight, keys = kendo.keys, NS = ".kendoSchedulerView", INVERSE_COLOR_CLASS = "k-event-inverse", ONGOING_CLASS = "k-event-ongoing", MIN_HORIZONTAL_SCROLL_SIZE = 1024, math = Math, SPACE = " ", DOT = ".";
function levels(values, key) {
var result = [];
function collect(depth, values) {
values = values[key];
if (values) {
var level = result[depth] = result[depth] || [];
for (var idx = 0; idx < values.length; idx++) {
level.push(values[idx]);
collect(depth + 1, values[idx]);
}
}
}
collect(0, values);
return result;
}
function table(tableRows, className) {
if (!tableRows.length) {
return "";
}
return "<table role=\"presentation\" class=\"" + kendo.trim("k-scheduler-table " + (className || "")) + "\">" + "<tr>" + tableRows.join("</tr><tr>") + "</tr>" + "</table>";
}
function allDayTable(tableRows, className) {
if (!tableRows.length) {
return "";
}
return `<div ${kendo.attr("style-position")}="relative">${table(tableRows, className)}</div>`;
}
function timesHeader(columnLevelCount, allDaySlot, rowCount) {
var tableRows = [];
if (rowCount > 0) {
for (var idx = 0; idx < columnLevelCount; idx++) {
tableRows.push("<th class='k-scheduler-cell'>​</th>");
}
}
if (allDaySlot) {
tableRows.push("<th class=\"k-scheduler-times-all-day k-scheduler-cell\">" + allDaySlot.text + "</th>");
}
if (rowCount < 1) {
return $();
}
return $("<div class=\"k-scheduler-times\">" + table(tableRows) + "</div>");
}
function content() {
return $("<div class=\"k-scheduler-content\">" + "<table role=\"presentation\" class=\"k-scheduler-table\"></table>" + "</div>");
}
var HINT = "<div class=\"k-marquee k-scheduler-marquee\">" + "<div class=\"k-marquee-color\"></div>" + "<div class=\"k-marquee-text\">" + "<div class=\"k-label-top\"></div>" + "<div class=\"k-label-bottom\"></div>" + "</div>" + "</div>";
var ResourceView = kendo.Class.extend({
init: function(index, isRtl, enforceAllDaySlot) {
this._index = index;
this._timeSlotCollections = [];
this._daySlotCollections = [];
this._isRtl = isRtl;
this._enforceAllDaySlot = enforceAllDaySlot;
},
addTimeSlotCollection: function(startDate, endDate) {
return this._addCollection(startDate, endDate, this._timeSlotCollections);
},
addDaySlotCollection: function(startDate, endDate) {
return this._addCollection(startDate, endDate, this._daySlotCollections);
},
_addCollection: function(startDate, endDate, collections) {
var collection = new SlotCollection(startDate, endDate, this._index, collections.length);
collections.push(collection);
return collection;
},
timeSlotCollectionCount: function() {
return this._timeSlotCollections.length;
},
daySlotCollectionCount: function() {
return this._daySlotCollections.length;
},
_refreshSlotCollections: function() {
var slotCollections = [this._timeSlotCollections || [], this._daySlotCollections || []];
for (var slotCollectionIndex = 0; slotCollectionIndex < slotCollections.length; slotCollectionIndex++) {
var collections = slotCollections[slotCollectionIndex];
for (var collectionIndex = 0; collectionIndex < collections.length; collectionIndex++) {
var collection = collections[collectionIndex];
collection.refresh();
}
}
},
daySlotByPosition: function(x, y, byDate) {
return this._slotByPosition(x, y, this._daySlotCollections, byDate);
},
timeSlotByPosition: function(x, y, byDate) {
return this._slotByPosition(x, y, this._timeSlotCollections, byDate);
},
_slotByPosition: function(x, y, collections, byDate) {
for (var collectionIndex = 0; collectionIndex < collections.length; collectionIndex++) {
var collection = collections[collectionIndex];
for (var slotIndex = 0; slotIndex < collection.count(); slotIndex++) {
var slot = collection.at(slotIndex);
var width = slot.offsetWidth;
var height = slot.offsetHeight;
var nextSlot;
var horizontalEnd = slot.offsetLeft + width;
var verticalEnd = slot.offsetTop + height;
if (!byDate) {
nextSlot = collection.at(slotIndex + 1);
}
if (nextSlot) {
if (nextSlot.offsetLeft != slot.offsetLeft) {
if (this._isRtl) {
horizontalEnd = slot.offsetLeft + (slot.offsetLeft - nextSlot.offsetLeft);
} else {
horizontalEnd = nextSlot.offsetLeft;
}
} else {
verticalEnd = nextSlot.offsetTop;
}
}
if (x >= slot.offsetLeft && x < horizontalEnd && y >= slot.offsetTop && y < verticalEnd) {
return slot;
}
}
}
},
refresh: function() {
var collectionIndex;
for (collectionIndex = 0; collectionIndex < this._daySlotCollections.length; collectionIndex++) {
this._daySlotCollections[collectionIndex].refresh();
}
for (collectionIndex = 0; collectionIndex < this._timeSlotCollections.length; collectionIndex++) {
this._timeSlotCollections[collectionIndex].refresh();
}
},
timeSlotRanges: function(startTime, endTime) {
var collections = this._timeSlotCollections;
var start = this._startSlot(startTime, collections);
var firstIndex, lastIndex;
if (!start.inRange && startTime >= start.slot.end) {
firstIndex = start.slot.collectionIndex + 1;
start = null;
}
var end = start;
if (startTime < endTime) {
end = this._endSlot(endTime, collections);
}
if (end && !end.inRange && endTime <= end.slot.start) {
lastIndex = end.slot.collectionIndex;
if (endTime === end.slot.start && (start && lastIndex > start.slot.collectionIndex || lastIndex > firstIndex)) {
lastIndex -= 1;
}
end = null;
}
if (start === null && end === null) {
if (endTime - startTime < kendo.date.MS_PER_DAY) {
return [];
} else {
start = {
inRange: true,
slot: collections[firstIndex].first()
};
end = {
inRange: true,
slot: collections[lastIndex].last()
};
}
}
if (start === null) {
if (end.slot.end <= startTime) {
return [];
}
start = {
inRange: true,
slot: (collections[firstIndex] || collections[end.slot.collectionIndex]).first()
};
}
if (end === null) {
if (start.slot.start >= endTime) {
return [];
}
end = {
inRange: true,
slot: (collections[lastIndex] || collections[start.slot.collectionIndex]).last()
};
}
return this._continuousRange(TimeSlotRange, collections, start, end);
},
daySlotRanges: function(startTime, endTime, isAllDay) {
var collections = this._daySlotCollections;
var start = this._startSlot(startTime, collections, isAllDay);
if (!start.inRange && startTime >= start.slot.end) {
start = null;
}
var end = start;
if (startTime < endTime) {
end = this._endSlot(endTime, collections, isAllDay);
}
if (end && !end.inRange && endTime <= end.slot.start) {
end = null;
}
if (start === null && end === null) {
return [];
}
if (start === null) {
if (end.slot.end <= startTime) {
return [];
}
do {
startTime += kendo.date.MS_PER_DAY;
start = this._startSlot(startTime, collections, isAllDay);
} while (!start.inRange && startTime >= start.slot.end);
}
if (end === null) {
if (start.slot.start >= endTime) {
return [];
}
do {
endTime -= kendo.date.MS_PER_DAY;
end = this._endSlot(endTime, collections, isAllDay);
} while (!end.inRange && endTime <= end.slot.start);
}
return this._continuousRange(DaySlotRange, collections, start, end);
},
_continuousRange: function(range, collections, start, end) {
var startSlot = start.slot;
var endSlot = end.slot;
var startIndex = startSlot.collectionIndex;
var endIndex = endSlot.collectionIndex;
var ranges = [];
for (var collectionIndex = startIndex; collectionIndex <= endIndex; collectionIndex++) {
var collection = collections[collectionIndex];
var first = collection.first();
var last = collection.last();
var head = false;
var tail = false;
if (collectionIndex == startIndex) {
tail = !start.inRange;
}
if (collectionIndex == endIndex) {
head = !end.inRange;
}
if (first.start < startSlot.start) {
first = startSlot;
}
if (last.start > endSlot.start) {
last = endSlot;
}
if (startIndex < endIndex) {
if (collectionIndex == startIndex) {
head = true;
} else if (collectionIndex == endIndex) {
tail = true;
} else {
head = tail = true;
}
}
ranges.push(new range({
start: first,
end: last,
collection,
head,
tail
}));
}
return ranges;
},
slotRanges: function(event, isDay) {
var startTime = event._startTime || kendo.date.toUtcTime(event.start);
var endTime = event._endTime || kendo.date.toUtcTime(event.end);
if (isDay === undefined) {
if (this._enforceAllDaySlot) {
isDay = event.isMultiDay();
} else {
isDay = event.isAllDay;
}
}
if (isDay) {
return this.daySlotRanges(startTime, endTime, event.isAllDay);
}
return this.timeSlotRanges(startTime, endTime);
},
ranges: function(startTime, endTime, isDay, isAllDay) {
if (typeof startTime != "number") {
startTime = kendo.date.toUtcTime(startTime);
}
if (typeof endTime != "number") {
endTime = kendo.date.toUtcTime(endTime);
}
if (isDay) {
return this.daySlotRanges(startTime, endTime, isAllDay);
}
return this.timeSlotRanges(startTime, endTime);
},
_startCollection: function(date, collections) {
for (var collectionIndex = 0; collectionIndex < collections.length; collectionIndex++) {
var collection = collections[collectionIndex];
if (collection.startInRange(date)) {
return collection;
}
}
return null;
},
_endCollection: function(date, collections, isAllDay) {
for (var collectionIndex = 0; collectionIndex < collections.length; collectionIndex++) {
var collection = collections[collectionIndex];
if (collection.endInRange(date, isAllDay)) {
return collection;
}
}
return null;
},
_getCollections: function(isDay) {
return isDay ? this._daySlotCollections : this._timeSlotCollections;
},
continuousSlot: function(slot, reverse) {
var pad = reverse ? -1 : 1;
var collections = this._getCollections(slot.isDaySlot);
var collection = collections[slot.collectionIndex + pad];
return collection ? collection[reverse ? "last" : "first"]() : undefined;
},
firstSlot: function() {
var collections = this._getCollections(this.daySlotCollectionCount());
return collections[0].first();
},
lastSlot: function() {
var collections = this._getCollections(this.daySlotCollectionCount());
return collections[collections.length - 1].last();
},
upSlot: function(slot, keepCollection, groupByDateVertically) {
var that = this;
var moveToDaySlot = function(isDaySlot, collectionIndex, index) {
var isFirstCell = index === 0;
if (!keepCollection && !isDaySlot && isFirstCell && that.daySlotCollectionCount()) {
return that._daySlotCollections[0].at(collectionIndex);
}
};
if (!this.timeSlotCollectionCount()) {
keepCollection = true;
}
return this._verticalSlot(slot, -1, moveToDaySlot, groupByDateVertically);
},
downSlot: function(slot, keepCollection, groupByDateVertically) {
var that = this;
var moveToTimeSlot = function(isDaySlot, collectionIndex, index) {
if (!keepCollection && isDaySlot && that.timeSlotCollectionCount()) {
return that._timeSlotCollections[index].at(0);
}
};
if (!this.timeSlotCollectionCount()) {
keepCollection = true;
}
return this._verticalSlot(slot, 1, moveToTimeSlot, groupByDateVertically);
},
leftSlot: function(slot, groupByDateVertically) {
return this._horizontalSlot(slot, -1, groupByDateVertically);
},
rightSlot: function(slot, groupByDateVertically) {
return this._horizontalSlot(slot, 1, groupByDateVertically);
},
_horizontalSlot: function(slot, step, groupByDateVertically) {
var index = slot.index;
var isDaySlot = slot.isDaySlot;
var collectionIndex = slot.collectionIndex;
var collections = this._getCollections(isDaySlot);
isDaySlot = groupByDateVertically ? false : isDaySlot;
if (isDaySlot) {
index += step;
} else {
collectionIndex += step;
}
var collection = collections[collectionIndex];
return collection ? collection.at(index) : undefined;
},
_verticalSlot: function(slot, step, swapCollection, groupByDateVertically) {
var index = slot.index;
var isDaySlot = slot.isDaySlot;
var collectionIndex = slot.collectionIndex;
var collections = this._getCollections(isDaySlot);
slot = swapCollection(isDaySlot, collectionIndex, index);
if (slot) {
return slot;
}
isDaySlot = groupByDateVertically ? false : isDaySlot;
if (isDaySlot) {
collectionIndex += step;
} else {
index += step;
}
var collection = collections[collectionIndex];
return collection ? collection.at(index) : undefined;
},
_collection: function(index, multiday) {
var collections = multiday ? this._daySlotCollections : this._timeSlotCollections;
return collections[index];
},
_startSlot: function(time, collections, isAllDay) {
var collection = this._startCollection(time, collections);
var inRange = true;
var index = 0;
if (!collection) {
collection = collections[index];
while (index < collections.length - 1 && collection._start < time) {
index++;
collection = collections[index];
}
inRange = false;
}
var slot = collection.slotByStartDate(time, isAllDay);
if (!slot) {
slot = collection.first();
inRange = false;
}
return {
slot,
inRange
};
},
_endSlot: function(time, collections, isAllDay) {
var collection = this._endCollection(time, collections, isAllDay);
var inRange = true;
var index = collections.length - 1;
if (!collection) {
collection = collections[index];
while (index > 0 && collection._start > time) {
index--;
collection = collections[index];
}
inRange = false;
}
var slot = collection.slotByEndDate(time, isAllDay);
if (!slot) {
if (time <= collection.first().start) {
slot = collection.first();
} else {
slot = collection.last();
}
inRange = false;
}
return {
slot,
inRange
};
},
getSlotCollection: function(index, isDay) {
return this[isDay ? "getDaySlotCollection" : "getTimeSlotCollection"](index);
},
getTimeSlotCollection: function(index) {
return this._timeSlotCollections[index];
},
getDaySlotCollection: function(index) {
return this._daySlotCollections[index];
}
});
var SlotRange = kendo.Class.extend({
init: function(options) {
$.extend(this, options);
},
innerHeight: function() {
var collection = this.collection;
var startIndex = this.start.index;
var endIndex = this.end.index;
var result = 0;
for (var slotIndex = startIndex; slotIndex <= endIndex; slotIndex++) {
result += collection.at(slotIndex).offsetHeight;
}
return result;
},
events: function() {
return this.collection.events();
},
addEvent: function(event) {
this.events().push(event);
},
startSlot: function() {
if (this.start.offsetLeft > this.end.offsetLeft) {
return this.end;
}
return this.start;
},
endSlot: function() {
if (this.start.offsetLeft > this.end.offsetLeft) {
return this.start;
}
return this.end;
}
});
var TimeSlotRange = SlotRange.extend({
innerHeight: function() {
var collection = this.collection;
var startIndex = this.start.index;
var endIndex = this.end.index;
var result = 0;
for (var slotIndex = startIndex; slotIndex <= endIndex; slotIndex++) {
result += collection.at(slotIndex).offsetHeight;
}
return result;
},
outerRect: function(start, end, snap) {
return this._rect("offset", start, end, snap);
},
_rect: function(property, start, end, snap) {
var top;
var bottom;
var left;
var right;
var startSlot = this.start;
var endSlot = this.end;
var isRtl = kendo.support.isRtl(startSlot.element);
if (typeof start != "number") {
start = kendo.date.toUtcTime(start);
}
if (typeof end != "number") {
end = kendo.date.toUtcTime(end);
}
if (snap) {
top = startSlot.offsetTop;
bottom = endSlot.offsetTop + endSlot[property + "Height"];
if (isRtl) {
left = endSlot.offsetLeft;
right = startSlot.offsetLeft + startSlot[property + "Width"];
} else {
left = startSlot.offsetLeft;
right = endSlot.offsetLeft + endSlot[property + "Width"];
}
} else {
var startOffset = start - startSlot.start;
if (startOffset < 0) {
startOffset = 0;
}
var startSlotDuration = startSlot.end - startSlot.start;
top = startSlot.offsetTop + startSlot[property + "Height"] * startOffset / startSlotDuration;
var endOffset = endSlot.end - end;
if (endOffset < 0) {
endOffset = 0;
}
var endSlotDuration = endSlot.end - endSlot.start;
bottom = endSlot.offsetTop + endSlot[property + "Height"] - endSlot[property + "Height"] * endOffset / endSlotDuration;
if (isRtl) {
left = Math.round(endSlot.offsetLeft + endSlot[property + "Width"] * endOffset / endSlotDuration);
right = Math.round(startSlot.offsetLeft + startSlot[property + "Width"] - startSlot[property + "Width"] * startOffset / startSlotDuration);
} else {
left = Math.round(startSlot.offsetLeft + startSlot[property + "Width"] * startOffset / startSlotDuration);
right = Math.round(endSlot.offsetLeft + endSlot[property + "Width"] - endSlot[property + "Width"] * endOffset / endSlotDuration);
}
}
return {
top,
bottom,
left: left === 0 ? left : left + 1,
right
};
},
innerRect: function(start, end, snap) {
return this._rect("client", start, end, snap);
}
});
var DaySlotRange = SlotRange.extend({ innerWidth: function() {
var collection = this.collection;
var startIndex = this.start.index;
var endIndex = this.end.index;
var result = 0;
var width = startIndex !== endIndex ? "offsetWidth" : "clientWidth";
for (var slotIndex = startIndex; slotIndex <= endIndex; slotIndex++) {
result += collection.at(slotIndex)[width];
}
return result;
} });
var SlotCollection = kendo.Class.extend({
init: function(startDate, endDate, groupIndex, collectionIndex) {
this._slots = [];
this._events = [];
this._start = kendo.date.toUtcTime(startDate);
this._end = kendo.date.toUtcTime(endDate);
this._groupIndex = groupIndex;
this._collectionIndex = collectionIndex;
},
refresh: function() {
for (var slotIndex = 0; slotIndex < this._slots.length; slotIndex++) {
this._slots[slotIndex].refresh();
}
},
startInRange: function(date) {
return this._start <= date && date < this._end;
},
endInRange: function(date, isAllDay) {
var end = isAllDay ? date < this._end : date <= this._end;
return this._start <= date && end;
},
slotByStartDate: function(date) {
var time = date;
if (typeof time != "number") {
time = kendo.date.toUtcTime(date);
}
for (var slotIndex = 0; slotIndex < this._slots.length; slotIndex++) {
var slot = this._slots[slotIndex];
if (slot.startInRange(time)) {
return slot;
}
}
return null;
},
slotByEndDate: function(date, allday) {
var time = date;
if (typeof time != "number") {
time = kendo.date.toUtcTime(date);
}
if (allday) {
return this.slotByStartDate(date, false);
}
for (var slotIndex = 0; slotIndex < this._slots.length; slotIndex++) {
var slot = this._slots[slotIndex];
if (slot.endInRange(time)) {
return slot;
}
}
return null;
},
count: function() {
return this._slots.length;
},
events: function() {
return this._events;
},
addTimeSlot: function(element, start, end, isHorizontal) {
var slot = new TimeSlot(element, start, end, this._groupIndex, this._collectionIndex, this._slots.length, isHorizontal);
this._slots.push(slot);
},
addDaySlot: function(element, start, end, eventCount) {
var slot = new DaySlot(element, start, end, this._groupIndex, this._collectionIndex, this._slots.length, eventCount);
this._slots.push(slot);
},
first: function() {
return this._slots[0];
},
last: function() {
return this._slots[this._slots.length - 1];
},
at: function(index) {
return this._slots[index];
}
});
var Slot = kendo.Class.extend({
init: function(element, start, end, groupIndex, collectionIndex, index) {
this.element = element;
this.clientWidth = element.clientWidth;
this.clientHeight = element.clientHeight;
this.offsetWidth = element.offsetWidth;
this.offsetHeight = element.offsetHeight;
this.offsetTop = element.offsetTop;
this.offsetLeft = element.offsetLeft;
this.start = start;
this.end = end;
this.element = element;
this.groupIndex = groupIndex;
this.collectionIndex = collectionIndex;
this.index = index;
this.isDaySlot = false;
},
refresh: function() {
var element = this.element;
this.clientWidth = element.clientWidth;
this.clientHeight = element.clientHeight;
this.offsetWidth = element.offsetWidth;
this.offsetHeight = element.offsetHeight;
this.offsetTop = element.offsetTop;
this.offsetLeft = element.offsetLeft;
},
startDate: function() {
return kendo.timezone.toLocalDate(this.start);
},
endDate: function() {
return kendo.timezone.toLocalDate(this.end);
},
startInRange: function(date) {
return this.start <= date && date < this.end;
},
endInRange: function(date) {
return this.start < date && date <= this.end;
},
startOffset: function() {
return this.start;
},
endOffset: function() {
return this.end;
}
});
var TimeSlot = Slot.extend({
init: function(element, start, end, groupIndex, collectionIndex, index, isHorizontal) {
Slot.fn.init.apply(this, arguments);
this.isHorizontal = isHorizontal ? true : false;
},
offsetX: function(rtl, offset) {
if (rtl) {
return this.offsetLeft + offset;
} else {
return this.offsetLeft + offset;
}
},
startInRange: function(date) {
return this.start <= date && date < this.end;
},
endInRange: function(date) {
return this.start < date && date <= this.end;
},
startOffset: function(x, y, snap) {
if (snap) {
return this.start;
}
var offset = $(this.element).offset();
var duration = this.end - this.start;
var difference;
var time;
if (this.isHorizontal) {
var isRtl = kendo.support.isRtl(this.element);
difference = x - offset.left;
time = Math.floor(duration * (difference / this.offsetWidth));
if (isRtl) {
return this.start + duration - time;
}
} else {
difference = y - offset.top;
time = Math.floor(duration * (difference / this.offsetHeight));
}
return this.start + time;
},
endOffset: function(x, y, snap) {
if (snap) {
return this.end;
}
var offset = $(this.element).offset();
var duration = this.end - this.start;
var difference;
var time;
if (this.isHorizontal) {
var isRtl = kendo.support.isRtl(this.element);
difference = x - offset.left;
time = Math.floor(duration * (difference / this.offsetWidth));
if (isRtl) {
return this.start + duration - time;
}
} else {
difference = y - offset.top;
time = Math.floor(duration * (difference / this.offsetHeight));
}
return this.start + time;
}
});
var DaySlot = Slot.extend({
init: function(element, start, end, groupIndex, collectionIndex, index, eventCount) {
Slot.fn.init.apply(this, arguments);
this.eventCount = eventCount;
this.isDaySlot = true;
if (this.element.children.length) {
var firstChild = this.element.children[0];
this.firstChildHeight = firstChild.offsetHeight;
this.firstChildTop = firstChild.offsetTop;
} else {
this.firstChildHeight = 3;
this.firstChildTop = 0;
}
},
startDate: function() {
var date = new Date(this.start);
return kendo.timezone.apply(date, "Etc/UTC");
},
endDate: function() {
var date = new Date(this.end);
return kendo.timezone.apply(date, "Etc/UTC");
},
startInRange: function(date) {
return this.start <= date && date < this.end;
},
endInRange: function(date) {
return this.start < date && date <= this.end;
}
});
kendo.ui.SchedulerView = Widget.extend({
init: function(element, options) {
Widget.fn.init.call(this, element, $.extend({}, this.options, options));
this._normalizeOptions();
this._initDefaultTools();
this._scrollbar = kendo.support.scrollbar();
this._isRtl = kendo.support.isRtl(element);
this._resizeHint = $();
this._moveHint = $();
this._cellId = kendo.guid();
this._resourcesForGroups();
this._selectedSlots = [];
this.element.attr("role", "application");
},
options: { messages: { ariaEventLabel: {
on: "on",
at: "at",
to: "to",
allDay: "(all day)",
prefix: ""
} } },
visibleEndDate: function() {
return this.endDate();
},
_initDefaultTools: function() {
this._defaultTools = {
todayMobile: {
type: "button",
fillMode: "flat",
text: this.options.messages.today,
click: this._footerTodayClickHandler.bind(this),
attributes: { class: "k-scheduler-today" }
},
fulldayDesktop: {
type: "button",
icon: "clock",
text: this.options.showWorkHours ? this.options.messages.showFullDay : this.options.messages.showWorkDay,
click: this.toggleFullDay ? this.toggleFullDay.bind(this) : $.noop,
attributes: { class: "k-scheduler-fullday" }
},
fulldayMobile: {
type: "button",
fillMode: "flat",
text: this.options.showWorkHours ? this.options.messages.showFullDay : this.options.messages.showWorkDay,
click: this.toggleFullDay ? this.toggleFullDay.bind(this) : $.noop,
attributes: { class: "k-scheduler-fullday" }
}
};
},
_normalizeOptions: function() {
var options = this.options;
if (options.startTime) {
options.startTime.setMilliseconds(0);
}
if (options.endTime) {
options.endTime.setMilliseconds(0);
}
if (options.workDayStart) {
options.workDayStart.setMilliseconds(0);
}
if (options.workDayEnd) {
options.workDayEnd.setMilliseconds(0);
}
},
_isMobile: function() {
var options = this.options;
return options.mobile === true && kendo.support.mobileOS || options.mobile === "phone" || options.mobile === "tablet";
},
_addResourceView: function() {
var resourceView = new ResourceView(this.groups.length, this._isRtl, this.options.enforceAllDaySlot);
this.groups.push(resourceView);
return resourceView;
},
_refreshResourceViews: function() {
var groups = this.groups;
if (groups) {
for (var i = 0; i < groups.length; i++) {
groups[i]._refreshSlotCollections();
}
}
},
dateForTitle: function() {
return kendo.format(this.options.selectedDateFormat, this.startDate(), this.endDate());
},
shortDateForTitle: function() {
return kendo.format(this.options.selectedShortDateFormat, this.startDate(), this.endDate());
},
mobileDateForTitle: function() {
return kendo.format(this.options.selectedMobileDateFormat || this.options.selectedShortDateFormat, this.startDate(), this.endDate());
},
_changeGroup: function(selection, previous) {
var method = previous ? "prevGroupSlot" : "nextGroupSlot";
var slot = this[method](selection.start, selection.groupIndex, selection.isAllDay);
if (slot) {
selection.groupIndex += previous ? -1 : 1;
}
if (this._isGroupedByDate() && !slot) {
selection.groupIndex = previous ? this.groups.length - 1 : 0;
}
return slot;
},
_changeDate: function(selection, slot, previous) {
var group = this.groups[selection.groupIndex];
var collections, index;
if (previous) {
collections = group._getCollections(false);
index = group.daySlotCollectionCount() ? slot.index - 1 : slot.collectionIndex - 1;
if (index >= 0) {
return collections[index]._slots[collections[index]._slots.length - 1];
}
} else {
collections = group._getCollections(group.daySlotCollectionCount());
index = group.daySlotCollectionCount() ? 0 : slot.collectionIndex + 1;
var slotIndex = group.daySlotCollectionCount() ? slot.collectionIndex + 1 : 0;
if (collections[index] && collections[index]._slots[slotIndex]) {
return collections[index]._slots[slotIndex];
}
}
},
_changeGroupContinuously: function() {
return null;
},
_changeViewPeriod: function() {
return false;
},
_isInRange: function(newStart, newEnd) {
if (!newStart || !newEnd || !this.options.min || !this.options.max) {
return false;
}
return getDate(newStart) <= getDate(this.options.min) || getDate(newEnd) >= getDate(this.options.max);
},
_horizontalSlots: function(selection, ranges, multiple, reverse) {
var method = reverse ? "leftSlot" : "rightSlot";
var horizontalRange = {
startSlot: ranges[0].start,
endSlot: ranges[ranges.length - 1].end
};
var group = this.groups[selection.groupIndex];
var isVertical = this._isVerticallyGrouped();
if (!multiple) {
var slot = this._normalizeHorizontalSelection(selection, ranges, reverse);
if (slot) {
horizontalRange.startSlot = horizontalRange.endSlot = slot;
}
}
if (this._isGroupedByDate() && !multiple) {
var tempSlot = this._changeGroup(selection, reverse);
if (!tempSlot) {
horizontalRange = this._getNextHorizontalRange(group, method, horizontalRange);
} else {
horizontalRange.startSlot = horizontalRange.endSlot = tempSlot;
}
} else {
horizontalRange.startSlot = group[method](horizontalRange.startSlot);
horizontalRange.endSlot = group[method](horizontalRange.endSlot);
if (!multiple && !isVertical && (!horizontalRange.startSlot || !horizontalRange.endSlot)) {
horizontalRange.startSlot = horizontalRange.endSlot = this._changeGroup(selection, reverse);
}
}
var continuousSlot;
if ((!horizontalRange.startSlot || !horizontalRange.endSlot) && !this._isGroupedByDate()) {
continuousSlot = this._continuousSlot(selection, ranges, reverse);
continuousSlot = this._changeGroupContinuously(selection, continuousSlot, multiple, reverse);
if (continuousSlot) {
horizontalRange.startSlot = horizontalRange.endSlot = continuousSlot;
}
}
return horizontalRange;
},
_getNextHorizontalRange: function(group, method, horizontalRange) {
if (!this._isVerticallyGrouped()) {
horizontalRange.startSlot = group[method](horizontalRange.startSlot);
horizontalRange.endSlot = group[method](horizontalRange.endSlot);
}
return horizontalRange;
},
_verticalSlots: function(selection, ranges, multiple, reverse) {
var group = this.groups[selection.groupIndex];
var slot;
var verticalRange = {
startSlot: ranges[0].start,
endSlot: ranges[ranges.length - 1].end
};
if (!multiple) {
slot = this._normalizeVerticalSelection(selection, ranges, reverse);
if (slot) {
verticalRange.startSlot = verticalRange.endSlot = slot;
}
}
var method = reverse ? "upSlot" : "downSlot";
verticalRange = this._getNextVerticalRange(group, method, verticalRange, multiple);
if (!multiple && this._isVerticallyGrouped() && (!verticalRange.startSlot || !verticalRange.endSlot)) {
if (this._isGroupedByDate()) {
verticalRange.startSlot = verticalRange.endSlot = this._changeDate(selection, slot, reverse);
} else {
verticalRange.startSlot = verticalRange.endSlot = this._changeGroup(selection, reverse);
}
}
return verticalRange;
},
_getNextVerticalRange: function(group, method, verticalRange, multiple) {
verticalRange.startSlot = group[method](verticalRange.startSlot, multiple);
verticalRange.endSlot = group[method](verticalRange.endSlot, multiple);
return verticalRange;
},
_normalizeHorizontalSelection: function() {
return null;
},
_normalizeVerticalSelection: function(selection, ranges, reverse) {
var slot;
if (reverse) {
slot = ranges[0].start;
} else {
slot = ranges[ranges.length - 1].end;
}
return slot;
},
_continuousSlot: function() {
return null;
},
_footerTodayClickHandler: function(e) {
e.preventDefault();
var that = this;
var options = that.options;
var timezone = that.options.timezone;
var action = "today";
var currentDate = new Date();
var date;
if (timezone) {
var timezoneOffset = kendo.timezone.offset(currentDate, timezone);
date = kendo.timezone.convert(currentDate, currentDate.getTimezoneOffset(), timezoneOffset);
} else {
date = currentDate;
}
that.trigger("navigate", {
view: that.name || options.name,
action,
date
});
},
_footerItems: function() {
var that = this, items = [], options = this.options;
if (that._isMobile()) {
items.push({
type: "button",
fillMode: "flat",
text: options.messages.today,
click: that._footerTodayClickHandler.bind(that),
attributes: { class: "k-scheduler-today" }
});
}
return items;
},
_footer: function() {
if (this.options.footer === false) {
return;
}
var that = this, items = that._footerItems();
if (items.length > 0) {
var html = $("<div class=\"k-scheduler-footer\">");
that.footer = html.appendTo(that.element);
that.footer.kendoToolBar({
resizable: false,
items
});
}
},
constrainSelection: function(selection) {
var group = this.groups[0];
var slot;
if (!this.inRange(selection)) {
slot = group.firstSlot();
selection.isAllDay = slot.isDaySlot;
selection.start = slot.startDate();
selection.end = slot.endDate();
} else {
if (!group.daySlotCollectionCount()) {
selection.isAllDay = false;
} else if (!group.timeSlotCollectionCount()) {
selection.isAllDay = true;
}
}
if (!this.groups[selection.groupIndex]) {
selection.groupIndex = 0;
}
},
move: function(selection, key, shift) {
var handled = false;
var group = this.groups[selection.groupIndex];
var verticalByDate = this._isGroupedByDate() && this._isVerticallyGrouped();
if (!group.timeSlotCollectionCount()) {
selection.isAllDay = true;
}
var ranges = group.ranges(selection.start, selection.end, selection.isAllDay, false);
var startSlot, endSlot, reverse, slots;
if (key === keys.DOWN || key === keys.UP) {
handled = true;
reverse = key === keys.UP;
this._updateDirection(selection, ranges, shift, reverse, true);
slots = this._verticalSlots(selection, ranges, shift, reverse);
if (!slots.startSlot && !shift && this._changeViewPeriod(selection, reverse, !verticalByDate)) {
return handled;
}
} else if (key === keys.LEFT || key === keys.RIGHT) {
handled = true;
reverse = key === keys.LEFT;
this._updateDirection(selection, ranges, shift, reverse, false);
slots = this._horizontalSlots(selection, ranges, shift, reverse);
if (!slots.startSlot && !shift && this._changeViewPeriod(selection, reverse, verticalByDate)) {
return handled;
}
}
if (handled) {
startSlot = slots.startSlot;
endSlot = slots.endSlot;
if (shift) {
var backward = selection.backward;
if (backward && startSlot) {
selection.start = startSlot.startDate();
} else if (!backward && endSlot) {
selection.end = endSlot.endDate();
}
} else if (startSlot && endSlot) {
selection.isAllDay = startSlot.isDaySlot;
selection.start = startSlot.startDate();
selection.end = endSlot.endDate();
}
selection.events = [];
}
return handled;
},
moveToEventInGroup: function(group, slot, selectedEvents, prev) {
var events = group._continuousEvents || [];
var found, event;
var pad = prev ? -1 : 1;
var length = events.length;
var idx = prev ? length - 1 : 0;
var i, lastSelected;
if (selectedEvents.length) {
lastSelected = selectedEvents[selectedEvents.length - 1];
if (prev) {
for (i = 0; i < events.length; i++) {
if (events[i].uid === lastSelected) {
idx = i + pad;
}
}
} else {
for (i = events.length - 1; i > -1; i--) {
if (events[i].uid === lastSelected) {
idx = i + pad;
}
}
}
}
while (idx < length && idx > -1) {
event = events[idx];
if (!prev && event.start.startDate() >= slot.startDate() || prev && event.start.startDate() <= slot.startDate()) {
if (event && $.inArray(event.uid, selectedEvents) === -1) {
found = !!event;
break;
}
}
idx += pad;
}
return event;
},
moveToEvent: function(selection, prev) {
var groupIndex = selection.groupIndex;
var group = this.groups[groupIndex];
var slot = group.ranges(selection.start, selection.end, this.name === "month" || selection.isAllDay, false)[0].start;
var length = this.groups.length;
var pad = prev ? -1 : 1;
var events = selection.events;
var event;
if (this._isGroupedByDate()) {
var allEvents = this._getAllEvents();
var uniqueAllEvents = this._getUniqueEvents(allEvents);
var sortedEvents = this._getSortedEvents(uniqueAllEvents);
if (events.length === 0) {
var eventIndex = this._getNextEventIndexBySlot(slot, sortedEvents, groupIndex);
if (prev) {
eventIndex--;
}
event = sortedEvents[eventIndex];
} else {
var idx = this._getStartIdx(events, sortedEvents);
while (idx < sortedEvents.length && idx > -1) {
if (events.length > 0) {
slot = this._getSelectedSlot(slot, sortedEvents, event, idx, pad, prev);
}
if (!slot) {
break;
}
if (!prev && sortedEvents[idx].start.startDate() >= slot.startDate() || prev && sortedEvents[idx].start.startDate() <= slot.startDate()) {
if (events[0] != sortedEvents[idx].uid) {
event = sortedEvents[idx];
break;
}
}
idx += pad;
}
}
} else {
while (groupIndex < length && groupIndex > -1) {
event = this.moveToEventInGroup(group, slot, events, prev);
groupIndex += pad;
group = this.groups[groupIndex];
if (!group || event) {
break;
}
events = [];
if (prev) {
slot = group.lastSlot();
} else {
slot = group.firstSlot(true);
}
}
}
if (event) {
selection.events = [event.uid];
selection.start = event.start.startDate();
selection.end = event.end.endDate();
selection.isAllDay = event.start.isDaySlot;
selection.groupIndex = event.start.groupIndex;
selection.eventElement = event.element[0];
}
return !!event;
},
current: function(candidate) {
if (candidate !== undefined) {
this._current = candidate;
if (this.content.has(candidate)) {
this._scrollTo(candidate, this.content[0]);
}
} else {
return this._current;
}
},
select: function(selection) {
this.clearSelection();
if (!this._selectEvents(selection)) {
this._selectSlots(selection);
}
},
_getNextEventIndexBySlot: function(slot, sortedEvents, groupIndex) {
var tempIndex = 0;
var slotStartDate = kendo.date.getDate(slot.startDate());
for (var i = 0; i < sortedEvents.length; i++) {
var eventStartDate = kendo.date.getDate(sortedEvents[i].start.startDate());
if (slotStartDate > eventStartDate) {
tempIndex++;
continue;
}
if (slotStartDate.getTime() === eventStartDate.getTime() && groupIndex > sortedEvents[i].start.groupIndex) {
tempIndex++;
continue;
}
if (slotStartDate.getTime() === eventStartDate.getTime() && groupIndex >= sortedEvents[i].start.groupIndex && slot.startDate() > sortedEvents[i].start.startDate()) {
tempIndex++;
continue;
}
break;
}
return tempIndex;
},
_getSelectedSlot: function(slot, sortedEvents, event, idx, pad, prev) {
if (sortedEvents[idx + pad] && sortedEvents[idx].start.groupIndex !== sortedEvents[idx + pad].start.groupIndex) {
var groupIndex = sortedEvents[idx + pad].start.groupIndex;
var group = this.groups[groupIndex];
if (!group || event) {
slot = null;
}
if (prev) {
slot = group.lastSlot();
} else {
slot = group.firstSlot(true);
}
}
return slot;
},
_getStartIdx: function(events, sortedEvents) {
var selectedEventIndex = 0;
$.each(sortedEvents, function() {
if (this.uid === events[0]) {
return false;
}
selectedEventIndex++;
});
return selectedEventIndex;
},
_getAllEvents: function() {
var allEvents = [];
var groups = this.groups;
for (var idx = 0; idx < groups.length; idx++) {
if (groups[idx]._continuousEvents) {
allEvents = allEvents.concat(groups[idx]._continuousEvents);
}
}
return allEvents;
},
_getUniqueEvents: function(allEvents) {
var uniqueAllEvents = [];
for (var i = 0; i < allEvents.length; i++) {
var exists = false;
for (var j = 0; j < uniqueAllEvents.length; j++) {
if (allEvents[i].uid === uniqueAllEvents[j].uid) {
exists = true;
break;
}
}
if (!exists) {
uniqueAllEvents.push(allEvents[i]);
}
}
return uniqueAllEvents;
},
_getSortedEvents: function(uniqueAllEvents) {
return uniqueAllEvents.sort(function(first, second) {
var firstStartDate = first.start.startDate();
var secondStartDate = second.start.startDate();
var result = kendo.date.getDate(firstStartDate) - kendo.date.getDate(secondStartDate);
if (result === 0) {
result = first.start.groupIndex - second.start.groupIndex;
}
if (result === 0) {
result = firstStartDate.getTime() - secondStartDate.getTime();
}
if (result === 0) {
if (first.start.isDaySlot && !second.start.isDaySlot) {
result = -1;
}
if (!first.start.isDaySlot && second.start.isDaySlot) {
result = 1;
}
}
if (result === 0) {
result = $(first.element).index() - $(second.element).index();
}
return result;
});
},
_selectSlots: function(selection) {
var isAllDay = selection.isAllDay;
var group = this.groups[selection.groupIndex];
if (!group.timeSlotCollectionCount()) {
isAllDay = true;
}
this._selectedSlots = [];
var ranges = group.ranges(selection.start, selection.end, isAllDay, false);
var element;
var slot;
for (var rangeIndex = 0; rangeIndex < ranges.length; rangeIndex++) {
var range = ranges[rangeIndex];
var collection = range.collection;
for (var slotIndex = range.start.index; slotIndex <= range.end.index; slotIndex++) {
slot = collection.at(slotIndex);
element = slot.element;
addSelectedState(element);
this._selectedSlots.push({
start: slot.startDate(),
end: slot.endDate(),
element
});
}
}
if (selection.backward) {
element = ranges[0].start.element;
}
this.current(element);
},
_selectEvents: function(selection) {
var found = false;
var events = selection.events;
var groupEvents = this._getAllEvents();
var idx, groupEvent, length = groupEvents.length;
if (!events[0] || !groupEvents[0]) {
return found;
}
var result = $();
selection.events = [];
for (idx = 0; idx < length; idx++) {
if ($.inArray(groupEvents[idx].uid, events) > -1) {
groupEvent = groupEvents[idx];
result = result.add(groupEvent.element);
if (selection.events.indexOf(groupEvent.uid) === -1) {
selection.events.push(groupEvent.uid);
}
}
}
if (result[0]) {
result.addClass("k-selected");
if (selection.eventElement) {
this.current(selection.eventElement);
} else {
this.current(result.last()[0]);
}
this._selectedSlots = [];
found = true;
}
return found;
},
inRange: function(options) {
var startDate = this.startDate();
var endDate = kendo.date.addDays(this.endDate(), 1);
var start = options.start;
var end = options.end;
return startDate <= start && start < endDate && startDate < end && end <= endDate;
},
_resourceValue: function(resource, item) {
if (resource.valuePrimitive) {
item = kendo.getter(resource.dataValueField)(item);
}
return item;
},
_setResourceValue: function(groupLevelMember, resource, result) {
var value = groupLevelMember.value, setter;
if (resource.multiple) {
value = [value];
}
setter = kendo.setter(resource.field);
setter(result, value);
},
_resourceBySlot: function(slot) {
var resources = this.groupedResources;
var result = {};
if (resources.length) {
var resourceIndex = slot.groupIndex, groupOptions = this.options.group, nestedMember = groupOptions.date || groupOptions.orientation === "horizontal" ? "columns" : "rows", levels = nestedMember === "rows" ? this.rowLevels : this.columnLevels, dateGroupCompensation = groupOptions.date && groupOptions.orientation === "horizontal" ? 1 : 0, groupLevel = levels[resources.length - 1 + dateGroupCompensation], resource = resources[resources.length - 1], groupLevelMember = groupLevel[resourceIndex], passedChildren, numberOfChildren, j, i;
this._setResourceValue(groupLevelMember, resource, result);
for (j = resources.length - 2; j >= 0; j--) {
groupLevel = levels[j + dateGroupCompensation];
resource = resources[j];
passedChildren = 0;
for (i = 0; i < groupLevel.length; i++) {
groupLevelMember = groupLevel[i];
numberOfChildren = groupLevelMember[nestedMember].length;
if (numberOfChildren > resourceIndex - passedChildren) {
this._setResourceValue(groupLevelMember, resource, result);
break;
} else {
passedChildren += numberOfChildren;
}
}
}
}
return result;
},
_createResizeHint: function(left, top, width, height) {
return $(HINT).css({
left,
top,
width,
height
});
},
_removeResizeHint: function() {
this._resizeHint.remove();
this._resizeHint = $();
},
_removeMoveHint: function(uid) {
if (uid) {
this._moveHint.filter("[data-uid='" + uid + "']").remove();
this._moveHint = this._moveHint.filter("[data-uid!='" + uid + "']");
} else {
this._moveHint.remove();
this._moveHint = $();
}
},
_scrollTo: function(element, container) {
var elementOffset = element.offsetTop, elementOffsetDir = element.offsetHeight, containerScroll = container.scrollTop, containerOffsetDir = container.clientHeight, bottomDistance = elementOffset + elementOffsetDir, result = 0;
if (containerScroll > elementOffset) {
result = elementOffset;
} else if (bottomDistance > containerScroll + containerOffsetDir) {
if (elementOffsetDir <= containerOffsetDir) {
result = bottomDistance - containerOffsetDir;
} else {
result = elementOffset;
}
} else {
result = containerScroll;
}
container.scrollTop = result;
},
_inverseEventColor: function(element) {
var eventColor = element.css("color");
var eventColorIsDark = new Color(eventColor).isDark();
var eventBackground = element.css("background-color");
var eventBackgroundIsDark = new Color(eventBackground).isDark();
if (eventColorIsDark == eventBackgroundIsDark) {
element.addClass(INVERSE_COLOR_CLASS);
}
},
_eventTmpl: function(template, wrapper) {
var options = this.options, settings = $.extend({}, kendo.Template, options.templateSettings), paramName = settings.paramName, html = "", type = typeof template, state = {
storage: {},
count: 0
};
if (type === "function") {
state.storage["tmpl" + state.count] = template;
html += "#=this.tmpl" + state.count + "(" + paramName + ")#";
state.count++;
} else if (type === "string") {
html += template;
}
var tmpl = kendo.template(kendo.format(wrapper, html), settings);
if (state.count > 0) {
tmpl = tmpl.bind(state.storage);
}
return tmpl;
},
eventResources: function(event) {
var resources = [], options = this.options;
if (!options.resources) {
return resources;
}
for (var idx = 0; idx < options.resources.length; idx++) {
var resource = options.resources[idx];
var field = resource.field;
var eventResources = kendo.getter(field)(event);
if (eventResources == null) {
continue;
}
if (!resource.multiple) {
eventResources = [eventResources];
}
var data = resource.dataSource.view();
for (var resourceIndex = 0; resourceIndex < eventResources.length; resourceIndex++) {
var eventResource = null;
var value = eventResources[resourceIndex];
if (!resource.valuePrimitive) {
value = kendo.getter(resource.dataValueField)(value);
}
for (var dataIndex = 0; dataIndex < data.length; dataIndex++) {
if (data[dataIndex].get(resource.dataValueField) == value) {
eventResource = data[dataIndex];
break;
}
}
if (eventResource !== null) {
var resourceColor = kendo.getter(resource.dataColorField)(eventResource);
resources.push({
field: resource.field,
title: resource.title,
name: resource.name,
text: kendo.getter(resource.dataTextField)(eventResource),
value,
color: resourceColor
});
}
}
}
return resources;
},
createLayout: