UNPKG

@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
//#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'>&#8203;</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: