@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
4,579 lines • 155 kB
JavaScript
//#region ../src/kendo.scheduler.js
const __meta__ = {
id: "scheduler",
name: "Scheduler",
category: "web",
description: "The Scheduler is an event calendar.",
depends: [
"dropdownlist",
"editable",
"multiselect",
"window",
"datepicker",
"datetimepicker",
"toolbar",
"scheduler.recurrence",
"scheduler.view",
"html.button",
"icons"
],
features: [
{
id: "scheduler-dayview",
name: "Scheduler Day View",
description: "Scheduler Day View",
depends: ["scheduler.dayview"]
},
{
id: "scheduler-agendaview",
name: "Scheduler Agenda View",
description: "Scheduler Agenda View",
depends: ["scheduler.agendaview"]
},
{
id: "scheduler-monthview",
name: "Scheduler Month View",
description: "Scheduler Month View",
depends: ["scheduler.monthview"]
},
{
id: "scheduler-timelineview",
name: "Scheduler Timeline View",
description: "Scheduler Timeline View",
depends: ["scheduler.timelineview"]
},
{
id: "scheduler-yearview",
name: "Scheduler Year View",
description: "Scheduler Year View",
depends: ["scheduler.yearview"]
},
{
id: "scheduler-mobile",
name: "Scheduler adaptive rendering",
description: "Support for adaptive rendering",
depends: [
"dialog",
"pane",
"switch"
]
},
{
id: "scheduler-pdf-export",
name: "PDF export",
description: "Export the scheduler events as PDF",
depends: ["pdf", "drawing"]
},
{
id: "scheduler-timezones",
name: "Timezones",
description: "Allow selecting timezones different than Etc/UTC",
depends: ["timezones"]
}
]
};
(function($, undefined) {
var kendo = window.kendo, date = kendo.date, MS_PER_DAY = date.MS_PER_DAY, getDate = date.getDate, getMilliseconds = kendo.date.getMilliseconds, recurrence = kendo.recurrence, encode = kendo.htmlEncode, keys = kendo.keys, ui = kendo.ui, Widget = ui.Widget, DataBoundWidget = ui.DataBoundWidget, STRING = "string", Popup = ui.Popup, Calendar = ui.Calendar, DataSource = kendo.data.DataSource, isPlainObject = $.isPlainObject, extend = $.extend, toString = Object.prototype.toString, isArray = Array.isArray, NS = ".kendoScheduler", CLICK = "click", MOUSEDOWN = "mousedown", TOUCHSTART = kendo.support.pointers ? "pointerdown" : "touchstart", TOUCHMOVE = kendo.support.pointers ? "pointermove" : "touchmove", TOUCHEND = kendo.support.pointers ? "pointerup" : "touchend", MOUSEMOVE = kendo.support.mousemove, CHANGE = "change", PROGRESS = "progress", ERROR = "error", CANCEL = "cancel", REMOVE = "remove", RESET = "resetSeries", SAVE = "save", ADD = "add", EDIT = "edit", DISABLED = "disabled", OPTION = "option", FOCUSEDSTATE = "k-focus", INVERSECOLORCLASS = "k-event-inverse", valueStartEndBoundRegex = /(?:value:start|value:end)(?:,|$)/, MIN_SCREEN = "(min-width: 1024px)", TODAY = getDate(new Date()), EXCEPTION_SEPARATOR = ",", OLD_EXCEPTION_SEPARATOR_REGEXP = /\;/g, RECURRENCE_EXCEPTION = "recurrenceException", DELETECONFIRM = "Are you sure you want to delete this event?", DELETERECURRING = "Do you want to delete only this event occurrence or the whole series?", EDITRECURRING = "Do you want to edit only this event occurrence or the whole series?", DELETERECURRINGCONFIRM = "Are you sure you want to delete this event occurrence?", RESETSERIESCONFIRM = "Are you sure you want to reset the whole series?", DELETESERIESCONFIRM = "Are you sure you want to delete the whole series?", ONGOING_CLASS = "k-event-ongoing", COMMANDBUTTONTMPL = ({ className, attr, text, icon, fillMode, themeColor }) => kendo.html.renderButton(`<button type="button" class="${className}" ${attr}>${text}</button>`, {
icon,
fillMode,
themeColor
}), VIEWS_DROPDOWN_TEMPLATE = kendo.template(({ label, views, type }) => `<select aria-label="${label}" class="k-picker k-dropdown-list k-dropdown ${type}">` + Object.keys(views).map((view) => `<option value="${view}">${views[view].title}</option>`).join("") + "</select>"), DEFAULT_TOOLS = {
pdf: {
name: "pdf",
type: "button",
icon: "file-pdf",
attributes: { class: "k-pdf" }
},
pdfMobile: {
name: "pdf",
type: "button",
icon: "file-pdf",
showText: "overflow",
attributes: { class: "k-pdf" }
},
today: {
name: "today",
type: "button",
attributes: { "ref-nav-today": "" }
},
previous: {
name: "previous",
type: "button",
icon: "caret-alt-left",
showText: "overflow",
attributes: { "ref-nav-prev": "" },
groupClass: "k-scheduler-navigation"
},
next: {
name: "next",
type: "button",
icon: "caret-alt-right",
showText: "overflow",
attributes: { "ref-nav-next": "" },
groupClass: "k-scheduler-navigation"
},
current: {
name: "current",
type: "button",
icon: "calendar",
fillMode: "flat",
text: "placeholder",
attributes: {
"aria-live": "polite",
class: "k-nav-current"
}
},
search: { template: "<span class=\"k-scheduler-search k-textbox k-input\">" + "<input tabindex=\"-1\" autocomplete=\"off\" class=\"k-input-inner k-scheduler-search-input k-input-inner\" />" + `<span class="k-input-suffix">${kendo.ui.icon("search")}</span>` + "</span>" },
refresh: {
name: "refresh",
type: "button",
icon: "arrow-rotate-cw",
showText: "overflow",
attributes: { class: "k-scheduler-refresh" }
},
create: {
name: "create",
type: "button",
icon: "plus",
attributes: { class: "k-create-event" }
},
calendar: {
name: "calendar",
type: "button",
icon: "calendar",
attributes: { class: "k-nav-calendar" }
},
previousMobile: {
name: "previous",
type: "button",
icon: "chevron-left",
showText: "overflow",
attributes: { "ref-nav-prev": "" },
groupClass: "k-scheduler-navigation"
},
nextMobile: {
name: "next",
type: "button",
icon: "chevron-right",
showText: "overflow",
attributes: { "ref-nav-next": "" },
groupClass: "k-scheduler-navigation"
},
currentMobile: { template: "<span class=\"k-scheduler-navigation\">" + "<span class=\"k-nav-current\">" + "<span class=\"k-m-date-format\"></span>" + "<span class=\"k-y-date-format\"></span>" + "</span>" + "</span>" },
view: {
name: "view",
type: "button",
togglable: true,
group: "views"
}
}, defaultDesktopTools = [
[
"today",
"previous",
"next"
],
"current",
{ type: "spacer" }
], defaultMobileToolsFirst = [["calendar"], { type: "spacer" }], defaultMobileToolsSecond = [
"previousMobile",
{ type: "spacer" },
"currentMobile",
{ type: "spacer" },
"nextMobile"
], MOBILEDATERANGEEDITOR = function(container, options) {
var attr = {
name: options.field,
title: options.title
};
var isAllDay = options.model.isAllDay;
var dateTimeValidate = kendo.attr("validate") + "='" + !isAllDay + "'";
var dateValidate = kendo.attr("validate") + "='" + !!isAllDay + "'";
appendTimezoneAttr(attr, options);
appendValidDateValidator(attr, options);
appendDateCompareValidator(attr, options);
$("<input type=\"datetime-local\" required " + kendo.attr("type") + "=\"datetime-local\" " + kendo.attr("bind") + "=\"value:" + options.field + ", invisible:isAllDay\" " + dateTimeValidate + "/>").attr(attr).appendTo(container);
$("<input type=\"date\" required " + kendo.attr("type") + "=\"date\" " + kendo.attr("bind") + "=\"value:" + options.field + ",visible:isAllDay\" " + dateValidate + "/>").attr(attr).appendTo(container);
$("<span " + kendo.attr("for") + "=\"" + options.field + "\" class=\"k-invalid-msg\"/>").hide().appendTo(container);
}, DATERANGEEDITOR = function(container, options) {
var attr = {
name: options.field,
title: options.title
}, isAllDay = options.model.isAllDay, dateTimeValidate = kendo.attr("validate") + "='" + !isAllDay + "' ", dateValidate = kendo.attr("validate") + "='" + !!isAllDay + "' ";
appendTimezoneAttr(attr, options);
appendValidDateValidator(attr, options);
appendDateCompareValidator(attr, options);
$("<input type=\"text\" required " + kendo.attr("type") + "=\"date\"" + " " + kendo.attr("role") + "=\"datetimepicker\" " + kendo.attr("bind") + "=\"value:" + options.field + ",invisible:isAllDay\" " + dateTimeValidate + "/>").attr(attr).appendTo(container);
$("<input type=\"text\" required " + kendo.attr("type") + "=\"date\"" + " " + kendo.attr("role") + "=\"datepicker\" " + kendo.attr("bind") + "=\"value:" + options.field + ",visible:isAllDay\" " + dateValidate + "/>").attr(attr).appendTo(container);
$("<span " + kendo.attr("bind") + "=\"text: " + options.field + "Timezone\"></span>").appendTo(container);
if (options.field === "end") {
$("<span " + kendo.attr("bind") + "=\"text: startTimezone, invisible: endTimezone\"></span>").appendTo(container);
}
$("<span " + kendo.attr("for") + "=\"" + options.field + "\" class=\"k-invalid-msg\"/>").hide().appendTo(container);
}, RECURRENCEEDITOR = function(container, options) {
$("<div " + kendo.attr("bind") + "=\"value:" + options.field + "\" />").attr({ name: options.field }).appendTo(container).kendoRecurrenceEditor({
start: options.model.start,
timezone: options.timezone,
messages: options.messages
});
}, MOBILERECURRENCEEDITOR = function(container, options) {
$("<div " + kendo.attr("bind") + "=\"value:" + options.field + "\" />").attr({ name: options.field }).appendTo(container).kendoMobileRecurrenceEditor({
start: options.model.start,
timezone: options.timezone,
messages: options.messages,
pane: options.pane,
value: options.model[options.field]
});
}, MOBILEISALLDAYEDITOR = function(container, options) {
$("<input type=\"checkbox\" data-role=\"switch\"" + kendo.attr("bind") + "=\"value:" + options.field + "\" />").appendTo(container);
}, ISALLDAYEDITOR = function(container, options) {
$("<input type=\"checkbox\" data-role=\"checkbox\"" + kendo.attr("bind") + "=\"value:" + options.field + "\" data-label=\"" + options.title + "\" />").attr({
id: options.field,
name: options.field,
title: options.title ? options.title : options.field
}).appendTo(container);
}, MOBILETIMEZONEPOPUP = function(container, options) {
var text = timezoneButtonText(options.model, options.messages.noTimezone);
$("<span class=\"k-timezone-label\"></span>").text(text).appendTo(container);
$(kendo.ui.icon("arrow-chevron-right")).appendTo(container);
container.closest("li.k-item label").on(CLICK, options.click);
}, TIMEZONEPOPUP = function(container, options) {
$("<a href=\"#\" class=\"k-button\" data-bind=\"invisible:isAllDay\"><span class=\"k-button-text\">" + options.messages.timezoneEditorButton + "</span></a>").on(CLICK, options.click).appendTo(container);
}, MOBILETIMEZONEEDITOR = function(container, options) {
$("<div class=\"k-mobiletimezoneeditor\" " + kendo.attr("bind") + "=\"value:" + options.field + "\" />").attr({ name: options.field }).appendTo(container).kendoMobileTimezoneEditor({ optionLabel: options.noTimezone });
}, TIMEZONEEDITOR = function(container, options) {
var visible = options.visible || options.visible === undefined;
$("<div " + kendo.attr("bind") + "=\"value:" + options.field + "\" />").attr({ name: options.field }).toggle(visible).appendTo(container).kendoTimezoneEditor({
optionLabel: options.noTimezone,
title: options.title
});
};
function timezoneButtonText(model, message) {
message = message || "";
if (model.startTimezone) {
message = model.startTimezone;
if (model.endTimezone) {
message += " | " + model.endTimezone;
}
}
return message;
}
function appendTimezoneAttr(attrs, options) {
var timezone = options.timezone;
if (timezone) {
attrs[kendo.attr("timezone")] = timezone;
}
}
function appendValidDateValidator(attrs, options) {
var validationRules = options.model.fields[options.field].validation;
if (validationRules) {
var validDateRule = validationRules.validDateValidator;
if (validDateRule && isPlainObject(validDateRule) && validDateRule.message) {
attrs[kendo.attr("validDate-msg")] = validDateRule.message;
}
}
}
function appendDateCompareValidator(attrs, options) {
var validationRules = options.model.fields[options.field].validation;
if (validationRules) {
var dateCompareRule = validationRules.dateCompare;
if (dateCompareRule && isPlainObject(dateCompareRule) && dateCompareRule.message) {
attrs[kendo.attr("dateCompare-msg")] = dateCompareRule.message;
}
}
}
function wrapDataAccess(originalFunction, timezone) {
return function(data) {
data = originalFunction(data);
convertData(data, "apply", timezone);
return data || [];
};
}
function wrapDataSerialization(originalFunction, timezone) {
return function(data) {
if (data) {
if (toString.call(data) !== "[object Array]" && !(data instanceof kendo.data.ObservableArray)) {
data = [data];
}
}
convertData(data, "remove", timezone, true);
data = originalFunction(data);
return data || [];
};
}
function convertData(data, method, timezone, removeUid) {
var event, idx, length, startOffset, endOffset;
data = data || [];
for (idx = 0, length = data.length; idx < length; idx++) {
event = data[idx];
startOffset = event.start ? event.start.getTimezoneOffset() : null;
endOffset = event.start ? event.end.getTimezoneOffset() : null;
if (removeUid) {
if (event.startTimezone || event.endTimezone) {
if (timezone) {
event.start = kendo.timezone.convert(event.start, event.startTimezone || event.endTimezone, timezone);
event.end = kendo.timezone.convert(event.end, event.endTimezone || event.startTimezone, timezone);
event.start = kendo.timezone[method](event.start, timezone);
event.end = kendo.timezone[method](event.end, timezone);
} else {
event.start = kendo.timezone[method](event.start, event.startTimezone || event.endTimezone);
event.end = kendo.timezone[method](event.end, event.endTimezone || event.startTimezone);
}
} else if (timezone) {
event.start = kendo.timezone[method](event.start, timezone);
event.end = kendo.timezone[method](event.end, timezone);
}
} else {
if (event.startTimezone || event.endTimezone) {
event.start = kendo.timezone[method](event.start, event.startTimezone || event.endTimezone);
event.end = kendo.timezone[method](event.end, event.endTimezone || event.startTimezone);
if (timezone) {
event.start = kendo.timezone.convert(event.start, event.startTimezone || event.endTimezone, timezone);
event.end = kendo.timezone.convert(event.end, event.endTimezone || event.startTimezone, timezone);
}
} else if (timezone) {
event.start = kendo.timezone[method](event.start, timezone);
event.end = kendo.timezone[method](event.end, timezone);
}
}
if (removeUid) {
delete event.uid;
}
if (method === "remove" && event.start && startOffset && startOffset !== event.start.getTimezoneOffset()) {
event.start = new Date(event.start.getTime() + (startOffset - event.start.getTimezoneOffset()) * 6e4);
}
if (method === "remove" && event.end && endOffset && endOffset !== event.end.getTimezoneOffset()) {
event.end = new Date(event.end.getTime() + (endOffset - event.end.getTimezoneOffset()) * 6e4);
}
}
return data;
}
function getOccurrenceByUid(data, uid) {
var length = data.length, idx = 0, event;
for (; idx < length; idx++) {
event = data[idx];
if (event.uid === uid) {
return event;
}
}
}
var SchedulerDataReader = kendo.Class.extend({
init: function(schema, reader) {
var timezone = schema.timezone;
this.reader = reader;
if (reader.model) {
this.model = reader.model;
}
this.timezone = timezone;
this.data = wrapDataAccess(this.data.bind(this), timezone);
this.serialize = wrapDataSerialization(this.serialize.bind(this), timezone);
},
errors: function(data) {
return this.reader.errors(data);
},
parse: function(data) {
return this.reader.parse(data);
},
data: function(data) {
return this.reader.data(data);
},
total: function(data) {
return this.reader.total(data);
},
groups: function(data) {
return this.reader.groups(data);
},
aggregates: function(data) {
return this.reader.aggregates(data);
},
serialize: function(data) {
return this.reader.serialize(data);
}
});
function applyZone(date, fromZone, toZone) {
if (toZone) {
date = kendo.timezone.convert(date, fromZone, toZone);
} else {
date = kendo.timezone.remove(date, fromZone);
}
return date;
}
function validDateValidator(input) {
if (input.filter("[name=start]").length && input.filter("[title=Start]").length || input.filter("[name=end]").length && input.filter("[title=End]").length || input.filter(".k-recur-until").length) {
var date;
var picker = kendo.widgetInstance(input, kendo.ui);
if (picker) {
date = kendo.parseDate(input.val(), picker.options.format);
return !!date && picker.value();
} else {
date = kendo.parseDate(input.val());
return !!date;
}
}
return true;
}
function dateCompareValidator(input) {
if (input.filter("[name=end]").length) {
var container = input.closest(".k-scheduler-edit-form");
var startInput = container.find("[name=start]:visible");
var endInput = container.find("[name=end]:visible");
if (endInput[0] && startInput[0]) {
var start, end;
var startPicker = kendo.widgetInstance(startInput, kendo.ui);
var endPicker = kendo.widgetInstance(endInput, kendo.ui);
var editable = container.data("kendoEditable");
var model = editable ? editable.options.model : null;
if (startPicker && endPicker) {
start = startPicker.value();
end = endPicker.value();
} else {
start = kendo.parseDate(startInput.val());
end = kendo.parseDate(endInput.val());
}
if (start && end) {
if (model) {
var timezone = startInput.attr(kendo.attr("timezone"));
var startTimezone = model.startTimezone;
var endTimezone = model.endTimezone;
startTimezone = startTimezone || endTimezone;
endTimezone = endTimezone || startTimezone;
if (startTimezone) {
start = applyZone(start, startTimezone, timezone);
end = applyZone(end, endTimezone, timezone);
}
}
return start <= end;
}
}
}
return true;
}
function untilDateCompareValidator(input) {
var untilPicker, until, container, startInput, start, startPicker;
if (input.filter(".k-recur-until").length) {
untilPicker = kendo.widgetInstance(input, kendo.ui);
until = untilPicker.value();
container = input.closest(".k-scheduler-edit-form");
startInput = container.find("[name=start]:visible");
if (startInput[0]) {
startPicker = kendo.widgetInstance(startInput, kendo.ui);
if (startPicker) {
start = startPicker.value();
} else {
start = kendo.parseDate(startInput.val());
}
if (start && until) {
return start <= until;
}
}
}
return true;
}
var SchedulerEvent = kendo.data.Model.define({
init: function(value) {
var that = this;
kendo.data.Model.fn.init.call(that, value);
that._defaultId = that.defaults[that.idField];
},
_time: function(field) {
var date = this[field];
var fieldTime = "_" + field + "Time";
if (this[fieldTime]) {
return this[fieldTime] - kendo.date.toUtcTime(kendo.date.getDate(date));
}
return getMilliseconds(date);
},
_date: function(field) {
var fieldTime = "_" + field + "Time";
if (this[fieldTime]) {
return this[fieldTime] - this._time(field);
}
return kendo.date.getDate(this[field]);
},
clone: function(options, updateUid) {
var uid = this.uid, event = new this.constructor($.extend({}, this.toJSON(), options));
if (!updateUid) {
event.uid = uid;
}
return event;
},
duration: function() {
var end = this.end;
var start = this.start;
var offset = (end.getTimezoneOffset() - start.getTimezoneOffset()) * kendo.date.MS_PER_MINUTE;
return end - start - offset;
},
expand: function(start, end, zone) {
return recurrence ? recurrence.expand(this, start, end, zone) : [this];
},
update: function(eventInfo) {
for (var field in eventInfo) {
this.set(field, eventInfo[field]);
}
if (this._startTime) {
this.set("_startTime", kendo.date.toUtcTime(this.start));
}
if (this._endTime) {
this.set("_endTime", kendo.date.toUtcTime(this.end));
}
},
isMultiDay: function() {
return this.isAllDay || this.duration() >= kendo.date.MS_PER_DAY;
},
isException: function() {
return !this.isNew() && this.recurrenceId;
},
isOccurrence: function() {
return this.isNew() && this.recurrenceId;
},
isRecurring: function() {
return !!(this.recurrenceRule || this.recurrenceId);
},
isRecurrenceHead: function() {
return !!(this.id && this.recurrenceRule);
},
toOccurrence: function(options) {
options = $.extend(options, {
recurrenceException: null,
recurrenceRule: null,
recurrenceId: this.id || this.recurrenceId
});
options[this.idField] = this.defaults[this.idField];
return this.clone(options, true);
},
toJSON: function() {
var obj = kendo.data.Model.fn.toJSON.call(this);
obj.uid = this.uid;
delete obj._startTime;
delete obj._endTime;
return obj;
},
shouldSerialize: function(field) {
return kendo.data.Model.fn.shouldSerialize.call(this, field) && field !== "_defaultId";
},
set: function(key, value) {
var isAllDay = this.isAllDay || false;
kendo.data.Model.fn.set.call(this, key, value);
if (key == "isAllDay" && value != isAllDay) {
var start = kendo.date.getDate(this.start);
var end = new Date(this.end);
var milliseconds = kendo.date.getMilliseconds(end);
if (milliseconds === 0 && value) {
milliseconds = MS_PER_DAY;
}
this.set("start", start);
if (value === true) {
kendo.date.setTime(end, -milliseconds);
if (end < start) {
end = start;
}
} else {
kendo.date.setTime(end, MS_PER_DAY - milliseconds);
}
this.set("end", end);
}
},
id: "id",
fields: {
id: { type: "number" },
title: {
defaultValue: "",
type: "string"
},
start: {
type: "date",
validation: {
required: true,
validDate: { value: validDateValidator }
}
},
startTimezone: { type: "string" },
end: {
type: "date",
validation: {
required: true,
validDate: { value: validDateValidator },
dateCompare: { value: dateCompareValidator }
}
},
endTimezone: { type: "string" },
recurrenceRule: {
defaultValue: "",
type: "string",
validation: {
validDate: { value: validDateValidator },
untilDateCompare: { value: untilDateCompareValidator }
}
},
recurrenceException: {
defaultValue: "",
type: "string"
},
isAllDay: {
type: "boolean",
defaultValue: false
},
description: { type: "string" }
}
});
var SchedulerDataSource = DataSource.extend({
init: function(options) {
DataSource.fn.init.call(this, extend(true, {}, { schema: {
modelBase: SchedulerEvent,
model: SchedulerEvent
} }, options));
this.reader = new SchedulerDataReader(this.options.schema, this.reader);
},
expand: function(start, end) {
var data = this.view(), filter = {}, endOffset;
if (start && end) {
endOffset = end.getTimezoneOffset();
end = new Date(end.getTime() + MS_PER_DAY - 1);
if (end.getTimezoneOffset() !== endOffset) {
end = kendo.timezone.apply(end, endOffset);
}
filter = {
logic: "or",
filters: [{
logic: "and",
filters: [
{
field: "start",
operator: "gte",
value: start
},
{
field: "end",
operator: "gte",
value: start
},
{
field: "start",
operator: "lte",
value: end
}
]
}, {
logic: "and",
filters: [{
field: "start",
operator: "lte",
value: new Date(start.getTime() + MS_PER_DAY - 1)
}, {
field: "end",
operator: "gte",
value: start
}]
}]
};
data = new kendo.data.Query(expandAll(data, start, end, this.reader.timezone)).filter(filter).toArray();
}
return data;
},
cancelChanges: function(model) {
if (model && model.isOccurrence()) {
this._removeExceptionDate(model);
}
DataSource.fn.cancelChanges.call(this, model);
},
insert: function(index, model) {
if (!model) {
return;
}
if (!(model instanceof SchedulerEvent)) {
var eventInfo = model;
model = this._createNewModel();
model.accept(eventInfo);
}
if (!this._pushCreated && model.isRecurrenceHead() || model.recurrenceId) {
model = model.recurrenceId ? model : model.toOccurrence();
this._addExceptionDate(model);
}
return DataSource.fn.insert.call(this, index, model);
},
pushCreate: function(items) {
this._pushCreated = true;
DataSource.fn.pushCreate.call(this, items);
this._pushCreated = false;
},
remove: function(model) {
if (model.isRecurrenceHead()) {
this._removeExceptions(model);
} else if (model.isRecurring()) {
this._addExceptionDate(model);
}
return DataSource.fn.remove.call(this, model);
},
_removeExceptions: function(model) {
var data = this.data().slice(0), item = data.shift(), id = model.id;
while (item) {
if (item.recurrenceId === id) {
DataSource.fn.remove.call(this, item);
}
item = data.shift();
}
model.set(RECURRENCE_EXCEPTION, "");
},
_removeExceptionDate: function(model) {
if (model.recurrenceId) {
var head = this.get(model.recurrenceId);
if (head) {
var start = model.defaults.start;
var replaceRegExp = new RegExp("(\\" + EXCEPTION_SEPARATOR + "?)" + recurrence.toExceptionString(start, this.reader.timezone));
var recurrenceException = (head.recurrenceException || "").replace(OLD_EXCEPTION_SEPARATOR_REGEXP, EXCEPTION_SEPARATOR).replace(/\,$/, "");
if (replaceRegExp.test(recurrenceException)) {
head.set(RECURRENCE_EXCEPTION, recurrenceException.replace(replaceRegExp, ""));
} else {
start = model.start;
replaceRegExp = new RegExp("(\\" + EXCEPTION_SEPARATOR + "?)" + recurrence.toExceptionString(start, this.reader.timezone));
head.set(RECURRENCE_EXCEPTION, recurrenceException.replace(replaceRegExp, ""));
}
}
}
},
_addExceptionDate: function(model) {
var start = model.start;
var zone = this.reader.timezone;
var head = this.get(model.recurrenceId);
var recurrenceException = (head.recurrenceException || "").replace(OLD_EXCEPTION_SEPARATOR_REGEXP, EXCEPTION_SEPARATOR).replace(/\,$/, "");
if (!recurrence.isException(recurrenceException, start, zone)) {
var newException = recurrence.toExceptionString(start, zone);
model.defaults.start = start;
head.set(RECURRENCE_EXCEPTION, recurrenceException + (recurrenceException && newException ? EXCEPTION_SEPARATOR : "") + newException);
}
}
});
function expandAll(events, start, end, zone) {
var length = events.length, data = [], idx = 0;
for (; idx < length; idx++) {
data = data.concat(events[idx].expand(start, end, zone));
}
return data;
}
SchedulerDataSource.create = function(options) {
if (isArray(options) || options instanceof kendo.data.ObservableArray) {
options = { data: options };
}
var dataSource = options || {}, data = dataSource.data;
dataSource.data = data;
if (!(dataSource instanceof SchedulerDataSource) && dataSource instanceof kendo.data.DataSource) {
throw new Error("Incorrect DataSource type. Only SchedulerDataSource instances are supported");
}
return dataSource instanceof SchedulerDataSource ? dataSource : new SchedulerDataSource(dataSource);
};
extend(true, kendo.data, {
SchedulerDataSource,
SchedulerDataReader,
SchedulerEvent
});
var defaultCommands = {
update: {
text: "Save",
className: "k-button-primary k-scheduler-update"
},
canceledit: {
text: "Cancel",
className: "k-scheduler-cancel"
},
destroy: {
text: "Delete",
icon: "trash",
imageClass: "k-i-trash",
className: "k-button-primary k-scheduler-delete",
iconClass: "k-icon"
}
};
function trimOptions(options, overrideOptions) {
delete options.name;
delete options.prefix;
delete options.remove;
delete options.edit;
delete options.add;
delete options.navigate;
for (var key in overrideOptions) {
options[key] = overrideOptions[key];
}
return options;
}
function descriptionEditor(options) {
var attr = createValidationAttributes(options.model, options.field);
return function(container, model) {
$("<textarea name=\"description\" class=\"k-input-inner\" title=\"" + model.title + "\"/>").attr(attr).appendTo(container).wrap("<span class=\"k-input k-textarea\"></span>");
};
}
function createValidationAttributes(model, field) {
var modelField = (model.fields || model)[field];
var specialRules = [
"url",
"email",
"number",
"date",
"boolean"
];
var validation = modelField ? modelField.validation : {};
var datatype = kendo.attr("type");
var inArray = $.inArray;
var ruleName;
var rule;
var attr = {};
for (ruleName in validation) {
rule = validation[ruleName];
if (inArray(ruleName, specialRules) >= 0) {
attr[datatype] = ruleName;
} else if (!kendo.isFunction(rule)) {
attr[ruleName] = isPlainObject(rule) ? rule.value || ruleName : rule;
}
attr[kendo.attr(ruleName + "-msg")] = rule.message;
}
return attr;
}
function filterResourceEditorData(editor, parentValue, parentValueField, valueField) {
var editorValue = editor.value(), isMs = Array.isArray(editorValue), valueArray;
if (isMs) {
valueArray = JSON.parse(JSON.stringify(editorValue));
} else {
valueArray = [editorValue.toString()];
}
editor.dataSource.data().forEach(function(item) {
if (item[parentValueField] === null || item[parentValueField] === undefined || item[parentValueField] == parentValue) {
item.set(DISABLED, false);
} else {
var currentValue = item.get(valueField);
item.set(DISABLED, true);
if (valueArray.indexOf(currentValue) >= 0 || valueArray.indexOf(currentValue.toString()) >= 0) {
if (isMs) {
valueArray.splice(valueArray.indexOf(currentValue), 1);
} else {
editor.value(null);
editor.trigger(CHANGE);
}
}
}
});
if (isMs && valueArray.length < editorValue.length) {
editor.value(valueArray);
editor.trigger(CHANGE);
}
}
function bindParentValueChangeHandler(container, currentEditor, resource, parent) {
var parentElement = container.closest(".k-edit-form-container").find("[data-" + kendo.ns + "bind='value:" + parent + "']");
var parentWidget = parentElement.getKendoDropDownList();
if (parentWidget) {
parentWidget.bind(CHANGE, function(ev) {
var parentValue = ev.sender.value();
filterResourceEditorData(currentEditor, parentValue, resource.dataParentValueField, resource.dataValueField);
});
} else {
parentElement.on(CHANGE, function(ev) {
var parentValue = ev.target.value;
filterResourceEditorData(currentEditor, parentValue, resource.dataParentValueField, resource.dataValueField);
});
}
}
function filterMobileResourceEditorData(resource, currentEditor, parentSelectedValue) {
var options = currentEditor.find(OPTION), editorValue = currentEditor.val(), isMs = Array.isArray(editorValue), valueArray;
if (isMs) {
valueArray = JSON.parse(JSON.stringify(editorValue));
} else {
valueArray = [editorValue];
}
resource.dataSource.view().forEach(function(item, index) {
var itemParentValue = kendo.getter(resource.dataParentValueField)(item);
var valid = itemParentValue === null || itemParentValue === undefined || itemParentValue == parentSelectedValue;
if (valid) {
options[index].removeAttribute(DISABLED);
} else {
options[index].setAttribute(DISABLED, DISABLED);
var currentValue = "" + item.get(resource.dataValueField);
if (valueArray.indexOf(currentValue) >= 0) {
if (isMs) {
valueArray.splice(valueArray.indexOf(currentValue), 1);
} else {
currentEditor.val(null);
currentEditor.trigger(CHANGE);
}
}
}
});
if (isMs && valueArray.length < editorValue.length) {
currentEditor.val(valueArray);
currentEditor.trigger(CHANGE);
}
}
function dropDownResourceEditor(resource, model, parent) {
var attr = createValidationAttributes(model, resource.field);
return function(container) {
var currentEditor;
if (parent) {
setTimeout(function() {
filterResourceEditorData(currentEditor, model[parent], resource.dataParentValueField, resource.dataValueField);
bindParentValueChangeHandler(container, currentEditor, resource, parent);
});
}
currentEditor = $(kendo.format("<select aria-labelledby=\"" + resource.field + "_label\" data-{0}bind=\"value:{1}\" title=\"" + model.title + "\">", kendo.ns, resource.field)).appendTo(container).attr(attr).kendoDropDownList({
dataTextField: resource.dataTextField,
dataValueField: resource.dataValueField,
dataSource: resource.dataSource.data(),
valuePrimitive: resource.valuePrimitive,
optionLabel: "None",
template: (data) => `<span ${data.disabled ? "data-disabled" : ""}><span class="k-scheduler-mark" ${kendo.attr("style-background-color")}="${data[resource.dataColorField] || "none"}"></span>${data[resource.dataTextField]}</span>`,
select: function(e) {
if (e.dataItem && e.dataItem.disabled) {
e.preventDefault();
}
},
dataBound: function(e) {
let options = e.sender.list.find("li");
for (let i = 0; i < options.length; i++) {
var element = options.eq(i);
if (element.find("[data-disabled]").length > 0) {
element.addClass("k-disabled");
}
}
}
}).data("kendoDropDownList");
};
}
function dropDownResourceEditorMobile(resource, model, parent) {
var attr = createValidationAttributes(model, resource.field);
return function(container) {
var options = "";
var view = resource.dataSource.view();
for (var idx = 0, length = view.length; idx < length; idx++) {
options += kendo.format("<option value=\"{0}\">{1}</option>", kendo.getter(resource.dataValueField)(view[idx]), kendo.getter(resource.dataTextField)(view[idx]));
}
var currentEditor = $(kendo.format("<select aria-labelledby=\"" + resource.field + "_label\" data-{0}bind=\"value:{1}\">{2}</select>", kendo.ns, resource.field, options)).appendTo(container).attr(attr);
if (parent) {
setTimeout(function() {
var parentElement = container.closest(".k-stretched-view").find("[data-" + kendo.ns + "bind='value:" + parent + "']");
var parentSelectedValue = model[parent];
filterMobileResourceEditorData(resource, currentEditor, parentSelectedValue);
parentElement.on(CHANGE, function(ev) {
var parentValue = ev.target.value;
filterMobileResourceEditorData(resource, currentEditor, parentValue);
});
});
}
};
}
function multiSelectResourceEditor(resource, model, parent) {
var attr = createValidationAttributes(model, resource.field);
return function(container) {
var currentEditor;
if (parent) {
setTimeout(function() {
filterResourceEditorData(currentEditor, model[parent], resource.dataParentValueField, resource.dataValueField);
bindParentValueChangeHandler(container, currentEditor, resource, parent);
});
}
currentEditor = $(kendo.format("<select aria-labelledby=\"" + resource.field + "_label\" data-{0}bind=\"value:{1}\">", kendo.ns, resource.field)).appendTo(container).attr(attr).kendoMultiSelect({
dataTextField: resource.dataTextField,
dataValueField: resource.dataValueField,
dataSource: resource.dataSource.data(),
valuePrimitive: resource.valuePrimitive,
itemTemplate: (data) => `<span ${data.disabled ? "data-disabled" : ""}><span class="k-scheduler-mark" ${kendo.attr("style-background-color")}="${data[resource.dataColorField] || "none"}"></span>${data[resource.dataTextField]}</span>`,
tagTemplate: (data) => `<span class="k-scheduler-mark" ${kendo.attr("style-background-color")}="${data[resource.dataColorField] || "none"}"></span>${data[resource.dataTextField]}`,
select: function(e) {
if (e.dataItem && e.dataItem.disabled) {
e.preventDefault();
}
},
dataBound: function(e) {
let options = e.sender.list.find("li");
for (let i = 0; i < options.length; i++) {
let element = options.eq(i);
if (element.find("[data-disabled]").length > 0) {
element.addClass("k-disabled");
}
}
}
}).data("kendoMultiSelect");
};
}
function multiSelectResourceEditorMobile(resource, model, parent) {
var attr = createValidationAttributes(model, resource.field);
return function(container) {
var options = "";
var view = resource.dataSource.view();
for (var idx = 0, length = view.length; idx < length; idx++) {
options += kendo.format("<option value=\"{0}\">{1}</option>", kendo.getter(resource.dataValueField)(view[idx]), kendo.getter(resource.dataTextField)(view[idx]));
}
var currentEditor = $(kendo.format("<select aria-labelledby=\"" + resource.field + "_label\" data-{0}bind=\"value:{1}\" multiple=\"multiple\">{2}</select>", kendo.ns, resource.field, options)).appendTo(container).attr(attr);
if (parent) {
setTimeout(function() {
var parentElement = container.closest(".k-stretched-view").find("[data-" + kendo.ns + "bind='value:" + parent + "']");
var parentSelectedValue = model[parent];
filterMobileResourceEditorData(resource, currentEditor, parentSelectedValue);
parentElement.on(CHANGE, function(ev) {
var parentValue = ev.target.value;
filterMobileResourceEditorData(resource, currentEditor, parentValue);
});
});
}
};
}
function moveEventRange(event, distance) {
var duration = event.end.getTime() - event.start.getTime();
var start = new Date(event.start.getTime());
kendo.date.setTime(start, distance);
var end = new Date(start.getTime());
kendo.date.setTime(end, duration, true);
return {
start,
end
};
}
var editors = {
mobile: {
dateRange: MOBILEDATERANGEEDITOR,
timezonePopUp: MOBILETIMEZONEPOPUP,
timezone: MOBILETIMEZONEEDITOR,
recurrence: MOBILERECURRENCEEDITOR,
description: descriptionEditor,
multipleResources: multiSelectResourceEditorMobile,
resources: dropDownResourceEditorMobile,
isAllDay: MOBILEISALLDAYEDITOR
},
desktop: {
dateRange: DATERANGEEDITOR,
timezonePopUp: TIMEZONEPOPUP,
timezone: TIMEZONEEDITOR,
recurrence: RECURRENCEEDITOR,
description: descriptionEditor,
multipleResources: multiSelectResourceEditor,
resources: dropDownResourceEditor,
isAllDay: ISALLDAYEDITOR
}
};
var Editor = kendo.Observable.extend({
init: function(element, options) {
kendo.Observable.fn.init.call(this);
this.element = element;
this.options = extend(true, {}, this.options, options);
this.createButton = this.options.createButton;
this.toggleDateValidationHandler = this._toggleDateValidation.bind(this);
},
_toggleDateValidation: function(e) {
if (e.field == "isAllDay") {
var container = this.container, isAllDay = this.editable.options.model.isAllDay, bindAttribute = kendo.attr("bind"), element, isDateTimeInput, shouldValidate;
container.find("[" + bindAttribute + "*=end],[" + bindAttribute + "*=start]").each(function() {
element = $(this);
if (valueStartEndBoundRegex.test(element.attr(bindAttribute))) {
isDateTimeInput = element.is("[" + kendo.attr("role") + "=datetimepicker],[type*=datetime]");
shouldValidate = isAllDay !== isDateTimeInput;
element.attr(kendo.attr("validate"), shouldValidate.toString());
}
});
}
},
fields: function(editors, model) {
var that = this;
var messages = that.options.messages;
var timezone = that.options.timezone;
var click = function(e) {
e.preventDefault();
that._initTimezoneEditor(model, this);
};
var fields = [
{
field: "title",
title: messages.editor.title
},
{
field: "start",
title: messages.editor.start,
editor: editors.dateRange,
timezone
},
{
field: "end",
title: messages.editor.end,
editor: editors.dateRange,
timezone
},
{
field: "isAllDay",
title: messages.editor.allDayEvent,
editor: editors.isAllDay
}
];
var checkHierarchical = function(item) {
return !!item[resource.dataParentValueField];
};
if (kendo.timezone.windows_zones) {
fields.push({
field: "timezone",
title: messages.editor.timezone,
editor: editors.timezonePopUp,
click,
messages: messages.editor,
model
});
fields.push({
field: "startTimezone",
title: messages.editor.startTimezone,
editor: editors.timezone,
noTimezone: messages.editor.noTimezone
});
fields.push({
field: "endTimezone",
title: messages.editor.endTimezone,
editor: editors.timezone,
noTimezone: messages.editor.noTimezone
});
}
if (!model.recurrenceId) {
fields.push({
field: "recurrenceRule",
title: messages.editor.repeat,
editor: editors.recurrence,
timezone,
messages: messages.recurrenceEditor,
pane: this.pane
});
}
if ("description" in model) {
fields.push({
field: "description",
title: messages.editor.description,
editor: editors.description({
model,
field: "description"
})
});
}
for (var resourceIndex = 0; resourceIndex < this.options.resources.length; resourceIndex++) {
var resource = this.options.resources[resourceIndex];
var resourceView = resource.dataSource.view();
var hasParent = resourceView.some(checkHierarchical);
var parentResource, parent;
if (hasParent) {
parentResource = this.options.resources[resourceIndex - 1];
if (parentResource) {
parent = parentResource.field;
}
}
fields.push({
field: resource.field,
title: resource.title,
editor: resource.multiple ? editors.multipleResources(resource, model, parent) : editors.resources(resource, model, parent)
});
}
return fields;
},
end: function() {
return this.editable.end();
},
_buildDesktopEditTemplate: function(model, fields, editableFields) {
var messages = this.options.messages;
const startTimezone = `<div class="k-popup-edit-form k-scheduler-edit-form k-scheduler-timezones" ${kendo.attr("style-display")}="none">` + "<div class=\"k-form\">" + "<div class=\"k-form-field\"><div class=\"k-form-field-wrap\">" + kendo.html.renderCheckBox($("<input class=\"k-timezone-toggle\"/>"), { label: messages.editor.separateTimezones }) + "</div></div>";
const editableField = (fieldName) => {
const isEditable = !model.editable || model.editable(fieldName);
if (isEditable) {
return `<div ${kendo.attr("container-for")}="${fieldName}" class="k-form-field-wrap"></div>`;
} else {
return `<div class="k-form-field-wrap">${fieldName && kendo.getter(fieldName)(model) || ""}</div>`;
}
};
const generateFields = (field) => {
const fieldName = field.field;
const fieldTitle = field.title;
const modelField = model.fields[fieldName];
const isEditable = !model.editable || model.editable(fieldName);
if (isEditable) {
editableFields.push(field);
}
return (fieldName === "startTimezone" ? startTimezone : "") + (modelField && modelField.type === "boolean" ? `<div class="k-form-field">${editableField(fieldName)}</div>` : `<div class="k-form-field"><label class="k-label k-form-label" for="${fieldName}" id="${fieldName}_label">${fieldTitle || fieldName || ""}</label>${editableField(fieldName)}</div>`) + (fieldName === "endTimezone" ? this._createEndTimezoneButton() : "");
};
return `<div class="k-form">` + fields.map(generateFields).join("") + `</div>`;
},
_buildMobileEditTemplate: function(model, fields, editableFields) {
var messages = this.options.messages;
const startTimezone = `<div class="k-popup-edit-form k-scheduler-edit-form k-scheduler-timezones" ${kendo.attr("style-display")}="none">` + "<ul class=\"k-listgroup k-listgroup-flush\">" + "<li class=\"k-item k-listgroup-item\">" + "<label class=\"k-label k-listgroup-form-row\">" + "<span class=\"k-item-title k-listgroup-form-row\">" + encode(messages.editor.separateTimezones) + "</span>" + "<span class=\"k-listgroup-form-field-wrapper\">" + "<input class=\"k-timezone-toggle\" data-role=\"switch\" type=\"checkbox\" />" + "</span>" + "</label>" + "</li>";
const editableField = (field) => {
const fieldName = field.field;
const fieldTitle = field.title;
const isEditable = !model.editable || model.editable(fieldName);
let midPart;
if (isEditable) {
midPart = `${fieldName === "timezone" ? `<label class="k-label k-listgroup-form-row" data-bind="css: { k-disabled: isAllDay }">` : `<label class="k-label k-listgroup-form-row">`}` + `<span class="k-item-title k-listgroup-form-field-label">${fieldTitle || fieldName || ""}</span>` + `<div class="k-listgroup-form-field-wrapper" ${kendo.attr("container-for")}="${fieldName}"></div>`;
} else {
midPart = `<li class="k-item k-listgroup-item">` + `<label class="k-label k-no-click k-listgroup-form-row">` + `<span class="k-item-title k-listgroup-form-field-label">${fieldTitle || fieldName || ""}</span>` + `<span class="k-no-editor k-listgroup-form-field-wrapper">${fieldName && kendo.getter(fieldName)(model) || ""}</span>`;
}
return `<li class="k-item k-listgroup-item">` + midPart + `</label></li>`;
};
const generateFields = (field) => {
const fieldName = field.field;
const isEditable = !model.editable || model.editable(fieldName);
if (isEditable) {
editableFields.push(field);
}
return (fieldName === "timezone" || fieldName === "recurrenceRule" ? "</ul><ul class=\"k-listgroup k-listgroup-flush\">" : "") + (fieldName === "startTimezone" ? startTimezone : "") + editableField(field) + (fieldName === "recurrenceRule" ? "</ul><ul class=\"k-listgroup k-listgroup-flush\">" : "") + (fieldName === "endTimezone" ? "</ul></div>" : "");
};
const result = `<ul class="k-listgroup k-listgroup-flush">` + fields.map(generateFields).join("") + `</ul>`;
return result;
},
_buildEditTemplate: function(model, fields, editableFields, isMobile) {
var settings = extend({}, kendo.Template, this.options.templateSettings);
var template = this.options.editable.template;
var html = "";
if (template) {
if (typeof template === STRING) {
template = kendo.unescape(template);
}
html += kendo.template(template, settings)(model);
} else if (isMobile) {
html += "<div data-role=\"content\">" + this._buildMobileEditTemplate(model, fields, editableFields) + "</div>";
} else {
html += this._buildDesktopEditTemplate(model, fields, editableFields);
}
return html;
},
_createEndTimezoneButton: function() {
return "</ul></div>";
},
_revertTimezones: function(model) {
model.set("startTimezone", this._startTimezone);
model.set("endTimezone", this._endTimezone);
delete this._startTimezone;
delete this._endTimezone;
}
});
var MobileEditor = Editor.extend({
init: function() {
Editor.fn.init.apply(this, arguments);
this.pane = kendo.Pane.wrap(this.element, { viewEngine: { viewOptions: {
renderOnInit: true,
wrap: false,
wrapInSections: true,
detachOnHide: false,
detachOnDestroy: false
} } });
this.pane.element.parent().css("height", this.options.height);
this.view = this.pane.view();
},
options: { animations: {
left: "slide",
right: "slide:right"
} },
destroy: function() {
this.close();
this.unbind();
this.pane.destroy();
},
_initTimezoneEditor: function(model) {
var that = this;
var pane = that.pane;
var messages = that.options.messages;
var timezoneView = that.timezoneView;
var container = timezoneView ? timezoneView.content.find(".k-scheduler-timezones") : that.container.find(".k-scheduler-timezones");
var kSwitch = container.find("input.k-timezone-toggle").data("kendoSwitch");
var endTimezoneRow = container.find("li.k-item:not(.k-zonepicker)").last();
var startTimezoneChange = function(e) {
if (e.field === "startTimezone") {
var value = model.startTimezone;
kSwitch.enable(value);
if (!value) {
endTimezoneRow.hide();
model.set("endTimezone", "");
kSwitch.value(false);
}
}
};
that._startTimezone = model.startTimezone || "";
that._endTimezone = model.endTimezone || "";
if (!timezoneView) {
var html = "<div data-role=\"view\" class=\"k-popup-edit-form k-scheduler-edit-form\">" + "<div data-role=\"header\" class=\"k-appbar k-appbar-primary\">" + kendo.html.renderButton(`<button class="k-header-cancel k-scheduler-cancel" title="${messages.cancel}" aria-label="${messages.cancel}"></button>`, {
fillMode: "flat",
icon: "chevron-left"
}) + "<span class=\"k-spacer\"></span>" + `<span>${encode(messages.editor.timezoneTitle)}</span>` + "<span class=\"k-spacer\"></span>" + encode(messages.editor.timezoneTitle) + kendo.html.renderButton(`<button class="k-header-done k-scheduler-update" title="${messages.save}" aria-label="${messages.save}"></button>`, {
fillMode: "flat",
icon: "check"
}) + "</div><div data-role=\"content\"></div>";
this.timezoneView = timezoneView = pane.append(html);
timezoneView.contentElement.append(container.show());
timezoneView.element.on(CLICK + NS, ".k-scheduler-cancel, .k-scheduler-update", function(e) {
e.preventDefault();
e.stopPropagation();
if ($(this).hasClass("k-scheduler-cancel")) {
that._revertTimezones(model);
}
var editView = that._editPane;
var text = timezoneButtonText(model, messages.editor.noTimezone);
editView.content.find(".k-timezone-label").text(text);
pane.navigate(editView, that.options.animations.right);
});
kSwitch.bind(CHANGE, function(ev) {
endTimezoneRow.toggle(ev.checked);
model.set("endTimezone", "");
});
model.bind(CHANGE, startTimezoneChange);
}
kSwitch.value(!!model.endTimezone);
kSwitch.enable(!!model.startTimezone);
if (model.endTimezone) {
endTimezoneRow.show();
} else {
endTimezoneRow.hide();
}
pane.navigate(timezoneView, that.options.animations.left);
},
showDialog: function(options) {
var actions = options.buttons.map(function(button) {
return {
text: button.text,
action: button.click
};
});
actions.push({
text: this.options.messages.cancel,
primary: true
});
$("<div />").appendTo(document.body).kendoDialog({
close: function() {
this.destroy();
},
modal: { preventScroll: true },
closable: false,
title: false,
content: options.text,
actions
});
},
editEvent: function(model) {
var pane = this.pane;
var html = "";
var messages = this.options.messages;
var updateText = messages.save;
var removeText = messages.destroy;
var cancelText = messages.cancel;
var titleText = messages.editor.editorTitle;
var resetSeries = messages.resetSeries;
html += "<div data-role=\"view\" class=\"k-popup-edit-form k-scheduler-edit-form\"" + kendo.attr("uid") + "=\"" + model.uid + "\">" + "<div data-role=\"header\" class=\"k-appbar k-appbar-primary\">" + kendo.html.renderButton(`<button class="k-header-cancel k-scheduler-cancel" title="${cancelText}" aria-label="${cancelText}"></button>`, {
fillMode: "flat",
icon: "chevron-left"
}) + "<span class=\"k-spacer\"></span>" + `<span>${encode(titleText)}</span>` + "<span class=\"k-spacer\"></span>" + kendo.html.renderButton(`<button class="k-header-done k-scheduler-update" title="${updateText}" aria-label="${updateText}"></button>`, {
fillMode: "flat",
icon: "check"
}) + "</div>";
var fields = this.fields(editors.mobile, model);
var that = this;
var editableFields = [];
html += this._buildEditTemplate(model, fields, editableFields, true);
html += "</div>";
var view = pane.append(html);
if (!model.isNew() && this.options.editable && this.options.editable.destroy !== false && model.isRecurrenceHead() && model.recurrenceException) {
var resetSeriesBtn = "<ul class=\"k-edit-buttons k-listgroup k-listgroup-flush\"><li class=\"k-item k-listgroup-item\"><span class=\"k-scheduler-resetSeries k-link k-label\" aria-label=\"" + resetSeries + "\">" + resetSeries + "</span></li></ul>";
view.contentElement.append(resetSeriesBtn);
}
if (!model.isNew() && this.options.editable && this.options.editable.destroy !== false) {
var deleteBtn = "<ul class=\"k-edit-buttons k-listgroup k-listgroup-flush\"><li class=\"k-item k-listgroup-item\"><span class=\"k-scheduler-delete k-link k-label\" aria-label=\"" + removeText + "\">" + removeText + "</span></li></ul>";
view.contentElement.append(deleteBtn);
}
this._editPane = view;
var container = this.container = view.element;
this.editable = container.kendoEditable({
fields: editableFields,
model,
clearContainer: false,
target: that.options.target,
validateOnBlur: true
}).data("kendoEditable");
if (!this.trigger("edit", {
container,
model
})) {
container.on(CLICK + NS, "a.k-scheduler-edit, .k-scheduler-cancel, .k-scheduler-update, span.k-scheduler-delete, span.k-scheduler-resetSeries", function(e) {
e.preventDefault();
e.stopPropagation();
var button = $(this);
if (!button.hasClass("k-scheduler-edit")) {
var name = "cancel";
if (button.hasClass("k-scheduler-update")) {
name = "save";
} else if (button.hasClass("k-scheduler-delete")) {
name = "remove";
} else if (button.hasClass("k-scheduler-resetSeries")) {
name = RESET;
}
that.trigger(name, {
container,
model
});
} else {
pane.navigate(this._editPane, that.options.animations.right);
}
});
pane.navigate(view, that.options.animations.left);
model.bind(CHANGE, that.toggleDateValidationHandler);
} else {
this.trigger("cancel", {
container,
model
});
}
return this.editable;
},
_views: function() {
return this.pane.element.find(kendo.roleSelector("view")).not(this.view.element);
},
close: function() {
if (this.container) {
this.pane.navigate("", this.options.animations.right);
var views = this._views();
var view;
for (var idx = 0, length = views.length; idx < length; idx++) {
view = views.eq(idx).data("kendoView");
if (view) {
view.purge();
}
}
views.remove();
this.container = null;
if (this.editable) {
this.editable.options.model.unbind(CHANGE, this.toggleDateValidationHandler);
this.editable.destroy();
this.editable = null;
}
this.timezoneView = null;
}
}
});
var PopupEditor = Editor.extend({
destroy: function() {
this.close();
this.unbind();
},
editEvent: function(model) {
var that = this;
var editable = that.options.editable;
var html = "<div " + kendo.attr("uid") + "=\"" + model.uid + "\" class=\"k-popup-edit-form k-scheduler-edit-form\"><div class=\"k-edit-form-container\">";
var $html;
var messages = that.options.messages;
var updateText = messages.save;
var updateIcon = "save";
var cancelText = messages.cancel;
var cancelIcon = "cancel-outline";
var deleteText = messages.destroy;
var deleteIcon = "trash";
var resetSeries = messages.resetSeries;
var fields = this.fields(editors.desktop, model);
var editableFields = [];
var fieldName;
html += this._buildEditTemplate(model, fields, editableFields, false);
var attr;
var options = isPlainObject(editable) ? editable.window : {};
html += "<div class=\"k-edit-buttons\">";
html += this.createButton({
name: "update",
text: updateText,
attr,
icon: updateIcon
}) + this.createButton({
name: "canceledit",
text: cancelText,
attr,
icon: cancelIcon
});
if (!model.isNew() && editable.destroy !== false && model.isRecurrenceHead() && model.recurrenceException) {
html += this.createButton({
name: "resetSeries",
text: resetSeries,
attr
});
}
if ((!model.isNew() || model.isRecurring()) && editable.destroy !== false) {
html += "<span class=\"k-spacer\"></span>" + this.createButton({
name: "delete",
text: deleteText,
attr,
icon: deleteIcon,
fillMode: "flat",
themeColor: "primary"
});
}
html += "</div></div></div>";
$html = $(html);
kendo.applyStylesFromKendoAttributes($html, ["display"]);
var container = this.container = $html.appendTo(that.element).eq(0).kendoWindow(extend({
modal: true,
resizable: false,
draggable: true,
title: messages.editor.editorTitle,
visible: false,
close: function(e) {
if (e.userTriggered) {
if (that.trigger(CANCEL, {
container,
model
})) {
e.preventDefault();
}
}
}
}, options));
that.editable = container.kendoEditable({
fields: editableFields,
model,
clearContainer: false,
validateOnBlur: true,
target: that.options.target
}).data("kendoEditable");
for (var field in editableFields) {
if (editableFields[field].field !== "recurrenceRule") {
fieldName = editableFields[field].field;
container.find("[name='" + fieldName + "']").attr("aria-labelledby", fieldName + "_label");
}
if (editableFields[field].field === "isAllDay") {
container.find("label[for='" + fieldName + "']").attr("id", fieldName + "_label");
}
}
if (!that.trigger(EDIT, {
container,
model
})) {
if (editable.window && editable.window.position) {
container.data("kendoWindow").open();
} else {
container.data("kendoWindow").center().open();
}
container.on(CLICK + NS, "button.k-scheduler-cancel", function(e) {
e.preventDefault();
e.stopPropagation();
that.trigger(CANCEL, {
container,
model
});
});
container.on(CLICK + NS, "button.k-scheduler-update", function(e) {
e.preventDefault();
e.stopPropagation();
that.trigger("save", {
container,
model
});
});
container.on(CLICK + NS, "button.k-scheduler-delete", function(e) {
e.preventDefault();
e.stopPropagation();
that.trigger(REMOVE, {
container,
model
});
});
container.on(CLICK + NS, "button.k-scheduler-resetSeries", function(e) {
e.preventDefault();
e.stopPropagation();
that.trigger(RESET, {
container,
model
});
});
kendo.cycleForm(container);
model.bind(CHANGE, that.toggleDateValidationHandler);
} else {
that.trigger(CANCEL, {
container,
model
});
}
return that.editable;
},
close: function() {
var that = this;
var destroy = function() {
if (that.editable) {
that.editable.options.model.unbind(CHANGE, that.toggleDateValidationHandler);
that.editable.destroy();
that.editable = null;
that.container = null;
}
if (that.popup) {
that.popup.destroy();
that.popup = null;
}
};
if (that.editable) {
if (that._timezonePopup && that._timezonePopup.data("kendoWindow")) {
that._timezonePopup.data("kendoWindow").destroy();
that._timezonePopup = null;
}
if (that.container.is(":visible")) {
that.container.data("kendoWindow").bind("deactivate", destroy).close();
} else {
destroy();
}
} else {
destroy();
}
},
_createEndTimezoneButton: function() {
var messages = this.options.messages;
var html = "";
html += "<div class=\"k-edit-buttons\">";
html += this.createButton({
name: "savetimezone",
icon: "save",
themeColor: "primary",
text: messages.save
}) + this.createButton({
name: "canceltimezone",
icon: "cancel-outline",
text: messages.cancel
});
html += "</div></div></div>";
return html;
},
showDialog: function(options) {
var html = kendo.format("<div class='k-popup-edit-form'><div class='k-edit-form-container'><p class='k-popup-message'>{0}</p>", options.text);
html += "<div class=\"k-edit-buttons\">";
for (var buttonIndex = 0; buttonIndex < options.buttons.length; buttonIndex++) {
html += this.createButton(options.buttons[buttonIndex]);
}
html += "</div></div></div>";
var wrapper = this.element;
if (this.popup) {
this.popup.destroy();
}
var popup = this.popup = $(html).appendTo(wrapper).eq(0).on(CLICK, ".k-button", function(e) {
e.preventDefault();
popup.close();
var buttonIndex = $(e.currentTarget).index();
options.buttons[buttonIndex].click();
}).kendoWindow({
modal: true,
resizable: false,
draggable: false,
title: options.title,
visible: false,
close: function() {
this.destroy();
wrapper.trigger("focus");
}
}).getKendoWindow();
popup.center().open();
},
_initTimezoneEditor: function(model, activator) {
var that = this;
var container = that.container.find(".k-scheduler-timezones");
var checkbox = container.find("input.k-timezone-toggle");
var endTimezoneRow = container.find(".k-form-field").last();
var saveButton = container.find(".k-scheduler-savetimezone");
var cancelButton = container.find(".k-scheduler-canceltimezone");
var timezonePopup = that._timezonePopup;
var startTimezoneChange = function(e) {
if (e.field === "startTimezone") {
var value = model.startTimezone;
checkbox.prop(DISABLED, !value);
if (!value) {
endTimezoneRow.hide();
model.set("endTimezone", "");
checkbox.prop("checked", false);
}
}
};
var wnd;
that._startTimezone = model.startTimezone;
that._endTimezone = model.endTimezone;
if (!timezonePopup) {
that._timezonePopup = timezonePopup = container.kendoWindow({
modal: true,
resizable: false,
draggable: true,
title: that.options.messages.editor.timezoneEditorTitle,
visible: false,
close: function(e) {
model.unbind(CHANGE, startTimezoneChange);
if (e.userTriggered) {
that._revertTimezones(model);
}
if (activator) {
activator.focus();
}
}
});
checkbox.on(CLICK, function() {
endTimezoneRow.toggle(checkbox.prop("checked"));
model.set("endTimezone", "");
});
saveButton.on(CLICK, function(e) {
e.preventDefault();
wnd.close();
});
cancelButton.on(CLICK, function(e) {
e.preventDefault();
that._revertTimezones(model);
wnd.close();
});
model.bind(CHANGE, startTimezoneChange);
}
checkbox.prop("checked", model.endTimezone).prop(DISABLED, !model.startTimezone);
if (model.endTimezone) {
endTimezoneRow.show();
} else {
endTimezoneRow.hide();
}
wnd = timezonePopup.data("kendoWindow");
wnd.center().open();
}
});
var Scheduler = DataBoundWidget.extend({
init: function(element, options) {
var that = this;
Widget.fn.init.call(that, element, options);
if (!that.options.views || !that.options.views.length) {
that.options.views = ["day", "week"];
}
that.resources = [];
that._initModel();
that._wrapper();
if (that.options.selectable) {
that._tabindex();
}
that._views();
that._toolbar();
that._dataSource();
that._resources();
that._resizeHandler = function() {
that.resize();
};
that.wrapper.on(MOUSEDOWN + NS + " selectstart" + NS, function(e) {
var target = $(e.target);
if (!target.is(":kendoFocusable") && target.closest(".k-button").length === 0) {
e.preventDefault();
}
});
if (that.options.editable && that.options.editable.resize !== false) {
that._resizable();
}
that._movable();
that._bindResize();
if (that.options.messages && that.options.messages.recurrence) {
recurrence.options = that.options.messages.recurrence;
}
that._navigation();
that._selectable();
that._touchHandlers();
that._ariaId = kendo.guid();
that._createEditor();
that.mediaQueryList = kendo.mediaQuery(MIN_SCREEN);
that.mediaQueryList.onChange(that._onMediaChange.bind(that));
that.toolbar.find(".k-scheduler-views").children().removeClass("k-button-rectangle");
if (that._showWatermarkOverlay) {
that._showWatermarkOverlay(that.element[0]);
}
},
_onMediaChange: function(e) {
var that = this;
var view = that._selectedView;
var toolbarEl = that.toolbar;
var toolbar = toolbarEl.getKendoToolBar();
if (e.matches) {
toolbar.hide(toolbarEl.find(".k-views-dropdown"));
toolbar.show(toolbarEl.find(".k-views-dropdown").parent().next(".k-button-group"));
} else {
toolbar.show(toolbarEl.find(".k-views-dropdown"));
toolbar.hide(toolbarEl.find(".k-views-dropdown").parent().next(".k-button-group"));
}
that._model.set("formattedDate", e.matches ? view.dateForTitle() : view.shortDateForTitle());
},
_bindResize: function() {
$(window).on("resize" + NS, this._resizeHandler);
},
_unbindResize: function() {
$(window).off("resize" + NS, this._resizeHandler);
},
dataItems: function() {
var that = this;
var items = that.items();
var events = that._data;
var eventsUids = $.map(items, function(item) {
return $(item).attr("data-uid");
});
var i;
var key;
var dict = {};
var eventsUidsLength = eventsUids.length;
for (i = 0; i < eventsUidsLength; i++) {
dict[eventsUids[i]] = null;
}
var eventsCount = events.length;
for (i = 0; i < eventsCount; i++) {
var event = events[i];
if (dict[event.uid] !== undefined) {
dict[event.uid] = event;
}
}
var sortedData = [];
for (key in dict) {
sortedData.push(dict[key]);
}
return sortedData;
},
_isMobile: function() {
var options = this.options;
return options.mobile === true && kendo.support.mobileOS || options.mobile === "phone" || options.mobile === "tablet";
},
_isTouch: function(event) {
return /touch/.test(event.type) || event.originalEvent && /touch/.test(event.originalEvent.pointerType);
},
_isInverseColor: function(eventElement) {
return eventElement.hasClass(INVERSECOLORCLASS);
},
_groupsByResource: function(resources, groupIndex, groupsArray, parentFieldValue, parentField) {
if (!groupsArray) {
groupsArray = [];
}
var resource = resources[0];
if (resource) {
var group;
var data = resource.dataSource.view();
var prevIndex = 0;
for (var dataIndex = 0; dataIndex < data.length; dataIndex++) {
var fieldValue = kendo.getter(resource.dataValueField)(data[dataIndex]);
var currentGroupIndex = groupIndex + prevIndex + dataIndex;
group = this._groupsByResource(resources.slice(1), currentGroupIndex, groupsArray, fieldValue, resource.field);
group[resource.field] = fieldValue;
prevIndex = group.groupIndex;
if (parentField && parentFieldValue) {
group[parentField] = parentFieldValue;
}
if (resources.length === 1) {
group.groupIndex = groupIndex + dataIndex;
groupsArray.push(group);
}
}
return group;
} else {
return {};
}
},
data: function() {
return this._data;
},
select: function(options) {
var that = this;
var view = that.view();
var selection = that._selection;
var groups = view.groups;
var selectedGroups;
if (options === undefined) {
var selectedEvents;
var slots = view._selectedSlots;
if (!selection) {
return [];
}
if (selection && selection.events) {
selectedEvents = that._selectedEvents();
}
return {
start: selection.start,
end: selection.end,
events: selectedEvents,
slots,
resources: view._resourceBySlot(selection)
};
}
if (!options) {
that._selection = null;
that._old = null;
view.clearSelection();
return;
}
if (Array.isArray(options)) {
options = { events: options.splice(0) };
}
if (options.resources) {
var fieldName;
var filters = [];
var groupsByResource = [];
if (view.groupedResources) {
that._groupsByResource(view.groupedResources, 0, groupsByResource);
}
for (fieldName in options.resources) {
filters.push({
field: fieldName,
operator: "eq",
value: options.resources[fieldName]
});
}
selectedGroups = new kendo.data.Query(groupsByResource).filter(filters).toArray();
}
if (options.events && options.events.length) {
that._selectEvents(options.events, selectedGroups);
that._select();
return;
}
if (groups && options.start && options.end) {
var rangeStart = getDate(view._startDate);
var rangeEnd = kendo.date.addDays(getDate(view._endDate), 1);
var group;
var ranges;
if (options.start < rangeEnd && rangeStart <= options.end) {
if (selectedGroups && selectedGroups.length) {
group = groups[selectedGroups[0].groupIndex];
} else {
group = groups[0];
}
if (!group.timeSlotCollectionCount()) {
options.isAllDay = true;
}
ranges = group.ranges(options.start, options.end, options.isAllDay, false);
if (ranges.length) {
that._selection = {
start: kendo.timezone.toLocalDate(ranges[0].start.start),
end: kendo.timezone.toLocalDate(ranges[ranges.length - 1].end.end),
groupIndex: ranges[0].start.groupIndex,
index: ranges[0].start.index,
isAllDay: ranges[0].start.isDaySlot,
events: []
};
that._select();
}
}
}
},
_selectEvents: function(eventsUids, selectedGroups) {
var that = this;
var idx;
var view = that.view();
var groups = view.groups;
var eventsLength = eventsUids.length;
var isGrouped = selectedGroups && selectedGroups.length;
var ctrlKey = that._ctrlKey;
that._ctrlKey = true;
for (idx = 0; idx < eventsLength; idx++) {
if (groups && isGrouped) {
var currentGroup = groups[selectedGroups[0].groupIndex];
var events = [];
var timeSlotCollectionCount = currentGroup.timeSlotCollectionCount();
var daySlotCollectionCount = currentGroup.daySlotCollectionCount();
for (var collIdx = 0; collIdx < timeSlotCollectionCount; collIdx++) {
events = events.concat(currentGroup.getTimeSlotCollection(collIdx).events());
}
for (var dayCollIdx = 0; dayCollIdx < daySlotCollectionCount; dayCollIdx++) {
events = events.concat(currentGroup.getDaySlotCollection(dayCollIdx).events());
}
events = new kendo.data.Query(events).filter({
field: (event) => event.element[0].getAttribute("data-uid"),
operator: "eq",
value: eventsUids[idx]
}).toArray();
if (events[0]) {
that._createSelection(events[0].element);
}
} else {
var element = view.element.find(kendo.format(".k-event[data-uid={0}], .k-task[data-uid={0}]", eventsUids[idx]));
if (element.length) {
that._createSelection(element[0]);
}
}
}
that._ctrlKey = ctrlKey;
},
_touchHandlers: function() {
var that = this;
var startX;
var startY;
var endX;
var endY;
var timeStamp;
var wrapper = that.wrapper;
var touchMoveHandler = that._touchMove.bind(that);
wrapper.on(TOUCHSTART + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td, .k-event", function(e) {
var content = that.wrapper.find(".k-scheduler-content");
if (!that._isTouch(e)) {
return;
}
content.stop(true, false);
that._touchPosX = startX = that._tapPosition(e, "X");
that._touchPosY = startY = that._tapPosition(e, "Y");
that._userTouched = true;
that.view()._scrolling = false;
timeStamp = Date.now();
wrapper.on(TOUCHMOVE + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td, .k-event", touchMoveHandler);
});
wrapper.on("contextmenu" + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td, .k-event", function(e) {
that._preventFocus = true;
});
wrapper.on(TOUCHEND + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td, .k-event", function(e) {
if (!that._isTouch(e)) {
return;
}
var delta = Date.now() - timeStamp;
var content = that.wrapper.find(".k-scheduler-content");
var amplitude = -that._amplitude * (3e3 / delta);
endX = that._tapPosition(e, "X");
endY = that._tapPosition(e, "Y");
if (that._dragging) {
return;
}
if (that.options.selectable && (Math.abs(endX - startX) <= 10 || Math.abs(endY - startY) <= 10)) {
that._mouseDownSelection(e);
}
if (!kendo.support.kineticScrollNeeded && delta < 200 && Math.abs(endX - startX) > 10) {
content.animate({ scrollTop: content[0].scrollTop + amplitude });
}
wrapper.off(TOUCHMOVE + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td", touchMoveHandler);
});
},
_navigation: function() {
var that = this, wrapper = that.wrapper;
wrapper.on("focus" + NS, function() {
var selection = that._selection;
setTimeout(function() {
if (!that._preventFocus) {
if ((!selection || selection.events.length > 0 && wrapper.find("[data-uid='" + selection.events[0] + "']").length === 0) && !that._userTouched && !that._mouseDown) {
that._initialFocus();
} else {
that._mouseDown = false;
}
that._select();
} else {
that._preventFocus = false;
}
}, 300);
});
wrapper.on("focusout" + NS, function() {
that._ctrlKey = that._shiftKey = false;
});
wrapper.on("keydown" + NS, that._keydown.bind(that));
wrapper.on("keyup" + NS, function(e) {
that._ctrlKey = e.ctrlKey;
that._shiftKey = e.shiftKey;
});
},
_selectable: function() {
var that = this;
var wrapper = that.wrapper;
if (!that.options.selectable) {
return;
}
wrapper.on(MOUSEDOWN + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td, .k-event, .k-scheduler-body td .k-link", function(e) {
if (that._isTouch(e)) {
return;
}
that._mouseDownSelection(e);
});
var mouseMoveHandler = that._mouseMove.bind(that);
wrapper.on(MOUSEDOWN + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td", function(e) {
var which = e.which;
var button = e.button;
var isRight = which && which === 3 || button && button == 2;
if (that._isTouch(e)) {
return;
}
if (!isRight) {
wrapper.on(MOUSEMOVE + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td", mouseMoveHandler);
}
});
wrapper.on("mouseup" + NS + " mousecancel" + NS, function() {
wrapper.off(MOUSEMOVE + NS, ".k-scheduler-header-all-day td, .k-scheduler-content td", mouseMoveHandler);
});
},
_mouseDownSelection: function(e) {
var which = e.which;
var button = e.button;
var isRight = which && which === 3 || button && button == 2;
if (!isRight) {
if (e.ctrlKey) {
this._ctrlKey = e.ctrlKey;
}
if (e.shiftKey) {
this._shiftKey = e.shiftKey;
}
this._mouseDown = true;
this._createSelection(e.currentTarget);
if ($(e.currentTarget).hasClass("k-event")) {
this._selection.eventElement = e.currentTarget;
}
}
if (this.view().name !== "year" && kendo._activeElement() !== this.wrapper.get(0)) {
kendo.focusElement(this.wrapper);
} else {
this._select();
}
if (this.toolbar) {
this.toolbar.find("." + FOCUSEDSTATE).removeClass(FOCUSEDSTATE);
}
},
_selectFirstSlot: function() {
var firstAllDay = this.wrapper.find(".k-scheduler-header-all-day").find("td").first(), firstContent = this.wrapper.find(".k-scheduler-content").find("td").first(), firstSlot;
if (firstAllDay.length > 0) {
firstSlot = firstAllDay;
} else if (firstContent.length > 0) {
firstSlot = firstContent;
} else {
firstSlot = this.wrapper.find(".k-scheduler-body").find(".k-link").first();
}
this._createSelection(firstSlot);
},
_firstEvent: function() {
var firstEventInAllDay = this.wrapper.find(".k-scheduler-header-wrap .k-event, .k-task").first(), firstEventInContent = this.wrapper.find(".k-scheduler-content .k-event").first(), firstEvent, allDayEvent, contentEvent;
if (firstEventInAllDay.length > 0) {
if (firstEventInContent.length > 0) {
allDayEvent = this.occurrenceByUid(firstEventInAllDay.data("uid"));
contentEvent = this.occurrenceByUid(firstEventInContent.data("uid"));
firstEvent = allDayEvent.start <= contentEvent.start ? firstEventInAllDay : firstEventInContent;
} else {
firstEvent = firstEventInAllDay;
}
} else if (firstEventInContent) {
firstEvent = firstEventInContent;
}
return firstEvent;
},
_initialFocus: function() {
var firstEvent = this._firstEvent();
if (this.options.selectable) {
if (firstEvent && firstEvent.length > 0) {
this._createSelection(firstEvent);
this._selection.eventElement = firstEvent[0];
} else {
this._selectFirstSlot();
}
} else if (this.toolbar && this.toolbar.find("." + FOCUSEDSTATE).length === 0) {
this._focusToolbar();
} else if (this.toolbar) {
this.toolbar.find("." + FOCUSEDSTATE).removeClass(FOCUSEDSTATE);
}
},
_select: function() {
const that = this, view = that.view(), wrapper = that.wrapper, selection = that._selection, oldSelection = that._old ? that._old.selection : null, oldEventsLength = that._old ? that._old.eventsLength : null;
let current = view.current();
if (!selection) {
return;
}
if (current) {
current.removeAttribute("id");
wrapper.removeAttr("aria-activedescendant");
}
view.select(selection);
that._selection.eventElement = null;
current = view.current();
if (current) {
current.setAttribute("id", that._ariaId);
wrapper.attr("aria-activedescendant", that._ariaId);
const oldSelectionStart = that._old ? that._old.selectionStart.getTime() : null;
const oldSelectionEnd = that._old ? that._old.selectionEnd.getTime() : null;
const selectionRangeChanged = selection.start.getTime() !== oldSelectionStart || selection.end.getTime() !== oldSelectionEnd;
if (oldSelection !== current || selection.events && oldEventsLength !== selection.events.length || selectionRangeChanged) {
var currentUid = $(current).data("uid");
if (that._old && currentUid && currentUid === $(that._old.selection).data("uid") && selection.events && that._old.eventsLength === selection.events.length) {
return;
}
var events = that._selectedEvents();
var slots = view._selectedSlots;
that._old = {
selection: current,
eventsLength: events.length,
selectionStart: selection.start,
selectionEnd: selection.end
};
that.trigger(CHANGE, {
start: selection.start,
end: selection.end,
events,
slots,
resources: view._resourceBySlot(selection)
});
}
}
},
_selectedEvents: function() {
var uids = this._selection.events;
var length = uids.length;
var idx = 0;
var event;
var events = [];
for (; idx < length; idx++) {
event = this.occurrenceByUid(uids[idx]);
if (event) {
events.push(event);
}
}
return events;
},
_tapPosition: function(event, coordinate) {
return /touch/.test(event.type) ? (event.originalEvent || event).changedTouches[0]["page" + coordinate] : event["page" + coordinate];
},
_touchMove: function(e) {
var that = this;
var content = that.wrapper.find(".k-scheduler-content");
var verticalScroll = content[0].scrollHeight > content[0].clientHeight;
var horizontalScroll = content[0].scrollWidth > content[0].clientWidth;
var endY = that._tapPosition(e, "Y");
var endX = that._tapPosition(e, "X");
var scrollTop = content[0].scrollTop - Math.round(endY - that._touchPosY);
var scrollLeft = content[0].scrollLeft - Math.round(endX - that._touchPosX);
var applyVerticalScroll = verticalScroll && Math.abs(endY - that._touchPosY) > 10;
var applyhorizontalScroll = horizontalScroll && Math.abs(endY - that._touchPosY) > 10;
if (that._dragging || kendo.support.kineticScrollNeeded || !that._isTouch(e)) {
return;
}
if (applyVerticalScroll || applyhorizontalScroll) {
that._amplitude = Math.round(endY - that._touchPosY);
that._touchPosY = endY;
that._touchPosX = endX;
content.animate({
scrollTop,
scrollLeft
}, 0);
that.view()._scrolling = true;
}
},
_mouseMove: function(e) {
var that = this;
clearTimeout(that._moveTimer);
if (that._isTouch(e)) {
return;
}
that._moveTimer = setTimeout(function() {
var view = that.view();
var selection = that._selection;
if (selection) {
var slot = view.selectionByElement($(e.currentTarget));
if (slot && selection.groupIndex === slot.groupIndex) {
var startDate = slot.startDate();
var endDate = slot.endDate();
if (startDate >= selection.end) {
selection.backward = false;
} else if (endDate <= selection.start) {
selection.backward = true;
}
if (selection.backward) {
selection.start = startDate;
} else {
selection.end = endDate;
}
that._select();
}
}
}, 5);
},
_viewByIndex: function(index) {
var view, views = this.views;
for (view in views) {
if (!index) {
return view;
}
index--;
}
},
_keydownToolbar: function(e) {
var key = e.keyCode;
if (e.altKey && key === keys.DOWN && this.toolbar.find(".k-nav-current").hasClass(FOCUSEDSTATE)) {
this._showCalendar();
e.preventDefault();
return;
} else if (key === keys.DOWN) {
if (this._selectedViewName === "year") {
this.view().calendar.focus();
} else if (document.activeElement !== this.element[0]) {
this.element.focus();
}
e.preventDefault();
return;
}
},
_keydownView: function(e) {
var key = e.keyCode, isModifier = key === 16 || key === 18 || key === 17 || key === 91 || key === 92, selection = this._selection, view = this.view(), editable = view.options.editable, arrowKeys = [
keys.LEFT,
keys.RIGHT,
keys.UP,
keys.DOWN
], isRtl = kendo.support.isRtl(this.element), previous, content, cell;
if (isModifier) {
return;
}
if (!selection) {
return;
}
if (key === keys.ENTER) {
if (selection.events.length && editable) {
if (editable.update !== false) {
this.editEvent(selection.events[0]);
}
}
} else if ((key === keys.DELETE || key === keys.BACKSPACE) && editable !== false && editable.destroy !== false) {
this.removeEvent(selection.events[0]);
} else if (arrowKeys.indexOf(key) > -1) {
previous = key === keys.UP || key === keys.LEFT && !isRtl || key === keys.RIGHT && isRtl;
if (view.moveToEvent(selection, previous)) {
this._select();
if (document.activeElement !== this.element[0]) {
this.element.trigger("focus");
}
e.preventDefault();
}
} else if (key === keys.HOME || key === keys.END) {
e.preventDefault();
content = this.view().content.eq(0);
if (key === keys.HOME) {
cell = content.find(".k-scheduler-table td").first();
} else {
cell = content.find(".k-scheduler-table td").last();
}
this.view()._scrollTo(cell[0], content[0]);
}
},
_keydownShortcut: function(key) {
var currentDate = new Date(), timezone = this.options.timezone, editable = this.view().options.editable;
if (key === 66 && !!this.view().toggleFullDay) {
this.view().toggleFullDay();
} else if (key === 67 && editable && editable.create !== false) {
this.addEvent(extend({}, {
start: this.date(),
end: this.date()
}));
} else if (key === 84) {
if (timezone) {
var timezoneOffset = kendo.timezone.offset(currentDate, timezone);
date = kendo.timezone.convert(currentDate, currentDate.getTimezoneOffset(), timezoneOffset);
} else {
date = currentDate;
}
if (!this.trigger("navigate", {
view: this._selectedViewName,
action: "today",
date
})) {
this.date(date);
}
}
},
_keydownYearView: function(e) {
var view = this.view(), selection = this._selection, key = e.keyCode;
if (key === keys.TAB) {
return;
}
if (key === keys.F10) {
this._focusToolbar();
e.preventDefault();
} else {
this.toolbar.find("." + FOCUSEDSTATE).removeClass(FOCUSEDSTATE);
view.calendar.focus();
}
if (selection) {
this._adjustSelectedDate();
}
e.preventDefault();
},
_keydownAgendaView: function(e) {
var key = e.keyCode, view = this.view(), selection = this._selection, content, cell;
if (view.move(selection, key)) {
e.preventDefault();
this.toolbar.find("." + FOCUSEDSTATE).removeClass(FOCUSEDSTATE);
this._select();
} else if (key === keys.HOME || key === keys.END) {
e.preventDefault();
content = this.view().content.eq(0);
if (key === keys.HOME) {
cell = content.find(".k-scheduler-table td").first();
} else {
cell = content.find(".k-scheduler-table td").last();
}
this.view()._scrollTo(cell[0], content[0]);
}
},
_keydownChangeView: function(key) {
var viewIndex = key - 49, viewByIndex;
if (viewIndex === -1) {
viewIndex = 9;
}
viewByIndex = this._viewByIndex(viewIndex);
if (viewByIndex && !this.trigger("navigate", {
view: viewByIndex,
action: "changeView",
date: this.date()
})) {
this.view(viewByIndex);
}
},
_keydownChangeDate: function(key) {
var isRtl = kendo.support.isRtl(this.element), direction = isRtl && key === keys.LEFT || !isRtl && key === keys.RIGHT ? "next" : "previous";
if (direction === "next") {
date = this.view().nextDate();
} else {
date = this.view().previousDate();
}
if (!this.trigger("navigate", {
view: this._selectedViewName,
action: direction,
date
})) {
this.date(date);
this._initialFocus();
this._select();
}
},
_keydown: function(e) {
var key = e.keyCode, shortcutKeys = [
66,
67,
84
], toolbarIsFocused = $(e.target).closest(".k-toolbar").length > 0, altKey = e.altKey;
this._ctrlKey = e.ctrlKey;
this._shiftKey = e.shiftKey;
if (key === keys.ESC && this.popup && this.popup.visible()) {
this.popup.close();
e.preventDefault();
return;
}
if ((key === keys.LEFT || key === keys.RIGHT) && this._shiftKey) {
this._keydownChangeDate(key);
return;
}
if (altKey && key >= 48 && key <= 57) {
this._keydownChangeView(key);
return;
}
if (shortcutKeys.indexOf(key) > -1) {
if (!$(e.target).hasClass("k-scheduler-search-input")) {
this._keydownShortcut(key);
}
return;
}
if (toolbarIsFocused) {
this._keydownToolbar(e);
} else {
if (this._selectedViewName === "year") {
this._keydownYearView(e);
return;
}
if (key === keys.F10) {
this._focusToolbar();
e.preventDefault();
return;
}
if (this._selectedViewName === "agenda") {
this._keydownAgendaView(e);
return;
}
this._keydownView(e);
}
},
_focusToolbar: function() {
this.toolbar.find("[tabindex=0]").first().trigger("focus").addClass(FOCUSEDSTATE);
},
_createSelection: function(item) {
var selection = this._selection, uid, slot;
item = $(item);
if (item.is(".k-event")) {
uid = item.attr(kendo.attr("uid"));
if (selection && selection.events.indexOf(uid) !== -1 && !this._ctrlKey) {
return;
}
}
if (!selection || !this._ctrlKey && !this._shiftKey) {
selection = this._selection = {
events: [],
groupIndex: 0
};
}
slot = this.view().selectionByElement(item);
if (slot) {
selection.groupIndex = slot.groupIndex || 0;
}
if (uid) {
slot = getOccurrenceByUid(this._data, uid);
}
if (slot && slot.uid) {
uid = [slot.uid];
}
this._updateSelection(slot, uid);
this._adjustSelectedDate();
},
_updateSelection: function(dataItem, events, groupIndex) {
var selection = this._selection;
if (dataItem && selection) {
var view = this.view();
if (dataItem.uid) {
dataItem = view._updateEventForSelection(dataItem);
}
if (this._shiftKey && selection.start && selection.end) {
var backward = dataItem.end < selection.end;
selection.end = dataItem.endDate ? dataItem.endDate() : dataItem.end;
if (backward && view._timeSlotInterval) {
kendo.date.setTime(selection.end, -view._timeSlotInterval());
}
} else {
selection.start = dataItem.startDate ? dataItem.startDate() : dataItem.start;
selection.end = dataItem.endDate ? dataItem.endDate() : dataItem.end;
}
if ("isDaySlot" in dataItem) {
selection.isAllDay = dataItem.isDaySlot;
} else {
selection.isAllDay = dataItem.isAllDay;
}
if (groupIndex !== null && groupIndex !== undefined) {
selection.groupIndex = groupIndex;
}
selection.index = dataItem.index;
if (this._ctrlKey) {
var indexOfEvent = events && events.length ? selection.events.indexOf(events[0]) : -1;
if (indexOfEvent > -1) {
selection.events.splice(indexOfEvent, 1);
} else {
selection.events = selection.events.concat(events || []);
}
} else {
selection.events = events || [];
}
}
},
options: {
name: "Scheduler",
date: TODAY,
editable: true,
autoBind: true,
snap: true,
mobile: false,
timezone: "",
allDaySlot: true,
min: new Date(1900, 0, 1),
max: new Date(2099, 11, 31),
toolbar: null,
workWeekStart: 1,
workWeekEnd: 5,
workDays: null,
showWorkHours: false,
startTime: TODAY,
endTime: TODAY,
currentTimeMarker: {
updateInterval: 1e4,
useLocalTimezone: true
},
ongoingEvents: {
cssClass: ONGOING_CLASS,
enabled: false,
updateInterval: 6e4,
useLocalTimezone: true
},
footer: {},
messages: {
today: "Today",
pdf: "Export to PDF",
save: "Save",
cancel: "Cancel",
destroy: "Delete",
resetSeries: "Reset Series",
deleteWindowTitle: "Delete event",
next: "Next",
previous: "Previous",
refresh: "Refresh",
selectView: "Select view",
ariaSlotLabel: "Selected from {0:t} to {1:t}",
ariaEventLabel: {
on: "on",
at: "at",
to: "to",
allDay: "(all day)",
prefix: ""
},
search: "Search...",
views: {
day: "Day",
week: "Week",
workWeek: "Work Week",
agenda: "Agenda",
month: "Month",
timeline: "Timeline",
timelineWeek: "Timeline Week",
timelineWorkWeek: "Timeline Work Week",
timelineMonth: "Timeline Month",
year: "Year"
},
recurrenceMessages: {
deleteWindowTitle: "Delete Recurring Item",
resetSeriesWindowTitle: "Reset Series",
deleteWindowOccurrence: "Delete current occurrence",
deleteWindowSeries: "Delete the series",
editWindowTitle: "Edit Recurring Item",
editWindowOccurrence: "Edit current occurrence",
editWindowSeries: "Edit the series"
},
editable: { confirmation: DELETECONFIRM },
editor: {
title: "Title",
start: "Start",
end: "End",
allDayEvent: "All day event",
description: "Description",
repeat: "Repeat",
timezone: "Timezone",
startTimezone: "Start timezone",
endTimezone: "End timezone",
separateTimezones: "Use separate start and end time zones",
timezoneEditorTitle: "Timezones",
timezoneEditorButton: "Time zone",
timezoneTitle: "Time zones",
noTimezone: "No timezone",
editorTitle: "Event"
}
},
height: null,
width: null,
resources: [],
group: {
resources: [],
orientation: "horizontal"
},
views: [],
selectable: false
},
events: [
REMOVE,
EDIT,
CANCEL,
SAVE,
"add",
"dataBinding",
"dataBound",
"moveStart",
"move",
"moveEnd",
"resizeStart",
"resize",
"resizeEnd",
"navigate",
CHANGE
],
destroy: function() {
var that = this, element;
Widget.fn.destroy.call(that);
if (that.dataSource) {
that.dataSource.unbind(CHANGE, that._refreshHandler);
that.dataSource.unbind(PROGRESS, that._progressHandler);
that.dataSource.unbind(ERROR, that._errorHandler);
}
if (that._resourceRefreshHandler) {
for (var idx = 0; idx < that.resources.length; idx++) {
var resourceDS = that.resources[idx].dataSource;
resourceDS.unbind(CHANGE, that._resourceRefreshHandler);
resourceDS.unbind(PROGRESS, that._resourceProgressHandler);
resourceDS.unbind(ERROR, that._resourceErrorHandler);
}
}
if (that.calendar) {
that.calendar.destroy();
that.popup.destroy();
}
if (that.view()) {
that.view().destroy();
}
if (that._editor) {
that._editor.destroy();
}
if (this._moveDraggable) {
this._moveDraggable.destroy();
}
if (this._resizeDraggable) {
this._resizeDraggable.destroy();
}
if (that.mediaQueryList) {
that.mediaQueryList.destroy();
}
element = that.element.add(that.wrapper).add(that.toolbar).add(that.popup);
element.off(NS);
clearTimeout(that._moveTimer);
that._model = null;
that.toolbar = null;
that.element = null;
$(window).off("resize" + NS, that._resizeHandler);
kendo.destroy(that.wrapper);
},
setDataSource: function(dataSource) {
this.options.dataSource = dataSource;
this._dataSource();
if (this.options.autoBind && dataSource.fetch) {
dataSource.fetch();
} else if (isArray(dataSource)) {
this.view(this._selectedView);
}
},
items: function() {
var content = this.wrapper.find(".k-scheduler-content");
var view = this.view();
if (view && view.options.name === "agenda") {
return content.find(".k-task");
} else {
return content.find(".k-event").add(this.wrapper.find(".k-scheduler-header-wrap").find(".k-scheduler-header-all-day").siblings());
}
},
_movable: function() {
var startSlot;
var endSlot;
var startResources;
var startTime;
var endTime;
var event;
var clonedEvent;
var that = this;
var originSlot;
var originStartTime;
var originalEvent;
var distance = 0;
var clonedEvents = [];
var cachedEvents = [];
var isMobile = that._isMobile();
var movable = that.options.editable && that.options.editable.move !== false;
var resizable = that.options.editable && that.options.editable.resize !== false;
if (movable || resizable && isMobile) {
that._dragging = false;
if (isMobile && kendo.support.mobileOS.android) {
distance = 5;
}
that._moveDraggable = new kendo.ui.Draggable(that.element, {
distance,
filter: ".k-event",
ignore: ".k-resize-handle",
holdToDrag: isMobile,
autoScroll: true
});
if (movable) {
that._moveDraggable.bind("dragstart", function(e) {
var view = that.view();
var eventElement = e.currentTarget;
var isTouch = that._isTouch(e);
that._dragging = true;
if (!view.options.editable || view.options.editable.move === false) {
that._dragging = false;
e.preventDefault();
return;
}
if (isTouch && !eventElement.hasClass("k-event-active")) {
that._dragging = false;
that.element.find(".k-event-active").removeClass("k-event-active");
e.preventDefault();
return;
}
event = that.occurrenceByUid(eventElement.attr(kendo.attr("uid")));
clonedEvent = event.clone();
originalEvent = event.clone();
clonedEvent.update(view._eventOptionsForMove(clonedEvent));
clonedEvent.inverseColor = that._isInverseColor(eventElement);
clonedEvents = [];
if (that._selection) {
var events = that._selection.events;
for (var i = 0; i < events.length; i++) {
var evtClone = that.occurrenceByUid(events[i]).clone();
var evtCloneElement = this.element.find("div.k-event[data-uid=\"" + evtClone.uid + "\"]").eq(0);
evtClone.update(view._eventOptionsForMove(evtClone));
if (evtCloneElement.length) {
evtClone.inverseColor = that._isInverseColor(evtCloneElement);
}
clonedEvents.push(evtClone);
}
} else {
clonedEvents.push(clonedEvent);
}
startSlot = view._slotByPosition(e.x.startLocation, e.y.startLocation);
startResources = view._resourceBySlot(startSlot);
originStartTime = startTime = startSlot.startOffset(e.x.startLocation, e.y.startLocation, that.options.snap);
endSlot = startSlot;
originSlot = startSlot;
if (!startSlot || that.trigger("moveStart", { event })) {
e.preventDefault();
}
}).bind("drag", function(e) {
var view = that.view();
var slot = view._slotByPosition(e.x.location, e.y.location);
var distance;
var range;
var i;
if (!slot) {
return;
}
endTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
if (slot.isDaySlot !== startSlot.isDaySlot) {
if (slot.isDaySlot !== originSlot.isDaySlot) {
var slotIndex = $(startSlot.element).index();
var targetSlotElement = $(slot.element).parent().children().eq(slotIndex);
startSlot = view._slotByPosition(targetSlotElement.offset().left, targetSlotElement.offset().top);
startTime = startSlot.startOffset(e.x.location, e.y.location, true);
cachedEvents = clonedEvents.map(function(event) {
return event.clone();
});
for (i = 0; i < clonedEvents.length; i++) {
if (clonedEvents[i].isAllDay != slot.isDaySlot) {
clonedEvents[i].isAllDay = slot.isDaySlot;
clonedEvents[i].end = kendo.date.getDate(clonedEvents[i].start);
clonedEvents[i].start = kendo.date.getDate(clonedEvents[i].start);
if (!slot.isDaySlot) {
kendo.date.setTime(clonedEvents[i].start, kendo.date.getMilliseconds(view.startTime()));
kendo.date.setTime(clonedEvents[i].end, kendo.date.getMilliseconds(view.startTime()) + view._timeSlotInterval());
}
}
}
} else {
startSlot = $.extend(true, {}, originSlot);
startTime = originStartTime;
clonedEvents = cachedEvents;
}
}
distance = endTime - startTime;
for (i = 0; i < clonedEvents.length; i++) {
view._updateMoveHint(clonedEvents[i], slot.groupIndex, distance);
}
range = moveEventRange(clonedEvent, distance);
if (!that.trigger("move", {
event,
slot: {
element: slot.element,
start: slot.startDate(),
end: slot.endDate(),
isDaySlot: slot.isDaySlot
},
resources: view._resourceBySlot(slot),
start: range.start,
end: range.end
})) {
endSlot = slot;
} else {
for (i = 0; i < clonedEvents.length; i++) {
view._updateMoveHint(clonedEvents[i], slot.groupIndex, distance);
}
}
}).bind("dragend", function(e) {
that.view()._removeMoveHint();
var distance = endTime - startTime;
var range = moveEventRange(clonedEvent, distance);
var start = range.start;
var end = range.end;
that._dragging = false;
var endResources = that.view()._resourceBySlot(endSlot);
var prevented = that.trigger("moveEnd", {
event,
slot: {
element: endSlot.element,
start: endSlot.startDate(),
end: endSlot.endDate()
},
start,
end,
resources: endResources
});
if (!prevented && (event.start.getTime() !== start.getTime() || event.end.getTime() !== end.getTime() || originSlot.isDaySlot !== endSlot.isDaySlot || kendo.stringify(endResources) !== kendo.stringify(startResources))) {
that._isMultiDrag = clonedEvents.length > 1;
for (var i = 0; i < clonedEvents.length; i++) {
var evt = clonedEvents[i];
range = moveEventRange(evt, distance);
var updatedEventOptions = that.view()._eventOptionsForMove(evt);
var eventOptions = $.extend({
isAllDay: evt.isAllDay,
start: range.start,
end: range.end
}, updatedEventOptions, endResources);
that._updateEvent(null, evt, eventOptions);
}
if (that._isMultiDrag) {
that.dataSource.sync();
that._isMultiDrag = false;
}
}
e.currentTarget.removeClass("k-event-active");
this.cancelHold();
clonedEvents = [];
cachedEvents = [];
}).bind("dragcancel", function() {
that.view()._removeMoveHint();
this.cancelHold();
clonedEvents = [];
cachedEvents = [];
});
}
that._moveDraggable.bind("hold", function(e) {
if (that._isTouch(e)) {
that.element.find(".k-event-active").removeClass("k-event-active");
if (that.options.selectable) {
that._createSelection(e.currentTarget);
}
e.currentTarget.addClass("k-event-active");
}
});
}
},
_resizable: function() {
var startTime;
var endTime;
var event;
var clonedEvent;
var slot;
var that = this;
var distance = 0;
function direction(handle) {
var directions = {
"k-resize-e": "east",
"k-resize-w": "west",
"k-resize-n": "north",
"k-resize-s": "south"
};
for (var key in directions) {
if (handle.hasClass(key)) {
return directions[key];
}
}
}
if (that._isMobile() && kendo.support.mobileOS.android) {
distance = 5;
}
that._resizeDraggable = new kendo.ui.Draggable(that.element, {
distance,
filter: ".k-resize-handle",
autoScroll: true,
dragstart: function(e) {
var dragHandle = $(e.currentTarget);
var eventElement = dragHandle.closest(".k-event");
var uid = eventElement.attr(kendo.attr("uid"));
var view = that.view();
that._dragging = true;
event = that.occurrenceByUid(uid);
clonedEvent = event.clone();
view._updateEventForResize(clonedEvent);
slot = view._slotByPosition(e.x.startLocation, e.y.startLocation);
if (that.trigger("resizeStart", { event })) {
e.preventDefault();
}
startTime = kendo.date.toUtcTime(clonedEvent.start);
endTime = kendo.date.toUtcTime(clonedEvent.end);
},
drag: function(e) {
if (!slot) {
return;
}
var dragHandle = $(e.currentTarget);
var dir = direction(dragHandle);
var view = that.view();
var currentSlot = view._slotByPosition(e.x.location, e.y.location);
if (!currentSlot || slot.groupIndex != currentSlot.groupIndex) {
return;
}
slot = currentSlot;
var originalStart = startTime;
var originalEnd = endTime;
if (dir == "south") {
if (!slot.isDaySlot && slot.end - kendo.date.toUtcTime(clonedEvent.start) >= view._timeSlotInterval()) {
if (clonedEvent.isAllDay) {
endTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
} else {
endTime = slot.endOffset(e.x.location, e.y.location, that.options.snap);
}
}
} else if (dir == "north") {
if (!slot.isDaySlot && kendo.date.toUtcTime(clonedEvent.end) - slot.start >= view._timeSlotInterval()) {
startTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
}
} else if (dir == "east") {
if (slot.isDaySlot && kendo.date.toUtcTime(kendo.date.getDate(slot.endDate())) >= kendo.date.toUtcTime(kendo.date.getDate(clonedEvent.start))) {
if (clonedEvent.isAllDay) {
endTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
} else {
endTime = slot.endOffset(e.x.location, e.y.location, that.options.snap);
}
} else if (!slot.isDaySlot && slot.end - kendo.date.toUtcTime(clonedEvent.start) >= view._timeSlotInterval()) {
endTime = slot.endOffset(e.x.location, e.y.location, that.options.snap);
}
} else if (dir == "west") {
if (slot.isDaySlot && kendo.date.toUtcTime(kendo.date.getDate(clonedEvent.end)) >= kendo.date.toUtcTime(kendo.date.getDate(slot.startDate()))) {
startTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
} else if (!slot.isDaySlot && kendo.date.toUtcTime(clonedEvent.end) - slot.start >= view._timeSlotInterval()) {
startTime = slot.startOffset(e.x.location, e.y.location, that.options.snap);
}
}
if (!that.trigger("resize", {
event,
slot: {
element: slot.element,
start: slot.startDate(),
end: slot.endDate()
},
start: kendo.timezone.toLocalDate(startTime),
end: kendo.timezone.toLocalDate(endTime),
resources: view._resourceBySlot(slot)
})) {
view._updateResizeHint(clonedEvent, slot.groupIndex, startTime, endTime);
} else {
startTime = originalStart;
endTime = originalEnd;
}
},
dragend: function(e) {
var dragHandle = $(e.currentTarget);
var start = new Date(clonedEvent.start.getTime());
var end = new Date(clonedEvent.end.getTime());
var dir = direction(dragHandle);
that._dragging = false;
that.view()._removeResizeHint();
if (dir == "south") {
end = kendo.timezone.toLocalDate(endTime);
} else if (dir == "north") {
start = kendo.timezone.toLocalDate(startTime);
} else if (dir == "east") {
if (slot.isDaySlot) {
end = kendo.date.getDate(kendo.timezone.toLocalDate(endTime));
} else {
end = kendo.timezone.toLocalDate(endTime);
}
} else if (dir == "west") {
if (slot.isDaySlot) {
start = new Date(kendo.timezone.toLocalDate(startTime));
start.setHours(0);
start.setMinutes(0);
} else {
start = kendo.timezone.toLocalDate(startTime);
}
}
var prevented = that.trigger("resizeEnd", {
event,
slot: {
element: slot.element,
start: slot.startDate(),
end: slot.endDate()
},
start,
end,
resources: that.view()._resourceBySlot(slot)
});
if (!prevented && end.getTime() >= start.getTime()) {
if (clonedEvent.start.getTime() != start.getTime() || clonedEvent.end.getTime() != end.getTime()) {
that.view()._updateEventForResize(event);
that._updateEvent(dir, event, {
start,
end
});
}
}
slot = null;
event = null;
},
dragcancel: function() {
that._dragging = false;
that.view()._removeResizeHint();
slot = null;
event = null;
}
});
},
_updateEvent: function(dir, event, eventInfo) {
var that = this;
var updateEvent = function(event, callback) {
var start;
try {
that._preventRefresh = true;
event.update(eventInfo);
that._convertDates(event);
start = event.start;
if (dir && event.duration() % MS_PER_DAY === 0 && start.getHours() === 0 && start.getMinutes() === 0 && start.getSeconds() === 0 && start.getMilliseconds() === 0) {
event.set("isAllDay", true);
}
} finally {
that._preventRefresh = false;
}
if (!that.trigger(SAVE, { event })) {
if (callback) {
callback();
}
if (!that._isMultiDrag) {
that.dataSource.sync();
}
}
};
var recurrenceHead = function(event) {
if (event.recurrenceRule) {
return that.dataSource.getByUid(event.uid);
} else {
return that.dataSource.get(event.recurrenceId);
}
};
var updateSeries = function() {
var head = recurrenceHead(event);
if (dir == "south" || dir == "north") {
if (eventInfo.start) {
var start = kendo.date.getDate(head.start);
kendo.date.setTime(start, getMilliseconds(eventInfo.start));
eventInfo.start = start;
}
if (eventInfo.end) {
var end = kendo.date.getDate(head.end);
kendo.date.setTime(end, getMilliseconds(eventInfo.end));
eventInfo.end = end;
}
}
that.dataSource._removeExceptions(head);
updateEvent(head);
};
var updateOccurrence = function() {
var head = recurrenceHead(event);
var eventUid;
var callback = function() {
that._convertDates(head);
if (that._selection) {
that._selection.events.push(eventUid);
}
};
var exception = head.toOccurrence({
start: event.start,
end: event.end
});
eventUid = exception.uid;
updateEvent(that.dataSource.add(exception), callback);
};
if (event.recurrenceRule || event.isOccurrence()) {
var recurrenceMessages = that.options.messages.recurrenceMessages;
that._showRecurringDialog(event, updateOccurrence, updateSeries, {
title: recurrenceMessages.editWindowTitle,
text: recurrenceMessages.editRecurring ? recurrenceMessages.editRecurring : EDITRECURRING,
occurrenceText: recurrenceMessages.editWindowOccurrence,
seriesText: recurrenceMessages.editWindowSeries
});
} else {
updateEvent(that.dataSource.getByUid(event.uid));
}
},
_modelForContainer: function(container) {
container = $(container).closest("[" + kendo.attr("uid") + "]");
return this.dataSource.getByUid(container.attr(kendo.attr("uid")));
},
showDialog: function(options) {
this.toolbar.find("." + FOCUSEDSTATE).removeClass(FOCUSEDSTATE);
this._editor.showDialog(options);
},
focus: function() {
this.wrapper.trigger("focus");
},
_confirmation: function(callback, model, isResetSeries) {
var editable = this.options.editable;
if (editable === true || editable.confirmation !== false) {
var messages = this.options.messages;
var title = messages.deleteWindowTitle;
var text = typeof editable.confirmation === STRING ? editable.confirmation : messages.editable.confirmation;
if (this._isEditorOpened() && model.isRecurring()) {
var recurrenceMessages = this.options.messages.recurrenceMessages;
title = recurrenceMessages.deleteWindowTitle;
if (model.isException() || model.isNew()) {
text = recurrenceMessages.deleteRecurringConfirmation ? recurrenceMessages.deleteRecurringConfirmation : DELETERECURRINGCONFIRM;
} else {
text = recurrenceMessages.deleteSeriesConfirmation ? recurrenceMessages.deleteSeriesConfirmation : DELETESERIESCONFIRM;
}
if (isResetSeries) {
title = recurrenceMessages.resetSeriesWindowTitle;
text = recurrenceMessages.resetSeriesConfirmation ? recurrenceMessages.resetSeriesConfirmation : RESETSERIESCONFIRM;
}
}
var buttons = [{
name: "destroy",
text: isResetSeries ? messages.resetSeries : messages.destroy,
click: function() {
callback();
}
}];
if (!(this._isMobile() && kendo.Pane)) {
buttons.push({
name: "canceledit",
text: messages.cancel,
click: function() {
callback(true);
}
});
}
this._unbindResize();
this.showDialog({
model,
text,
title,
buttons
});
this._bindResize();
} else {
callback();
}
},
addEvent: function(eventInfo) {
var editable = this._editor.editable;
var dataSource = this.dataSource;
var event;
eventInfo = eventInfo || {};
var prevented = this.trigger("add", { event: eventInfo });
if (!prevented && (editable && editable.end() || !editable)) {
this.cancelEvent();
if (eventInfo && eventInfo.toJSON) {
eventInfo = eventInfo.toJSON();
}
event = dataSource.add(eventInfo);
if (event) {
this.cancelEvent();
this._editEvent(event);
}
}
},
saveEvent: function() {
var that = this;
var editor = that._editor;
var dataSource = that.dataSource;
if (!editor) {
return;
}
var editable = editor.editable;
var container = editor.container;
var model = that._modelForContainer(container);
var events, i, event;
if (container && editable && editable.end() && !that.trigger(SAVE, {
container,
event: model
})) {
if (!model.isOccurrence() && !!model.recurrenceException && !model.recurrenceRule) {
events = dataSource.data();
for (i = events.length - 1; i >= 0; i -= 1) {
event = events[i];
if (event && event.recurrenceId === model.id) {
dataSource.remove(event);
}
}
model.set("recurrenceException", "");
}
if (!model.dirty && !model.isOccurrence()) {
that._convertDates(model, "remove");
}
dataSource.sync();
}
},
cancelEvent: function() {
var editor = this._editor;
var container = editor.container;
var model;
if (container) {
model = this._modelForContainer(container);
if (model && model.isOccurrence()) {
this._convertDates(model, "remove");
this._convertDates(this.dataSource.get(model.recurrenceId), "remove");
}
this.dataSource.cancelChanges(model);
editor.close();
}
},
editEvent: function(uid) {
var model = typeof uid == "string" ? this.occurrenceByUid(uid) : uid;
if (!model) {
return;
}
this.cancelEvent();
if (model.isRecurring()) {
this._editRecurringDialog(model);
} else {
this._editEvent(model);
}
},
_editEvent: function(model) {
this._preventRefresh = true;
this._unbindResize();
this._createPopupEditor(model);
this.toolbar.find("." + FOCUSEDSTATE).removeClass(FOCUSEDSTATE);
this._bindResize();
},
_editRecurringDialog: function(model) {
var that = this;
var editOccurrence = function() {
if (model.isException()) {
that._editEvent(model);
} else {
that.addEvent(model);
}
};
var editSeries = function() {
if (model.recurrenceId) {
model = that.dataSource.get(model.recurrenceId);
}
that._editEvent(model);
};
var recurrenceMessages = that.options.messages.recurrenceMessages;
that._showRecurringDialog(model, editOccurrence, editSeries, {
title: recurrenceMessages.editWindowTitle,
text: recurrenceMessages.editRecurring ? recurrenceMessages.editRecurring : EDITRECURRING,
occurrenceText: recurrenceMessages.editWindowOccurrence,
seriesText: recurrenceMessages.editWindowSeries
});
},
_showRecurringDialog: function(model, editOccurrence, editSeries, messages) {
var editable = this.options.editable;
var editRecurringMode = isPlainObject(editable) ? editable.editRecurringMode : "dialog";
if (editRecurringMode === "occurrence" || this._isMultiDrag) {
editOccurrence();
} else if (editRecurringMode === "series") {
editSeries();
} else {
this._unbindResize();
this.showDialog({
model,
title: messages.title,
text: messages.text,
buttons: [{
text: messages.occurrenceText,
click: editOccurrence
}, {
text: messages.seriesText,
click: editSeries
}]
});
this._bindResize();
}
},
_createButton: function(command) {
var template = command.template || COMMANDBUTTONTMPL, commandName = typeof command === STRING ? command : command.name || command.text, options = {
className: "k-scheduler-" + (commandName || "").replace(/\s/g, ""),
text: commandName,
attr: ""
};
if (!commandName && !(isPlainObject(command) && command.template)) {
throw new Error("Custom commands should have name specified");
}
if (isPlainObject(command)) {
if (command.className) {
command.className += " " + options.className;
}
if (commandName === "edit" && isPlainObject(command.text)) {
command = extend(true, {}, command);
command.text = command.text.edit;
}
options = extend(true, options, defaultCommands[commandName], command);
} else {
options = extend(true, options, defaultCommands[commandName]);
}
return kendo.template(template)(options);
},
_convertDates: function(model, method) {
var timezone = this.dataSource.reader.timezone;
var startTimezone = model.startTimezone;
var endTimezone = model.endTimezone;
var start = model.start;
var end = model.start;
method = method || "apply";
startTimezone = startTimezone || endTimezone;
endTimezone = endTimezone || startTimezone;
if (startTimezone) {
if (timezone) {
if (method === "apply") {
start = kendo.timezone.convert(model.start, timezone, startTimezone);
end = kendo.timezone.convert(model.end, timezone, endTimezone);
} else {
start = kendo.timezone.convert(model.start, startTimezone, timezone);
end = kendo.timezone.convert(model.end, endTimezone, timezone);
}
} else {
start = kendo.timezone[method](model.start, startTimezone);
end = kendo.timezone[method](model.end, endTimezone);
}
model._set("start", start);
model._set("end", end);
}
},
_createEditor: function() {
var that = this;
var editor;
if (this._isMobile() && kendo.Pane) {
editor = that._editor = new MobileEditor(this.wrapper, extend({}, this.options, {
target: this,
timezone: that.dataSource.reader.timezone,
resources: that.resources,
createButton: this._createButton.bind(this)
}));
} else {
editor = that._editor = new PopupEditor(this.wrapper, extend({}, this.options, {
target: this,
createButton: this._createButton.bind(this),
timezone: that.dataSource.reader.timezone,
resources: that.resources
}));
}
editor.bind("cancel", function(e) {
if (that.trigger("cancel", {
container: e.container,
event: e.model
})) {
e.preventDefault();
return;
}
that._preventRefresh = false;
that.cancelEvent();
if (that._attemptRefresh) {
that.refresh();
}
that.focus();
});
editor.bind("edit", function(e) {
if (that.trigger(EDIT, {
container: e.container,
event: e.model
})) {
e.preventDefault();
}
});
editor.bind("save", function() {
that._preventRefresh = false;
that.saveEvent();
});
editor.bind("remove", function(e) {
that._preventRefresh = false;
that.removeEvent(e.model);
});
editor.bind("resetSeries", function(e) {
that._confirmation(function(cancel) {
that._preventRefresh = false;
if (!cancel) {
that.dataSource._removeExceptions(e.model);
that.saveEvent();
}
}, e.model, true);
});
},
_createPopupEditor: function(model) {
var editor = this._editor;
if (!model.isNew() || model.isOccurrence()) {
if (model.isOccurrence()) {
this._convertDates(model.recurrenceId ? this.dataSource.get(model.recurrenceId) : model);
}
this._convertDates(model);
}
this.editable = editor.editEvent(model);
},
removeEvent: function(uid) {
var that = this, model = typeof uid == "string" ? that.occurrenceByUid(uid) : uid;
if (!model) {
return;
}
if (model.isRecurring()) {
that._deleteRecurringDialog(model);
} else {
that._confirmation(function(cancel) {
if (!cancel) {
that._removeEvent(model);
}
}, model);
}
},
occurrenceByUid: function(uid) {
var occurrence = this.dataSource.getByUid(uid);
if (!occurrence) {
occurrence = getOccurrenceByUid(this._data, uid);
}
return occurrence;
},
occurrencesInRange: function(start, end) {
return new kendo.data.Query(this._data).filter({
logic: "or",
filters: [{
logic: "and",
filters: [
{
field: "start",
operator: "gte",
value: start
},
{
field: "end",
operator: "gte",
value: start
},
{
field: "start",
operator: "lt",
value: end
}
]
}, {
logic: "and",
filters: [{
field: "start",
operator: "lte",
value: start
}, {
field: "end",
operator: "gt",
value: start
}]
}]
}).toArray();
},
_removeEvent: function(model) {
if (!this.trigger(REMOVE, { event: model })) {
if (this.dataSource.remove(model)) {
this.dataSource.sync();
}
}
},
_deleteRecurringDialog: function(model) {
var that = this;
var currentModel = model;
var editable = that.options.editable;
var deleteOccurrence;
var deleteSeries;
var createException;
var deleteOccurrenceConfirmation;
var deleteSeriesConfirmation;
var createExceptionConfirmation;
var editRecurringMode = isPlainObject(editable) ? editable.editRecurringMode : "dialog";
deleteOccurrence = function() {
var occurrence = currentModel.recurrenceId ? currentModel : currentModel.toOccurrence();
var head = that.dataSource.get(occurrence.recurrenceId);
that._convertDates(head);
that._removeEvent(occurrence);
};
deleteSeries = function() {
if (currentModel.recurrenceId) {
currentModel = that.dataSource.get(currentModel.recurrenceId);
}
that._removeEvent(currentModel);
};
createException = function() {
that.dataSource.remove(currentModel);
that.dataSource.sync();
};
if (editRecurringMode != "dialog" || that._isEditorOpened()) {
deleteOccurrenceConfirmation = function() {
that._confirmation(function(cancel) {
if (!cancel) {
deleteOccurrence();
}
}, currentModel);
};
deleteSeriesConfirmation = function() {
that._confirmation(function(cancel) {
if (!cancel) {
deleteSeries();
}
}, currentModel);
};
createExceptionConfirmation = function() {
that._confirmation(function(cancel) {
if (!cancel) {
createException();
}
}, currentModel);
};
}
var seriesCallback = deleteSeriesConfirmation || deleteSeries;
var occurrenceCallback = deleteOccurrenceConfirmation || deleteOccurrence;
var exeptionCallback = createExceptionConfirmation || createException;
if (that._isEditorOpened()) {
if (model.isException()) {
occurrenceCallback();
} else if (model.isNew()) {
exeptionCallback();
} else {
seriesCallback();
}
} else {
var recurrenceMessages = that.options.messages.recurrenceMessages;
that._showRecurringDialog(model, occurrenceCallback, seriesCallback, {
title: recurrenceMessages.deleteWindowTitle,
text: recurrenceMessages.deleteRecurring ? recurrenceMessages.deleteRecurring : DELETERECURRING,
occurrenceText: recurrenceMessages.deleteWindowOccurrence,
seriesText: recurrenceMessages.deleteWindowSeries
});
}
},
_isEditorOpened: function() {
return !!this._editor.container;
},
_unbindView: function(view) {
var that = this;
view.destroy();
},
_bindView: function(view) {
var that = this;
if (that.options.editable) {
if (that._viewRemoveHandler) {
view.unbind(REMOVE, that._viewRemoveHandler);
}
that._viewRemoveHandler = function(e) {
that.removeEvent(e.uid);
};
view.bind(REMOVE, that._viewRemoveHandler);
if (that._viewAddHandler) {
view.unbind(ADD, that._viewAddHandler);
}
that._viewAddHandler = function(e) {
that.addEvent(e.eventInfo);
};
view.bind(ADD, this._viewAddHandler);
if (that._viewEditHandler) {
view.unbind(EDIT, that._viewEditHandler);
}
that._viewEditHandler = function(e) {
that.editEvent(e.uid);
};
view.bind(EDIT, this._viewEditHandler);
}
if (that._viewNavigateHandler) {
view.unbind("navigate", that._viewNavigateHandler);
}
that._viewNavigateHandler = function(e) {
if (e.action) {
if (!that.trigger("navigate", {
view: e.view,
isWorkDay: e.isWorkDay,
action: e.action,
date: e.date
})) {
that.date(e.date);
}
} else if (e.view) {
var switchWorkDay = "isWorkDay" in e;
var action = switchWorkDay ? "changeWorkDay" : "changeView";
if (!that.trigger("navigate", {
view: e.view,
isWorkDay: e.isWorkDay,
action,
date: e.date
})) {
if (switchWorkDay) {
that._workDayMode = e.isWorkDay;
}
that._selectView(e.view);
that.date(e.date);
}
}
};
view.bind("navigate", that._viewNavigateHandler);
if (that._viewActivateHandler) {
view.unbind("activate", that._viewActivateHandler);
}
that._viewActivateHandler = function() {
var view = this;
if (that._selection) {
view.constrainSelection(that._selection);
if ($(document.activeElement).closest(".k-scheduler-toolbar").length === 0) {
if (document.activeElement !== that.wrapper[0]) {
that.element.trigger("focus");
} else {
that._select();
}
}
that._adjustSelectedDate();
}
};
view.bind("activate", that._viewActivateHandler);
},
_selectView: function(name) {
var that = this;
if (name && that.views[name]) {
if (that._selectedView) {
that._unbindView(that._selectedView);
}
that._selectedView = that._renderView(name);
that._selectedViewName = name;
if (this._initialSize) {
this._initialSize = false;
this._onMediaChange(window.matchMedia(MIN_SCREEN));
}
if (that._viewsCount > 1 && !that._isMobile()) {
var viewElementToSelect = that.toolbar.find("[" + kendo.attr("name") + "=" + name + "]");
var viewsDropdown = that.toolbar.find(".k-views-dropdown");
var viewsGroupEl = viewElementToSelect.closest(".k-button-group");
var viewsButtonGroup = viewsGroupEl.data("kendoButtonGroup");
viewsDropdown.val(name);
if (viewsButtonGroup) {
viewsButtonGroup.select(viewElementToSelect);
}
} else if (that._viewsCount > 1) {
var viewSelect = that.toolbar.find(".k-scheduler-mobile-views");
viewSelect.find("[value=" + name.replace(/\./g, "\\.") + "]").prop("selected", "selected");
}
}
},
view: function(name) {
var that = this;
if (name) {
if (name === "year") {
that.wrapper.removeAttr("tabindex");
} else {
that.wrapper.attr("tabindex", 0);
}
that._selectView(name);
that.rebind();
return;
}
return that._selectedView;
},
viewName: function() {
return this.view().name;
},
_renderView: function(name) {
var view = this._initializeView(name);
this._bindView(view);
if (kendo.support.mouseAndTouchPresent || kendo.support.pointers) {
view.content.css("-ms-touch-action", "pinch-zoom");
view.content.css("touch-action", "pinch-zoom");
}
this._model.set("formattedDate", view.dateForTitle());
this._model.set("formattedShortDate", view.shortDateForTitle());
this._model.set("formattedMobileDate", view.mobileDateForTitle ? view.mobileDateForTitle() : view.shortDateForTitle());
this._model.set("formattedYear", view.mobileYearForTitle ? view.mobileYearForTitle() : kendo.format("{0:yyyy}", view.startDate()));
return view;
},
resize: function(force) {
var size = this.getSize();
var currentSize = this._size;
var view = this.view();
if (!view || !view.groups) {
return;
}
if (force || !currentSize || size.width !== currentSize.width || size.height !== currentSize.height) {
this.refresh({ action: "resize" });
this._size = size;
}
},
_adjustSelectedDate: function() {
var date = this._model.selectedDate, selection = this._selection, start = selection.start;
if (start && !kendo.date.isInDateRange(date, getDate(start), getDate(selection.end))) {
date.setFullYear(start.getFullYear(), start.getMonth(), start.getDate());
}
},
_initializeView: function(name) {
var view = this.views[name];
if (view) {
var isSettings = isPlainObject(view), overrideOptions = {}, type = view.type;
if (typeof type === STRING) {
type = kendo.getter(view.type)(window);
}
if (isSettings && view.workDays && view.workDays.length) {
overrideOptions.workDays = view.workDays;
}
if (type) {
view = new type(this.wrapper, trimOptions(extend(true, {}, this.options, isSettings ? view : {}, {
resources: this.resources,
date: this.date(),
startTime: kendo.parseDate(view.startTime) || kendo.parseDate(this.options.startTime),
endTime: kendo.parseDate(view.endTime) || kendo.parseDate(this.options.endTime),
showWorkHours: this._workDayMode
}), overrideOptions));
} else {
throw new Error("There is no such view");
}
}
return view;
},
_views: function() {
var views = this.options.views;
var view;
var defaultView;
var selected;
var isSettings;
var name;
var type;
var idx;
var length;
this.views = {};
this._viewsCount = 0;
for (idx = 0, length = views.length; idx < length; idx++) {
var hasType = false;
view = views[idx];
isSettings = isPlainObject(view);
if (isSettings) {
type = name = view.type ? view.type : view;
if (typeof type !== STRING) {
name = view.name || view.title;
hasType = true;
}
} else {
type = name = view;
view = {};
}
defaultView = defaultViews[name];
if (defaultView && !hasType) {
view.type = defaultView.type;
defaultView.title = this.options.messages.views[name];
if (defaultView.type === "day") {
defaultView.messages = { allDay: this.options.messages.allDay };
} else if (defaultView.type === "agenda") {
defaultView.messages = {
event: this.options.messages.event,
date: this.options.messages.date,
time: this.options.messages.time
};
}
}
view = extend({ title: name }, defaultView, isSettings ? view : {});
if (name) {
this.views[name] = view;
this._viewsCount++;
if (!selected || view.selected) {
selected = name;
}
}
}
if (selected) {
this._selectedViewName = selected;
}
},
rebind: function() {
var that = this, resources = that.resources, resourceFetchArray = [];
if (that._preventRebind) {
that._preventRebind = false;
return;
}
if (that.options.autoBind === false && resources && resources.length > 0) {
resources.forEach(function(resource) {
if (resource.dataSource.data().length === 0) {
that._preventRebind = true;
resourceFetchArray.push(resource.dataSource.fetch());
}
});
$.when.apply(null, resourceFetchArray).then(function() {
that.dataSource.fetch();
that._preventRebind = false;
});
} else {
that.dataSource.fetch();
}
},
_dataSource: function() {
var that = this, options = that.options, dataSource = options.dataSource;
dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
if (options.timezone && !(dataSource instanceof SchedulerDataSource)) {
dataSource = extend(true, dataSource, { schema: { timezone: options.timezone } });
} else if (dataSource instanceof SchedulerDataSource) {
options.timezone = dataSource.options.schema ? dataSource.options.schema.timezone : "";
}
if (that.dataSource && that._refreshHandler) {
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
} else {
that._refreshHandler = that.refresh.bind(that);
that._progressHandler = that._requestStart.bind(that);
that._errorHandler = that._error.bind(that);
}
that.dataSource = kendo.data.SchedulerDataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(ERROR, that._errorHandler);
that.options.dataSource = that.dataSource;
},
_error: function() {
this._progress(false);
},
_requestStart: function() {
this._progress(true);
},
_progress: function(toggle) {
var element = this.element.find(".k-scheduler-content");
kendo.ui.progress(element, toggle);
},
_resources: function() {
var that = this;
var resources = that.options.resources;
var resourcePromises = [];
for (var idx = 0; idx < resources.length; idx++) {
var resource = resources[idx];
var field = resource.field;
var name = resource.name || field;
var dataSource = resource.dataSource;
if (!field || !dataSource) {
throw new Error("The \"field\" and \"dataSource\" options of the scheduler resource are mandatory.");
}
that.resources.push({
field,
name,
title: resource.title || field,
dataTextField: resource.dataTextField || "text",
dataValueField: resource.dataValueField || "value",
dataColorField: resource.dataColorField || "color",
dataParentValueField: resource.dataParentValueField || "parentValue",
valuePrimitive: resource.valuePrimitive != null ? resource.valuePrimitive : true,
multiple: resource.multiple || false,
dataSource: that._resourceDataSource(dataSource, name, resourcePromises)
});
}
if (!that.options.autoBind) {
that._selectView(that._selectedViewName);
} else {
$.when.apply(null, resourcePromises).then(function() {
that.view(that._selectedViewName);
});
}
},
_resourceDataSource: function(resourceDS, groupName, promises) {
var that = this;
var dataSource = isArray(resourceDS) ? { data: resourceDS } : resourceDS;
var dataSourceInstance = kendo.data.DataSource.create(dataSource);
if (that.options.autoBind) {
promises.push(dataSourceInstance.fetch(function() {
that._bindResourceEvents(this, groupName);
}));
} else {
that._bindResourceEvents(dataSourceInstance, groupName);
}
return dataSourceInstance;
},
_bindResourceEvents: function(resourceDS, groupName) {
var that = this;
var isGrouped = that.options.group && that.options.group.resources.length;
var isResourceGrouped = isGrouped && that.options.group.resources.indexOf(groupName) > -1;
if (!that._resourceRefreshHandler && isResourceGrouped) {
that._resourceRefreshHandler = that._refreshResource.bind(that);
that._resourceErrorHandler = that._error.bind(that);
}
if (isResourceGrouped) {
resourceDS.bind(CHANGE, that._resourceRefreshHandler).bind(ERROR, that._resourceErrorHandler);
}
},
_refreshResource: function() {
var that = this;
var preventRefresh = that._editor && that._editor.editable || that._preventRefresh;
if (!preventRefresh) {
that.view(that._selectedViewName);
}
},
_initModel: function() {
var that = this;
that._model = kendo.observable({
selectedDate: new Date(this.options.date),
formattedDate: "",
formattedShortDate: ""
});
that._model.bind(CHANGE, function(e) {
if (e.field === "selectedDate") {
that.view(that._selectedViewName);
}
});
},
_wrapper: function() {
var that = this;
var options = that.options;
var height = options.height;
var width = options.width;
that.wrapper = that.element.addClass("k-scheduler");
if (that._isMobile()) {
that.wrapper.addClass("k-scheduler-mobile");
}
if (height) {
that.wrapper.height(height);
}
if (width) {
that.wrapper.width(width);
}
},
date: function(value) {
if (value != null && getDate(value) >= getDate(this.options.min) && getDate(value) <= getDate(this.options.max)) {
this._model.set("selectedDate", value);
}
return getDate(this._model.get("selectedDate"));
},
_processHandlers: function(defaults) {
var that = this;
that._pdfClickHandler = (e) => {
e.preventDefault();
that.saveAsPDF();
};
that._createClickHandler = (e) => {
e.preventDefault();
that.addEvent();
};
that._calendarClickHandler = that._currentClickHandler = (e) => {
e.preventDefault();
that._showCalendar(e.target);
};
that._todayClickHandler = (e) => {
e.preventDefault();
var timezone = that.options.timezone, currentDate = new Date(), date;
if (timezone) {
var timezoneOffset = kendo.timezone.offset(currentDate, timezone);
date = kendo.timezone.convert(currentDate, currentDate.getTimezoneOffset(), timezoneOffset);
} else {
date = currentDate;
}
if (!that.trigger("navigate", {
view: that._selectedViewName,
action: "today",
date
})) {
that.date(date);
}
};
that._previousClickHandler = (e) => {
e.preventDefault();
var date = that.view().previousDate();
if (!that.trigger("navigate", {
view: that._selectedViewName,
action: "previous",
date
})) {
that.date(date);
}
};
that._nextClickHandler = (e) => {
e.preventDefault();
var date = that.view().nextDate();
if (!that.trigger("navigate", {
view: that._selectedViewName,
action: "next",
date
})) {
that.date(date);
}
};
that._refreshClickHandler = (e) => {
e.preventDefault();
var name = that.view().name;
if (!that.trigger("navigate", {
view: name,
action: "changeView",
date: that.date()
})) {
that.view(name);
}
};
that._viewClickHandler = (e) => {
var name = e.target.attr(kendo.attr("name"));
if (!that.trigger("navigate", {
view: name,
action: "changeView",
date: that.date()
})) {
that.view(name);
}
};
Object.values(defaults).map((t) => {
if (t.name) {
t.click = that["_" + t.name + "ClickHandler"];
}
});
},
_processDefaults: function() {
var views = this.views, defaults = $.extend(true, {}, DEFAULT_TOOLS), viewsButtons = [], isRtl = kendo.support.isRtl(this.wrapper), viewsDdl, viewsMobile;
Object.keys(views).map((name) => {
var current = $.extend(true, {}, defaults.view);
current.text = views[name].title;
const refAttribute = `ref-view-${name.toLowerCase()}`;
current.attributes = {
[refAttribute]: "",
"data-name": name
};
current.groupClass = "k-scheduler-views";
defaults[name] = current;
viewsButtons.push(name);
});
if (viewsButtons.length > 1) {
viewsDdl = VIEWS_DROPDOWN_TEMPLATE({
views: this.views,
label: this.options.messages.selectView,
type: "k-views-dropdown"
});
viewsMobile = VIEWS_DROPDOWN_TEMPLATE({
views: this.views,
label: this.options.messages.selectView,
type: "k-scheduler-mobile-views"
});
}
defaults.viewsDdl = { template: viewsDdl };
defaults.viewsMobile = { template: viewsMobile };
this._viewsButtons = viewsButtons;
if (isRtl) {
defaults.previous.icon = "caret-alt-right";
defaults.next.icon = "caret-alt-left";
}
return defaults;
},
_processToolbarArray: function() {
var options = this.options, toolbarOptions = options.toolbar, commands = [], isMobile = this._isMobile(), tools, pdf, search;
if (toolbarOptions) {
commands = Array.isArray(toolbarOptions) ? toolbarOptions : [toolbarOptions];
}
pdf = $.grep(commands, function(item) {
return item == "pdf" || item.name == "pdf";
}).length > 0;
search = $.grep(commands, function(item) {
return item == "search" || item.name == "search";
}).length > 0;
if (isMobile) {
tools = [...defaultMobileToolsFirst];
if (options.editable) {
tools[0].push("create");
}
} else {
tools = [...defaultDesktopTools];
}
if (search) {
tools.push("search");
}
if (pdf) {
if (isMobile) {
tools[0].unshift("pdfMobile");
} else {
tools.unshift("pdf");
}
}
if (this._viewsCount === 1) {
tools.push("refresh");
} else if (this._viewsCount > 1) {
if (isMobile) {
tools.push("viewsMobile");
} else {
tools.push("viewsDdl");
tools.push(this._viewsButtons);
}
}
return tools;
},
_processViewTools: function(items) {
var result = items, viewsIndex = -1;
items.some((item, index) => {
if (item === "views" || item.name === "views") {
viewsIndex = index;
return true;
}
return false;
});
if (viewsIndex > -1) {
result.splice(viewsIndex, 1, "viewsDdl", this._viewsButtons);
}
return result;
},
_processToolbarItems: function() {
var desktopItems = this.options.toolbar.items.desktop || this.options.toolbar.items, mobileItems = this.options.toolbar.items.mobile || this.options.toolbar.items, isMobile = this._isMobile();
if (!isMobile) {
if (desktopItems.main) {
if (desktopItems.navigation) {
desktopItems = desktopItems.main.concat(desktopItems.navigation);
} else {
desktopItems = desktopItems.main;
}
}
return { tools: this._processViewTools(desktopItems) };
} else {
if (Array.isArray(mobileItems)) {
return {
tools: this._processViewTools(mobileItems),
navigation: []
};
} else {
return {
tools: this._processViewTools(mobileItems.main),
navigation: this._processViewTools(mobileItems.navigation)
};
}
}
},
_processTools: function() {
var options = this.options, toolbarOptions = options.toolbar;
if (toolbarOptions && toolbarOptions.items) {
return this._processToolbarItems();
} else {
return { tools: this._processToolbarArray() };
}
},
_toolbar: function() {
var that = this;
var options = that.options;
var defaults = that._processDefaults();
var { tools, navigation } = that._processTools();
var toolbar = $("<div class='k-scheduler-toolbar'>");
var secondToolbar;
that._processHandlers(defaults);
that.wrapper.append(toolbar);
that.toolbar = toolbar;
toolbar.kendoToolBar({
resizable: false,
tools,
defaultTools: defaults,
parentMessages: options.messages
});
if (that._isMobile() && (!navigation || navigation.length !== 0)) {
secondToolbar = $("<div class='k-scheduler-toolbar'>");
that.wrapper.append(secondToolbar);
that.toolbar = that.toolbar.add(secondToolbar);
secondToolbar.kendoToolBar({
resizable: false,
tools: navigation || [...defaultMobileToolsSecond],
defaultTools: defaults,
parentMessages: options.messages
});
}
that.toolbar.find(".k-nav-current .k-button-text").attr("data-" + kendo.ns + "bind", "text: formattedDate");
that.toolbar.find(".k-m-date-format").attr("data-" + kendo.ns + "bind", "text: formattedMobileDate");
that.toolbar.find(".k-y-date-format").attr("data-" + kendo.ns + "bind", "text: formattedYear");
kendo.bind(that.toolbar, that._model);
that.toolbar.find(".k-scheduler-search-input").attr({
placeholder: options.messages.search,
title: options.messages.search
});
toolbar.on("input" + NS, ".k-scheduler-search-input", kendo.throttle(function(e) {
that.dataSource.filter({
logic: "or",
filters: [{
field: "title",
operator: "contains",
value: e.target.value
}, {
field: "description",
operator: "contains",
value: e.target.value
}]
});
}, 250));
toolbar.on(CHANGE + NS, ".k-views-dropdown, .k-scheduler-mobile-views", function() {
var name = this.value;
if (!that.trigger("navigate", {
view: name,
action: "changeView",
date: that.date()
})) {
that.view(name);
}
});
that._initialSize = true;
},
_showCalendar: function(targetElm) {
var that = this, target = targetElm || that.toolbar.find(".k-nav-current"), html = $("<div class=\"k-calendar-container\"><div class=\"k-scheduler-calendar\"></div></div>");
if (!that.popup) {
that.popup = new Popup(html, {
anchor: target,
activate: function() {
if (that.popup && that.calendar) {
that.popup._toggleResize(false);
that.calendar.element.find("table").trigger("focus");
that.popup._toggleResize(true);
}
},
open: function() {
if (!that.calendar) {
that.calendar = new Calendar(this.element.find(".k-scheduler-calendar"), {
change: function() {
var date = this.value();
if (!that.trigger("navigate", {
view: that._selectedViewName,
action: "changeDate",
date
})) {
that.date(date);
that.popup.close();
}
if (!that._isMobile()) {
that._selectedView.element.trigger("focus");
that.toolbar.find(".k-nav-current").trigger("focus").addClass(FOCUSEDSTATE);
}
},
min: that.options.min,
max: that.options.max
});
}
that.calendar.element.on("keydown" + NS, function(e) {
if (e.keyCode === keys.ESC || e.keyCode === keys.TAB) {
that.popup.close();
that._selectedView.element.trigger("focus");
that.toolbar.find(".k-nav-current").trigger("focus").addClass(FOCUSEDSTATE);
}
});
that.calendar.setOptions({
start: that._selectedViewName === "year" ? "decade" : "month",
depth: that._selectedViewName === "year" ? "decade" : "month"
});
that.calendar.value(that.date());
},
copyAnchorStyles: false
});
}
that.popup.open();
},
refresh: function(e) {
var that = this;
var view = this.view();
var preventRefresh = e && e.action === "itemchange" && (this._editor.editable || this._preventRefresh) || this.dataSource.options.type === "signalr" && this._preventRefresh;
this._progress(false);
e = e || {};
if (!view) {
return;
}
if (preventRefresh) {
this._attemptRefresh = true && this.dataSource.options.type === "signalr";
return;
}
if (this.trigger("dataBinding", {
action: e.action || "rebind",
index: e.index,
items: e.items
})) {
return;
}
if (!(e && e.action === "resize") && this._editor) {
this._editor.close();
}
this._data = this.dataSource.expand(view.startDate(), view.visibleEndDate());
view.refreshLayout();
view.render(this._data);
this.trigger("dataBound");
this._attemptRefresh = false;
},
slotByPosition: function(x, y) {
var view = this.view();
if (!view._slotByPosition) {
return null;
}
var slot = view._slotByPosition(x, y);
if (!slot) {
return null;
}
return {
startDate: slot.startDate(),
endDate: slot.endDate(),
groupIndex: slot.groupIndex,
element: slot.element,
isDaySlot: slot.isDaySlot
};
},
slotByElement: function(element) {
var el = $(element), offset = el.offset(), width = el.width(), height = el.height(), centerX = offset.left + width / 2, centerY = offset.top + height / 2, offset = $(element).offset();
return this.slotByPosition(centerX, centerY);
},
resourcesBySlot: function(slot) {
return this.view()._resourceBySlot(slot);
}
});
var defaultViews = {
day: { type: "kendo.ui.DayView" },
week: { type: "kendo.ui.WeekView" },
workWeek: { type: "kendo.ui.WorkWeekView" },
agenda: { type: "kendo.ui.AgendaView" },
month: { type: "kendo.ui.MonthView" },
timeline: { type: "kendo.ui.TimelineView" },
timelineWeek: { type: "kendo.ui.TimelineWeekView" },
timelineWorkWeek: { type: "kendo.ui.TimelineWorkWeekView" },
timelineMonth: { type: "kendo.ui.TimelineMonthView" },
year: { type: "kendo.ui.YearView" }
};
ui.plugin(Scheduler);
if (kendo.PDFMixin) {
kendo.PDFMixin.extend(Scheduler.prototype);
var SCHEDULER_EXPORT = "k-scheduler-pdf-export";
Scheduler.fn._drawPDF = function(progress) {
var wrapper = this.wrapper;
var styles = wrapper[0].style.cssText;
wrapper.css({
width: wrapper.width(),
height: wrapper.height()
});
wrapper.addClass(SCHEDULER_EXPORT);
var scheduler = this;
var promise = new $.Deferred();
var table = wrapper.find(".k-scheduler-content").find("table").css("table-layout", "auto");
setTimeout(function() {
table.css("table-layout", "fixed");
scheduler.resize(true);
scheduler._drawPDFShadow({}, { avoidLinks: scheduler.options.pdf.avoidLinks }).done(function(group) {
var args = {
page: group,
pageNumber: 1,
progress: 1,
totalPages: 1
};
progress.notify(args);
promise.resolve(args.page);
}).fail(function(err) {
promise.reject(err);
}).always(function() {
wrapper[0].style.cssText = styles;
wrapper.removeClass(SCHEDULER_EXPORT);
scheduler.resize(true);
scheduler.resize(true);
});
});
return promise;
};
}
var TimezoneEditor = Widget.extend({
init: function(element, options) {
var that = this, zones = kendo.timezone.windows_zones;
if (!zones || !kendo.timezone.zones_titles) {
throw new Error("kendo.timezones.min.js is not included.");
}
Widget.fn.init.call(that, element, options);
that.wrapper = that.element;
that._zonesQuery = new kendo.data.Query(zones);
that._zoneTitleId = kendo.guid();
that._zoneTitlePicker();
that._zonePicker();
that._zoneTitle.bind("cascade", function() {
if (!this.value()) {
that._zone.wrapper.hide();
}
});
that._zone.bind("cascade", function() {
that._value = this.value();
that.trigger(CHANGE);
});
that.value(that.options.value);
},
options: {
name: "TimezoneEditor",
value: "",
optionLabel: "No timezone"
},
events: [CHANGE],
_zoneTitlePicker: function() {
var that = this, zoneTitle = $("<input id=\"" + that._zoneTitleId + "\" aria-label=\"" + that.options.title + "\"/>").appendTo(that.wrapper);
that._zoneTitle = new kendo.ui.DropDownList(zoneTitle, {
dataSource: kendo.timezone.zones_titles,
dataValueField: "other_zone",
dataTextField: "name",
optionLabel: that.options.optionLabel
});
},
_zonePicker: function() {
var that = this, zone = $("<input aria-label=\"" + that.options.title + "\"/>").appendTo(this.wrapper);
that._zone = new kendo.ui.DropDownList(zone, {
dataValueField: "zone",
dataTextField: "territory",
dataSource: that._zonesQuery.data,
cascadeFrom: that._zoneTitleId,
dataBound: function() {
that._value = this.value();
this.wrapper.toggle(this.dataSource.view().length > 1);
}
});
that._zone.wrapper.hide();
},
destroy: function() {
Widget.fn.destroy.call(this);
kendo.destroy(this.wrapper);
},
value: function(value) {
var that = this, zone;
if (value === undefined) {
return that._value;
}
zone = that._zonesQuery.filter({
field: "zone",
operator: "eq",
value
}).data[0];
if (zone) {
that._zoneTitle.value(zone.other_zone);
that._zone.value(zone.zone);
} else {
that._zoneTitle.select(0);
}
}
});
ui.plugin(TimezoneEditor);
var ZONETITLEOPTIONTEMPLATE = kendo.template(({ name, other_zone }) => `<option value="${other_zone}">${name}</option>`);
var ZONEOPTIONTEMPLATE = kendo.template(({ zone, territory }) => `<option value="${zone}">${territory}</option>`);
var MobileTimezoneEditor = Widget.extend({
init: function(element, options) {
var that = this, zones = kendo.timezone.windows_zones;
if (!zones || !kendo.timezone.zones_titles) {
throw new Error("kendo.timezones.min.js is not included.");
}
Widget.fn.init.call(that, element, options);
that.wrapper = that.element;
that._zonesQuery = new kendo.data.Query(zones);
that._zoneTitlePicker();
that._zonePicker();
that.value(that.options.value);
},
options: {
name: "MobileTimezoneEditor",
optionLabel: "No timezone",
value: ""
},
events: [CHANGE],
_bindZones: function(value) {
var data = value ? this._filter(value) : [];
this._zone.html(this._options(data, ZONEOPTIONTEMPLATE));
},
_filter: function(value) {
return this._zonesQuery.filter({
field: "other_zone",
operator: "eq",
value
}).data;
},
_options: function(data, template, optionLabel) {
var idx = 0;
var html = "";
var length = data.length;
if (optionLabel) {
html += template({
other_zone: "",
name: optionLabel
});
}
for (; idx < length; idx++) {
html += template(data[idx]);
}
return html;
},
_zoneTitlePicker: function() {
var that = this;
var options = that._options(kendo.timezone.zones_titles, ZONETITLEOPTIONTEMPLATE, that.options.optionLabel);
that._zoneTitle = $("<select>" + options + "</select>").appendTo(that.wrapper).on("change", function() {
var value = this.value;
var zone = that._zonePickerLabel;
var zoneSelect = zone.find("select");
that._bindZones(value);
if (value && zoneSelect.children().length > 1) {
zone.show();
} else {
zone.hide();
}
that._value = that._zone[0].value;
that.trigger(CHANGE);
});
},
_zonePicker: function() {
var that = this;
that._zonePickerLabel = $("<li class='k-item k-listgroup-item k-zonepicker'>" + "<label class='k-label k-listgroup-form-row'>" + "<span class='k-item-title k-listgroup-form-field-label'></span>" + "<div class='k-listgroup-form-field-wrapper'></div>" + "</label>" + "</li>").hide();
that._zone = $("<select></select>").appendTo(that._zonePickerLabel.find("div")).on("change", function() {
that._value = this.value;
that.trigger(CHANGE);
});
this.wrapper.closest(".k-item").after(that._zonePickerLabel);
that._bindZones(that._zoneTitle.val());
that._value = that._zone[0].value;
},
destroy: function() {
Widget.fn.destroy.call(this);
kendo.destroy(this.wrapper);
},
value: function(value) {
var that = this;
var zonePicker = that._zone;
var other_zone = "";
var zone_value = "";
var zone;
if (value === undefined) {
return that._value;
}
zone = that._zonesQuery.filter({
field: "zone",
operator: "eq",
value
}).data[0];
if (zone) {
zone_value = zone.zone;
other_zone = zone.other_zone;
}
that._zoneTitle.val(other_zone);
that._bindZones(other_zone);
zonePicker.val(zone_value);
zone_value = zonePicker[0].value;
if (zone_value && zonePicker.children.length > 1) {
that._zonePickerLabel.show();
} else {
that._zonePickerLabel.hide();
}
that._value = zone_value;
}
});
ui.plugin(MobileTimezoneEditor);
})(window.kendo.jQuery);
var kendo_scheduler_default = kendo;
//#endregion
Object.defineProperty(exports, '__meta__', {
enumerable: true,
get: function () {
return __meta__;
}
});
Object.defineProperty(exports, 'kendo_scheduler_default', {
enumerable: true,
get: function () {
return kendo_scheduler_default;
}
});