/**
 * DevExtreme (esm/ui/scheduler/ui.scheduler.js)
 * Version: 21.2.0 (build 21200-0321)
 * Build date: Mon Jul 19 2021
 *
 * Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
import _extends from "@babel/runtime/helpers/esm/extends";
import registerComponent from "../../core/component_registrator";
import config from "../../core/config";
import devices from "../../core/devices";
import $ from "../../core/renderer";
import {
    BindableTemplate
} from "../../core/templates/bindable_template";
import {
    EmptyTemplate
} from "../../core/templates/empty_template";
import {
    inArray
} from "../../core/utils/array";
import Callbacks from "../../core/utils/callbacks";
import {
    noop
} from "../../core/utils/common";
import {
    compileGetter,
    compileSetter
} from "../../core/utils/data";
import {
    getBoundingRect
} from "../../core/utils/position";
import dateUtils from "../../core/utils/date";
import dateSerialization from "../../core/utils/date_serialization";
import {
    Deferred,
    when,
    fromPromise
} from "../../core/utils/deferred";
import {
    extend
} from "../../core/utils/extend";
import {
    each
} from "../../core/utils/iterator";
import {
    isDefined,
    isString,
    isObject,
    isFunction,
    isEmptyObject,
    isDeferred,
    isPromise
} from "../../core/utils/type";
import {
    hasWindow
} from "../../core/utils/window";
import DataHelperMixin from "../../data_helper";
import {
    triggerResizeEvent
} from "../../events/visibility_change";
import dateLocalization from "../../localization/date";
import messageLocalization from "../../localization/message";
import {
    custom as customDialog
} from "../dialog";
import {
    isMaterial
} from "../themes";
import errors from "../widget/ui.errors";
import Widget from "../widget/ui.widget";
import {
    AppointmentPopup,
    ACTION_TO_APPOINTMENT
} from "./appointmentPopup/popup";
import {
    AppointmentForm
} from "./appointmentPopup/form";
import {
    CompactAppointmentsHelper
} from "./compactAppointmentsHelper";
import {
    DesktopTooltipStrategy
} from "./tooltip_strategies/desktopTooltipStrategy";
import {
    MobileTooltipStrategy
} from "./tooltip_strategies/mobileTooltipStrategy";
import {
    hide as hideLoading,
    show as showLoading
} from "./loading";
import AppointmentCollection from "./appointments/appointmentCollection";
import AppointmentLayoutManager from "./appointments.layout_manager";
import {
    Header
} from "./header/header";
import subscribes from "./subscribes";
import {
    getRecurrenceProcessor
} from "./recurrence";
import timeZoneUtils from "./utils.timeZone";
import SchedulerAgenda from "./workspaces/ui.scheduler.agenda";
import SchedulerTimelineDay from "./workspaces/ui.scheduler.timeline_day";
import SchedulerTimelineMonth from "./workspaces/ui.scheduler.timeline_month";
import SchedulerTimelineWeek from "./workspaces/ui.scheduler.timeline_week";
import SchedulerTimelineWorkWeek from "./workspaces/ui.scheduler.timeline_work_week";
import SchedulerWorkSpaceDay from "./workspaces/ui.scheduler.work_space_day";
import SchedulerWorkSpaceMonth from "./workspaces/ui.scheduler.work_space_month";
import SchedulerWorkSpaceWeek from "./workspaces/ui.scheduler.work_space_week";
import SchedulerWorkSpaceWorkWeek from "./workspaces/ui.scheduler.work_space_work_week";
import {
    createAppointmentAdapter
} from "./appointmentAdapter";
import {
    AppointmentTooltipInfo
} from "./dataStructures";
import {
    AppointmentSettingsGenerator
} from "./appointments/settingsGenerator";
import {
    utils
} from "./utils";
import {
    createFactoryInstances,
    disposeFactoryInstances,
    getResourceManager,
    getAppointmentDataProvider,
    getTimeZoneCalculator
} from "./instanceFactory";
import {
    getCellGroups
} from "./resources/utils";
import {
    ExpressionUtils
} from "./expressionUtils";
import {
    validateDayHours
} from "./workspaces/utils/base";
var MINUTES_IN_HOUR = 60;
var WIDGET_CLASS = "dx-scheduler";
var WIDGET_SMALL_CLASS = "".concat(WIDGET_CLASS, "-small");
var WIDGET_ADAPTIVE_CLASS = "".concat(WIDGET_CLASS, "-adaptive");
var WIDGET_READONLY_CLASS = "".concat(WIDGET_CLASS, "-readonly");
var WIDGET_SMALL_WIDTH = 400;
var FULL_DATE_FORMAT = "yyyyMMddTHHmmss";
var UTC_FULL_DATE_FORMAT = FULL_DATE_FORMAT + "Z";
var VIEWS_CONFIG = {
    day: {
        workSpace: SchedulerWorkSpaceDay,
        renderingStrategy: "vertical"
    },
    week: {
        workSpace: SchedulerWorkSpaceWeek,
        renderingStrategy: "vertical"
    },
    workWeek: {
        workSpace: SchedulerWorkSpaceWorkWeek,
        renderingStrategy: "vertical"
    },
    month: {
        workSpace: SchedulerWorkSpaceMonth,
        renderingStrategy: "horizontalMonth"
    },
    timelineDay: {
        workSpace: SchedulerTimelineDay,
        renderingStrategy: "horizontal"
    },
    timelineWeek: {
        workSpace: SchedulerTimelineWeek,
        renderingStrategy: "horizontal"
    },
    timelineWorkWeek: {
        workSpace: SchedulerTimelineWorkWeek,
        renderingStrategy: "horizontal"
    },
    timelineMonth: {
        workSpace: SchedulerTimelineMonth,
        renderingStrategy: "horizontalMonthLine"
    },
    agenda: {
        workSpace: SchedulerAgenda,
        renderingStrategy: "agenda"
    }
};
var StoreEventNames = {
    ADDING: "onAppointmentAdding",
    ADDED: "onAppointmentAdded",
    DELETING: "onAppointmentDeleting",
    DELETED: "onAppointmentDeleted",
    UPDATING: "onAppointmentUpdating",
    UPDATED: "onAppointmentUpdated"
};
var RECURRENCE_EDITING_MODE = {
    SERIES: "editSeries",
    OCCURENCE: "editOccurence",
    CANCEL: "cancel"
};
class Scheduler extends Widget {
    _getDefaultOptions() {
        var defaultOptions = extend(super._getDefaultOptions(), {
            views: ["day", "week"],
            currentView: "day",
            currentDate: dateUtils.trimTime(new Date),
            min: void 0,
            max: void 0,
            dateSerializationFormat: void 0,
            firstDayOfWeek: void 0,
            groups: [],
            resources: [],
            dataSource: null,
            customizeDateNavigatorText: void 0,
            appointmentTemplate: "item",
            appointmentCollectorTemplate: "appointmentCollector",
            dataCellTemplate: null,
            timeCellTemplate: null,
            resourceCellTemplate: null,
            dateCellTemplate: null,
            startDayHour: 0,
            endDayHour: 24,
            editing: {
                allowAdding: true,
                allowDeleting: true,
                allowDragging: true,
                allowResizing: true,
                allowUpdating: true,
                allowTimeZoneEditing: false
            },
            showAllDayPanel: true,
            showCurrentTimeIndicator: true,
            shadeUntilCurrentTime: false,
            indicatorUpdateInterval: 3e5,
            indicatorTime: void 0,
            recurrenceEditMode: "dialog",
            cellDuration: 30,
            maxAppointmentsPerCell: "auto",
            selectedCellData: [],
            groupByDate: false,
            onAppointmentRendered: null,
            onAppointmentClick: null,
            onAppointmentDblClick: null,
            onAppointmentContextMenu: null,
            onCellClick: null,
            onCellContextMenu: null,
            onAppointmentAdding: null,
            onAppointmentAdded: null,
            onAppointmentUpdating: null,
            onAppointmentUpdated: null,
            onAppointmentDeleting: null,
            onAppointmentDeleted: null,
            onAppointmentFormOpening: null,
            appointmentTooltipTemplate: "appointmentTooltip",
            appointmentPopupTemplate: "appointmentPopup",
            crossScrollingEnabled: false,
            useDropDownViewSwitcher: false,
            startDateExpr: "startDate",
            endDateExpr: "endDate",
            textExpr: "text",
            descriptionExpr: "description",
            allDayExpr: "allDay",
            recurrenceRuleExpr: "recurrenceRule",
            recurrenceExceptionExpr: "recurrenceException",
            disabledExpr: "disabled",
            remoteFiltering: false,
            timeZone: "",
            startDateTimeZoneExpr: "startDateTimeZone",
            endDateTimeZoneExpr: "endDateTimeZone",
            noDataText: messageLocalization.format("dxCollectionWidget-noDataText"),
            adaptivityEnabled: false,
            allowMultipleCellSelection: true,
            scrolling: {
                mode: "standard"
            },
            renovateRender: true,
            _draggingMode: "outlook",
            _appointmentTooltipOffset: {
                x: 0,
                y: 0
            },
            _appointmentTooltipButtonsPosition: "bottom",
            _appointmentTooltipOpenButtonText: messageLocalization.format("dxScheduler-openAppointment"),
            _dropDownButtonIcon: "overflow",
            _appointmentCountPerCell: 2,
            _collectorOffset: 0,
            _appointmentOffset: 26
        });
        return extend(true, defaultOptions, {
            integrationOptions: {
                useDeferUpdateForTemplates: false
            }
        })
    }
    _defaultOptionsRules() {
        return super._defaultOptionsRules().concat([{
            device: function() {
                return "desktop" === devices.real().deviceType && !devices.isSimulator()
            },
            options: {
                focusStateEnabled: true
            }
        }, {
            device: function() {
                return !devices.current().generic
            },
            options: {
                useDropDownViewSwitcher: true,
                editing: {
                    allowDragging: false,
                    allowResizing: false
                }
            }
        }, {
            device: function() {
                return isMaterial()
            },
            options: {
                useDropDownViewSwitcher: true,
                dateCellTemplate: function(data, index, element) {
                    var text = data.text;
                    text.split(" ").forEach((function(text, index) {
                        var span = $("<span>").text(text).addClass("dx-scheduler-header-panel-cell-date");
                        $(element).append(span);
                        if (!index) {
                            $(element).append(" ")
                        }
                    }))
                },
                _appointmentTooltipOffset: {
                    x: 0,
                    y: 11
                },
                _appointmentTooltipButtonsPosition: "top",
                _appointmentTooltipOpenButtonText: null,
                _dropDownButtonIcon: "chevrondown",
                _appointmentCountPerCell: 1,
                _collectorOffset: 20,
                _appointmentOffset: 30
            }
        }])
    }
    _getAppointmentSettingsGenerator(rawAppointment) {
        var workspace = this.getWorkSpace();
        return new AppointmentSettingsGenerator({
            key: this.key,
            rawAppointment: rawAppointment,
            timeZoneCalculator: getTimeZoneCalculator(this.key),
            resourceManager: getResourceManager(this.key),
            appointmentTakesAllDay: this.appointmentTakesAllDay(rawAppointment),
            timeZone: this.option("timeZone"),
            firstDayOfWeek: this.getFirstDayOfWeek(),
            viewStartDayHour: this._getCurrentViewOption("startDayHour"),
            viewEndDayHour: this._getCurrentViewOption("endDayHour"),
            isVirtualScrolling: this.isVirtualScrolling(),
            viewType: workspace.type,
            endViewDate: workspace.getEndViewDate(),
            positionHelper: workspace.positionHelper,
            isGroupedByDate: workspace.isGroupedByDate(),
            cellDuration: workspace.getCellDuration(),
            viewDataProvider: workspace.viewDataProvider,
            supportAllDayRow: workspace.supportAllDayRow(),
            dateRange: workspace.getDateRange(),
            intervalDuration: workspace.getIntervalDuration(),
            isVerticalOrientation: workspace.isVerticalOrientation(),
            allDayIntervalDuration: workspace.getIntervalDuration(true),
            isSkippedDataCallback: workspace._isSkippedData.bind(workspace),
            getPositionShiftCallback: workspace.getPositionShift.bind(workspace),
            DOMMetaData: workspace.getDOMElementsMetaData()
        })
    }
    _postponeDataSourceLoading(promise) {
        this.postponedOperations.add("_reloadDataSource", this._reloadDataSource.bind(this), promise)
    }
    _postponeResourceLoading() {
        var whenLoaded = this.postponedOperations.add("loadResources", () => {
            var groups = this._getCurrentViewOption("groups");
            return getResourceManager(this.key).loadResources(groups)
        });
        var resolveCallbacks = new Deferred;
        whenLoaded.done(resources => {
            resolveCallbacks.resolve(resources)
        });
        this._postponeDataSourceLoading(whenLoaded);
        return resolveCallbacks.promise()
    }
    reinitRenderingStrategy() {
        var strategy = this._getAppointmentsRenderingStrategy();
        this.getLayoutManager().initRenderingStrategy(strategy)
    }
    _optionChanged(args) {
        var value = args.value;
        var name = args.name;
        switch (args.name) {
            case "customizeDateNavigatorText":
                this._updateOption("header", name, value);
                break;
            case "firstDayOfWeek":
                this._updateOption("workSpace", name, value);
                this._updateOption("header", name, value);
                break;
            case "currentDate":
                value = this._dateOption(name);
                value = dateUtils.trimTime(new Date(value));
                this.option("selectedCellData", []);
                this._workSpace.option(name, new Date(value));
                this._header.option(name, new Date(value));
                this._header.option("displayedDate", this._workSpace._getViewStartByOptions());
                this._appointments.option("items", []);
                this._filterAppointmentsByDate();
                this._postponeDataSourceLoading();
                break;
            case "dataSource":
                this._initDataSource();
                getAppointmentDataProvider(this.key).setDataSource(this._dataSource);
                this._postponeResourceLoading().done(resources => {
                    this._filterAppointmentsByDate();
                    this._updateOption("workSpace", "showAllDayPanel", this.option("showAllDayPanel"))
                });
                break;
            case "min":
            case "max":
                value = this._dateOption(name);
                this._updateOption("header", name, new Date(value));
                this._updateOption("workSpace", name, new Date(value));
                break;
            case "views":
                this._processCurrentView();
                if (this._getCurrentViewOptions()) {
                    this.repaint()
                } else {
                    this._header.option(name, value)
                }
                break;
            case "useDropDownViewSwitcher":
                this._header.option(name, value);
                break;
            case "currentView":
                this._processCurrentView();
                this._validateDayHours();
                this.reinitRenderingStrategy();
                this._validateCellDuration();
                this._appointments.option({
                    items: [],
                    allowDrag: this._allowDragging(),
                    allowResize: this._allowResizing(),
                    itemTemplate: this._getAppointmentTemplate("appointmentTemplate")
                });
                this._postponeResourceLoading().done(resources => {
                    this._refreshWorkSpace(resources);
                    this._updateHeader();
                    this._filterAppointmentsByDate();
                    this._appointments.option("allowAllDayResize", "day" !== value)
                });
                break;
            case "appointmentTemplate":
                this._appointments.option("itemTemplate", value);
                break;
            case "dateCellTemplate":
            case "resourceCellTemplate":
            case "dataCellTemplate":
            case "timeCellTemplate":
                this._updateOption("workSpace", name, value);
                this.repaint();
                break;
            case "groups":
                this._postponeResourceLoading().done(resources => {
                    this._refreshWorkSpace(resources);
                    this._filterAppointmentsByDate()
                });
                break;
            case "resources":
                this.updateFactoryInstances();
                this._postponeResourceLoading().done(resources => {
                    this._appointments.option("items", []);
                    this._refreshWorkSpace(resources);
                    this._filterAppointmentsByDate()
                });
                break;
            case "startDayHour":
            case "endDayHour":
                this._validateDayHours();
                this.updateFactoryInstances();
                this._appointments.option("items", []);
                this._updateOption("workSpace", name, value);
                this._appointments.repaint();
                this._filterAppointmentsByDate();
                this._postponeDataSourceLoading();
                break;
            case StoreEventNames.ADDING:
            case StoreEventNames.ADDED:
            case StoreEventNames.UPDATING:
            case StoreEventNames.UPDATED:
            case StoreEventNames.DELETING:
            case StoreEventNames.DELETED:
            case "onAppointmentFormOpening":
                this._actions[name] = this._createActionByOption(name);
                break;
            case "onAppointmentRendered":
                this._appointments.option("onItemRendered", this._getAppointmentRenderedAction());
                break;
            case "onAppointmentClick":
                this._appointments.option("onItemClick", this._createActionByOption(name));
                break;
            case "onAppointmentDblClick":
                this._appointments.option(name, this._createActionByOption(name));
                break;
            case "onAppointmentContextMenu":
                this._appointments.option("onItemContextMenu", this._createActionByOption(name));
                break;
            case "noDataText":
            case "allowMultipleCellSelection":
            case "selectedCellData":
            case "accessKey":
            case "onCellClick":
            case "onCellContextMenu":
                this._workSpace.option(name, value);
                break;
            case "crossScrollingEnabled":
                this._postponeResourceLoading().done(resources => {
                    this._appointments.option("items", []);
                    this._refreshWorkSpace(resources);
                    if (this._readyToRenderAppointments) {
                        this._appointments.option("items", this._getAppointmentsToRepaint())
                    }
                });
                break;
            case "cellDuration":
                this._validateCellDuration();
                this._appointments.option("items", []);
                if (this._readyToRenderAppointments) {
                    this._updateOption("workSpace", "hoursInterval", value / 60);
                    this._appointments.option("items", this._getAppointmentsToRepaint())
                }
                break;
            case "tabIndex":
            case "focusStateEnabled":
                this._updateOption("header", name, value);
                this._updateOption("workSpace", name, value);
                this._appointments.option(name, value);
                super._optionChanged(args);
                break;
            case "width":
                this._updateOption("header", name, value);
                if (this.option("crossScrollingEnabled")) {
                    this._updateOption("workSpace", "width", value)
                }
                this._updateOption("workSpace", "schedulerWidth", value);
                super._optionChanged(args);
                this._dimensionChanged();
                break;
            case "height":
                super._optionChanged(args);
                this._dimensionChanged();
                this._updateOption("workSpace", "schedulerHeight", value);
                break;
            case "editing":
                this._initEditing();
                var editing = this._editing;
                this._bringEditingModeToAppointments(editing);
                this.hideAppointmentTooltip();
                this._cleanPopup();
                break;
            case "showAllDayPanel":
                this.updateFactoryInstances();
                this._postponeResourceLoading().done(resources => {
                    this._filterAppointmentsByDate();
                    this._updateOption("workSpace", "allDayExpanded", value);
                    this._updateOption("workSpace", name, value)
                });
                break;
            case "showCurrentTimeIndicator":
            case "indicatorTime":
            case "indicatorUpdateInterval":
            case "shadeUntilCurrentTime":
            case "groupByDate":
                this._updateOption("workSpace", name, value);
                this.repaint();
                break;
            case "appointmentDragging":
            case "appointmentTooltipTemplate":
            case "appointmentPopupTemplate":
            case "recurrenceEditMode":
            case "remoteFiltering":
            case "timeZone":
                this.updateFactoryInstances();
                this.repaint();
                break;
            case "appointmentCollectorTemplate":
            case "_appointmentTooltipOffset":
            case "_appointmentTooltipButtonsPosition":
            case "_appointmentTooltipOpenButtonText":
            case "_dropDownButtonIcon":
            case "_appointmentCountPerCell":
            case "_collectorOffset":
            case "_appointmentOffset":
                this.repaint();
                break;
            case "dateSerializationFormat":
            case "maxAppointmentsPerCell":
                break;
            case "startDateExpr":
            case "endDateExpr":
            case "startDateTimeZoneExpr":
            case "endDateTimeZoneExpr":
            case "textExpr":
            case "descriptionExpr":
            case "allDayExpr":
            case "recurrenceRuleExpr":
            case "recurrenceExceptionExpr":
            case "disabledExpr":
                this._updateExpression(name, value);
                getAppointmentDataProvider(this.key).updateDataAccessors(utils.dataAccessors.combine(this.key, this._dataAccessors));
                this._initAppointmentTemplate();
                this.repaint();
                break;
            case "adaptivityEnabled":
                this._toggleAdaptiveClass();
                this.repaint();
                break;
            case "scrolling":
                this.option("crossScrollingEnabled", this._isHorizontalVirtualScrolling() || this.option("crossScrollingEnabled"));
                this._updateOption("workSpace", args.fullName, value);
                break;
            case "renovateRender":
                this._updateOption("workSpace", name, value);
                break;
            case "_draggingMode":
                this._workSpace.option("draggingMode", value);
                break;
            default:
                super._optionChanged(args)
        }
    }
    _updateHeader() {
        var viewCountConfig = this._getViewCountConfig();
        this._header.option("intervalCount", viewCountConfig.intervalCount);
        this._header.option("displayedDate", this._workSpace._getViewStartByOptions());
        this._header.option("min", this._dateOption("min"));
        this._header.option("max", this._dateOption("max"));
        this._header.option("currentDate", this._dateOption("currentDate"));
        this._header.option("firstDayOfWeek", this._getCurrentViewOption("firstDayOfWeek"));
        this._header.option("currentView", this._currentView)
    }
    _dateOption(optionName) {
        var optionValue = this._getCurrentViewOption(optionName);
        return dateSerialization.deserializeDate(optionValue)
    }
    _getSerializationFormat(optionName) {
        var value = this._getCurrentViewOption(optionName);
        if ("number" === typeof value) {
            return "number"
        }
        if (!isString(value)) {
            return
        }
        return dateSerialization.getDateSerializationFormat(value)
    }
    _bringEditingModeToAppointments(editing) {
        var editingConfig = {
            allowDelete: editing.allowUpdating && editing.allowDeleting
        };
        if (!this._isAgenda()) {
            editingConfig.allowDrag = editing.allowDragging;
            editingConfig.allowResize = editing.allowResizing;
            editingConfig.allowAllDayResize = editing.allowResizing && this._supportAllDayResizing()
        }
        this._appointments.option(editingConfig);
        this.repaint()
    }
    _isAgenda() {
        return "agenda" === this._getAppointmentsRenderingStrategy()
    }
    _allowDragging() {
        return this._editing.allowDragging && !this._isAgenda()
    }
    _allowResizing() {
        return this._editing.allowResizing && !this._isAgenda()
    }
    _allowAllDayResizing() {
        return this._editing.allowResizing && this._supportAllDayResizing()
    }
    _supportAllDayResizing() {
        return "day" !== this._getCurrentViewType() || this._currentView.intervalCount > 1
    }
    _isAllDayExpanded(items) {
        return this.option("showAllDayPanel") && getAppointmentDataProvider(this.key).hasAllDayAppointments(items)
    }
    _getTimezoneOffsetByOption(date) {
        return timeZoneUtils.calculateTimezoneByValue(this.option("timeZone"), date)
    }
    _filterAppointmentsByDate() {
        var dateRange = this._workSpace.getDateRange();
        var timeZoneCalculator = getTimeZoneCalculator(this.key);
        var startDate = timeZoneCalculator.createDate(dateRange[0], {
            path: "fromGrid"
        });
        var endDate = timeZoneCalculator.createDate(dateRange[1], {
            path: "fromGrid"
        });
        getAppointmentDataProvider(this.key).filterByDate(startDate, endDate, this.option("remoteFiltering"), this.option("dateSerializationFormat"))
    }
    _reloadDataSource() {
        var result = new Deferred;
        if (this._dataSource) {
            this._dataSource.load().done(function() {
                hideLoading();
                this._fireContentReadyAction(result)
            }.bind(this)).fail((function() {
                hideLoading();
                result.reject()
            }));
            this._dataSource.isLoading() && showLoading({
                container: this.$element(),
                position: {
                    of: this.$element()
                }
            })
        } else {
            this._fireContentReadyAction(result)
        }
        return result.promise()
    }
    _fireContentReadyAction(result) {
        var contentReadyBase = super._fireContentReadyAction.bind(this);
        var fireContentReady = () => {
            contentReadyBase();
            null === result || void 0 === result ? void 0 : result.resolve()
        };
        if (this._workSpaceRecalculation) {
            var _this$_workSpaceRecal;
            null === (_this$_workSpaceRecal = this._workSpaceRecalculation) || void 0 === _this$_workSpaceRecal ? void 0 : _this$_workSpaceRecal.done(() => {
                fireContentReady()
            })
        } else {
            fireContentReady()
        }
    }
    _dimensionChanged() {
        var {
            filteredItems: filteredItems
        } = getAppointmentDataProvider(this.key);
        this._toggleSmallClass();
        if (!this._isAgenda() && filteredItems && this._isVisible()) {
            this._workSpace.option("allDayExpanded", this._isAllDayExpanded(filteredItems));
            this._workSpace._dimensionChanged();
            var appointments = this.getLayoutManager().createAppointmentsMap(filteredItems);
            this._appointments.option("items", appointments)
        }
        this.hideAppointmentTooltip();
        this._appointmentPopup.triggerResize();
        this._appointmentPopup.updatePopupFullScreenMode()
    }
    _clean() {
        this._cleanPopup();
        super._clean()
    }
    _toggleSmallClass() {
        var width = getBoundingRect(this.$element().get(0)).width;
        this.$element().toggleClass(WIDGET_SMALL_CLASS, width < WIDGET_SMALL_WIDTH)
    }
    _toggleAdaptiveClass() {
        this.$element().toggleClass(WIDGET_ADAPTIVE_CLASS, this.option("adaptivityEnabled"))
    }
    _visibilityChanged(visible) {
        visible && this._dimensionChanged()
    }
    _dataSourceOptions() {
        return {
            paginate: false
        }
    }
    _init() {
        this._initExpressions({
            startDate: this.option("startDateExpr"),
            endDate: this.option("endDateExpr"),
            startDateTimeZone: this.option("startDateTimeZoneExpr"),
            endDateTimeZone: this.option("endDateTimeZoneExpr"),
            allDay: this.option("allDayExpr"),
            text: this.option("textExpr"),
            description: this.option("descriptionExpr"),
            recurrenceRule: this.option("recurrenceRuleExpr"),
            recurrenceException: this.option("recurrenceExceptionExpr"),
            disabled: this.option("disabledExpr")
        });
        super._init();
        this._initDataSource();
        this.$element().addClass(WIDGET_CLASS);
        this._initEditing();
        this.updateFactoryInstances();
        this._initActions();
        this._compactAppointmentsHelper = new CompactAppointmentsHelper(this);
        this._asyncTemplatesTimers = [];
        this._dataSourceLoadedCallback = Callbacks();
        this._subscribes = subscribes
    }
    updateFactoryInstances() {
        var model = this._options._optionManager._options;
        this.key = createFactoryInstances({
            key: this.key,
            scheduler: this,
            model: model,
            getIsVirtualScrolling: this.isVirtualScrolling.bind(this),
            resources: this.option("resources"),
            dataSource: this._dataSource,
            startDayHour: this._getCurrentViewOption("startDayHour"),
            endDayHour: this._getCurrentViewOption("endDayHour"),
            appointmentDuration: this._getCurrentViewOption("cellDuration"),
            firstDayOfWeek: this.getFirstDayOfWeek(),
            showAllDayPanel: this.option("showAllDayPanel"),
            timeZone: this.option("timeZone"),
            getDataAccessors: function(key) {
                return utils.dataAccessors.combine(key, this._dataAccessors)
            }.bind(this)
        })
    }
    _initTemplates() {
        this._initAppointmentTemplate();
        this._templateManager.addDefaultTemplates({
            appointmentTooltip: new EmptyTemplate,
            dropDownAppointment: new EmptyTemplate
        });
        super._initTemplates()
    }
    _initAppointmentTemplate() {
        var {
            expr: expr
        } = this._dataAccessors;
        var createGetter = property => compileGetter("appointmentData.".concat(property));
        var getDate = getter => data => {
            var value = getter(data);
            if (value instanceof Date) {
                return value.valueOf()
            }
            return value
        };
        this._templateManager.addDefaultTemplates({
            item: new BindableTemplate(($container, data, model) => this.getAppointmentsInstance()._renderAppointmentTemplate($container, data, model), ["html", "text", "startDate", "endDate", "allDay", "description", "recurrenceRule", "recurrenceException", "startDateTimeZone", "endDateTimeZone"], this.option("integrationOptions.watchMethod"), {
                text: createGetter(expr.textExpr),
                startDate: getDate(createGetter(expr.startDateExpr)),
                endDate: getDate(createGetter(expr.endDateExpr)),
                startDateTimeZone: createGetter(expr.startDateTimeZoneExpr),
                endDateTimeZone: createGetter(expr.endDateTimeZoneExpr),
                allDay: createGetter(expr.allDayExpr),
                recurrenceRule: createGetter(expr.recurrenceRuleExpr)
            })
        })
    }
    _renderContent() {
        this._renderContentImpl()
    }
    _dataSourceChangedHandler(result) {
        if (this._readyToRenderAppointments) {
            this._workSpaceRecalculation.done(function() {
                this._renderAppointments();
                var {
                    filteredItems: filteredItems
                } = getAppointmentDataProvider(this.key);
                this.getWorkSpace().onDataSourceChanged(filteredItems)
            }.bind(this))
        }
    }
    isVirtualScrolling() {
        var _currentViewOptions$s;
        var workspace = this.getWorkSpace();
        if (workspace) {
            return workspace.isVirtualScrolling()
        }
        var currentViewOptions = this._getCurrentViewOptions();
        var scrolling = this.option("scrolling");
        return "virtual" === (null === scrolling || void 0 === scrolling ? void 0 : scrolling.mode) || "virtual" === (null === currentViewOptions || void 0 === currentViewOptions ? void 0 : null === (_currentViewOptions$s = currentViewOptions.scrolling) || void 0 === _currentViewOptions$s ? void 0 : _currentViewOptions$s.mode)
    }
    _filterAppointments() {
        getAppointmentDataProvider(this.key).filter()
    }
    _renderAppointments() {
        var workspace = this.getWorkSpace();
        this._filterAppointments();
        var {
            filteredItems: filteredItems
        } = getAppointmentDataProvider(this.key);
        workspace.preRenderAppointments({
            allDayExpanded: this._isAllDayExpanded(filteredItems),
            appointments: filteredItems
        });
        if (filteredItems.length && this._isVisible()) {
            this._appointments.option("items", this._getAppointmentsToRepaint());
            getAppointmentDataProvider(this.key).cleanState()
        } else {
            this._appointments.option("items", [])
        }
    }
    _getAppointmentsToRepaint() {
        var {
            filteredItems: filteredItems
        } = getAppointmentDataProvider(this.key);
        var layoutManager = this.getLayoutManager();
        var appointments = layoutManager.createAppointmentsMap(filteredItems);
        return layoutManager.getRepaintedAppointments(appointments, this.getAppointmentsInstance().option("items"))
    }
    _initExpressions(fields) {
        if (!this._dataAccessors) {
            this._dataAccessors = {
                getter: {},
                setter: {},
                expr: {}
            }
        }
        each(fields, function(name, expr) {
            if (expr) {
                var getter = compileGetter(expr);
                var setter = compileSetter(expr);
                var dateGetter;
                var dateSetter;
                if (field = name, "startDate" === field || "endDate" === field) {
                    var that = this;
                    dateGetter = function() {
                        var value = getter.apply(this, arguments);
                        if (config().forceIsoDateParsing) {
                            if (!that.option("dateSerializationFormat")) {
                                var format = dateSerialization.getDateSerializationFormat(value);
                                if (format) {
                                    that.option("dateSerializationFormat", format)
                                }
                            }
                            value = dateSerialization.deserializeDate(value)
                        }
                        return value
                    };
                    dateSetter = function(object, value) {
                        if (config().forceIsoDateParsing || that.option("dateSerializationFormat")) {
                            value = dateSerialization.serializeDate(value, that.option("dateSerializationFormat"))
                        }
                        setter.call(this, object, value)
                    }
                }
                this._dataAccessors.getter[name] = dateGetter || getter;
                this._dataAccessors.setter[name] = dateSetter || setter;
                this._dataAccessors.expr[name + "Expr"] = expr
            } else {
                delete this._dataAccessors.getter[name];
                delete this._dataAccessors.setter[name];
                delete this._dataAccessors.expr[name + "Expr"]
            }
            var field
        }.bind(this))
    }
    _updateExpression(name, value) {
        var exprObj = {};
        exprObj[name.replace("Expr", "")] = value;
        this._initExpressions(exprObj)
    }
    _initEditing() {
        var editing = this.option("editing");
        this._editing = {
            allowAdding: !!editing,
            allowUpdating: !!editing,
            allowDeleting: !!editing,
            allowResizing: !!editing,
            allowDragging: !!editing
        };
        if (isObject(editing)) {
            this._editing = extend(this._editing, editing)
        }
        this._editing.allowDragging = this._editing.allowDragging && this._editing.allowUpdating;
        this._editing.allowResizing = this._editing.allowResizing && this._editing.allowUpdating;
        this.$element().toggleClass(WIDGET_READONLY_CLASS, this._isReadOnly())
    }
    _isReadOnly() {
        var result = true;
        var editing = this._editing;
        for (var prop in editing) {
            if (Object.prototype.hasOwnProperty.call(editing, prop)) {
                result = result && !editing[prop]
            }
        }
        return result
    }
    _dispose() {
        var _this$_recurrenceDial;
        this._appointmentTooltip && this._appointmentTooltip.dispose();
        null === (_this$_recurrenceDial = this._recurrenceDialog) || void 0 === _this$_recurrenceDial ? void 0 : _this$_recurrenceDial.hide(RECURRENCE_EDITING_MODE.CANCEL);
        this.hideAppointmentPopup();
        this.hideAppointmentTooltip();
        this._asyncTemplatesTimers.forEach(clearTimeout);
        this._asyncTemplatesTimers = [];
        super._dispose();
        disposeFactoryInstances(this.key)
    }
    _initActions() {
        this._actions = {
            onAppointmentAdding: this._createActionByOption(StoreEventNames.ADDING),
            onAppointmentAdded: this._createActionByOption(StoreEventNames.ADDED),
            onAppointmentUpdating: this._createActionByOption(StoreEventNames.UPDATING),
            onAppointmentUpdated: this._createActionByOption(StoreEventNames.UPDATED),
            onAppointmentDeleting: this._createActionByOption(StoreEventNames.DELETING),
            onAppointmentDeleted: this._createActionByOption(StoreEventNames.DELETED),
            onAppointmentFormOpening: this._createActionByOption("onAppointmentFormOpening")
        }
    }
    _getAppointmentRenderedAction() {
        return this._createActionByOption("onAppointmentRendered", {
            excludeValidators: ["disabled", "readOnly"]
        })
    }
    _renderFocusTarget() {
        return noop()
    }
    _initMarkup() {
        super._initMarkup();
        this._validateDayHours();
        this._validateCellDuration();
        this._processCurrentView();
        this._renderHeader();
        this._layoutManager = new AppointmentLayoutManager(this, this._getAppointmentsRenderingStrategy());
        this._appointments = this._createComponent("<div>", AppointmentCollection, this._appointmentsConfig());
        this._appointments.option("itemTemplate", this._getAppointmentTemplate("appointmentTemplate"));
        this._appointmentTooltip = new(this.option("adaptivityEnabled") ? MobileTooltipStrategy : DesktopTooltipStrategy)(this._getAppointmentTooltipOptions());
        this._appointmentForm = this.createAppointmentForm();
        this._appointmentPopup = this.createAppointmentPopup(this._appointmentForm);
        if (this._isLoaded() || this._isDataSourceLoading()) {
            this._initMarkupCore(getResourceManager(this.key).loadedResources);
            this._dataSourceChangedHandler(this._dataSource.items());
            this._fireContentReadyAction()
        } else {
            var groups = this._getCurrentViewOption("groups");
            getResourceManager(this.key).loadResources(groups).done(resources => {
                this._initMarkupCore(resources);
                this._reloadDataSource()
            })
        }
    }
    createAppointmentForm() {
        var scheduler = {
            getResourceManager: () => this.fire("getResourceManager"),
            getDataAccessors: () => this._dataAccessors,
            createComponent: (element, component, options) => this._createComponent(element, component, options),
            getFirstDayOfWeek: () => this.option("firstDayOfWeek"),
            getStartDayHour: () => this.option("startDayHour"),
            getCalculatedEndDate: startDateWithStartHour => this._workSpace.calculateEndDate(startDateWithStartHour)
        };
        return new AppointmentForm(scheduler)
    }
    createAppointmentPopup(form) {
        var scheduler = {
            getKey: () => this.key,
            getElement: () => this.$element(),
            createComponent: (element, component, options) => this._createComponent(element, component, options),
            focus: () => this.focus(),
            getResourceManager: () => this.fire("getResourceManager"),
            getEditingConfig: () => this._editing,
            getDataAccessors: () => this._dataAccessors,
            getAppointmentFormOpening: () => this._actions.onAppointmentFormOpening,
            processActionResult: (arg, canceled) => this._processActionResult(arg, canceled),
            addAppointment: appointment => this.addAppointment(appointment),
            updateAppointment: (sourceAppointment, updatedAppointment) => this.updateAppointment(sourceAppointment, updatedAppointment),
            updateScrollPosition: (startDate, resourceItem, inAllDayRow) => {
                this._workSpace.updateScrollPosition(startDate, resourceItem, inAllDayRow)
            }
        };
        return new AppointmentPopup(scheduler, form)
    }
    _getAppointmentTooltipOptions() {
        return {
            createComponent: this._createComponent.bind(this),
            container: this.$element(),
            getScrollableContainer: this.getWorkSpaceScrollableContainer.bind(this),
            addDefaultTemplates: this._templateManager.addDefaultTemplates.bind(this._templateManager),
            getAppointmentTemplate: this._getAppointmentTemplate.bind(this),
            showAppointmentPopup: this.showAppointmentPopup.bind(this),
            checkAndDeleteAppointment: this.checkAndDeleteAppointment.bind(this),
            isAppointmentInAllDayPanel: this.isAppointmentInAllDayPanel.bind(this),
            createFormattedDateText: (appointment, targetedAppointment, format) => this.fire("getTextAndFormatDate", appointment, targetedAppointment, format),
            getAppointmentDisabled: appointment => createAppointmentAdapter(this.key, appointment).disabled
        }
    }
    checkAndDeleteAppointment(appointment, targetedAppointment) {
        var targetedAdapter = createAppointmentAdapter(this.key, targetedAppointment);
        this._checkRecurringAppointment(appointment, targetedAppointment, targetedAdapter.startDate, () => {
            this.deleteAppointment(appointment)
        }, true)
    }
    _getExtraAppointmentTooltipOptions() {
        return {
            rtlEnabled: this.option("rtlEnabled"),
            focusStateEnabled: this.option("focusStateEnabled"),
            editing: this.option("editing"),
            offset: this.option("_appointmentTooltipOffset")
        }
    }
    isAppointmentInAllDayPanel(appointmentData) {
        var workSpace = this._workSpace;
        var itTakesAllDay = this.appointmentTakesAllDay(appointmentData);
        return itTakesAllDay && workSpace.supportAllDayRow() && workSpace.option("showAllDayPanel")
    }
    _initMarkupCore(resources) {
        this._readyToRenderAppointments = hasWindow();
        this._workSpace && this._cleanWorkspace();
        this._renderWorkSpace(resources);
        this._appointments.option({
            fixedContainer: this._workSpace.getFixedContainer(),
            allDayContainer: this._workSpace.getAllDayContainer()
        });
        this._waitAsyncTemplate(() => {
            var _this$_workSpaceRecal2;
            return null === (_this$_workSpaceRecal2 = this._workSpaceRecalculation) || void 0 === _this$_workSpaceRecal2 ? void 0 : _this$_workSpaceRecal2.resolve()
        });
        this._filterAppointmentsByDate()
    }
    _isLoaded() {
        return getResourceManager(this.key).isLoaded() && this._isDataSourceLoaded()
    }
    _isDataSourceLoaded() {
        return this._dataSource && this._dataSource.isLoaded()
    }
    _render() {
        this._toggleSmallClass();
        this._toggleAdaptiveClass();
        super._render()
    }
    _renderHeader() {
        var $header = $("<div>").appendTo(this.$element());
        this._header = this._createComponent($header, Header, this._headerConfig())
    }
    _headerConfig() {
        var currentViewOptions = this._getCurrentViewOptions();
        var countConfig = this._getViewCountConfig();
        var result = extend({
            isAdaptive: this.option("adaptivityEnabled"),
            firstDayOfWeek: this.option("firstDayOfWeek"),
            currentView: this._currentView,
            tabIndex: this.option("tabIndex"),
            focusStateEnabled: this.option("focusStateEnabled"),
            width: this.option("width"),
            rtlEnabled: this.option("rtlEnabled"),
            useDropDownViewSwitcher: this.option("useDropDownViewSwitcher"),
            _dropDownButtonIcon: this.option("_dropDownButtonIcon"),
            customizeDateNavigatorText: this.option("customizeDateNavigatorText")
        }, currentViewOptions);
        result.observer = this;
        result.intervalCount = countConfig.intervalCount;
        result.views = this.option("views");
        result.min = new Date(this._dateOption("min"));
        result.max = new Date(this._dateOption("max"));
        result.currentDate = dateUtils.trimTime(new Date(this._dateOption("currentDate")));
        result.todayDate = () => {
            var result = getTimeZoneCalculator(this.key).createDate(new Date, {
                path: "toGrid"
            });
            return result
        };
        return result
    }
    _appointmentsConfig() {
        var config = {
            key: this.key,
            observer: this,
            onItemRendered: this._getAppointmentRenderedAction(),
            onItemClick: this._createActionByOption("onAppointmentClick"),
            onItemContextMenu: this._createActionByOption("onAppointmentContextMenu"),
            onAppointmentDblClick: this._createActionByOption("onAppointmentDblClick"),
            tabIndex: this.option("tabIndex"),
            focusStateEnabled: this.option("focusStateEnabled"),
            allowDrag: this._allowDragging(),
            allowDelete: this._editing.allowUpdating && this._editing.allowDeleting,
            allowResize: this._allowResizing(),
            allowAllDayResize: this._allowAllDayResizing(),
            rtlEnabled: this.option("rtlEnabled"),
            currentView: this.option("currentView"),
            groups: this._getCurrentViewOption("groups"),
            onContentReady: () => {
                var _this$_workSpace;
                var filteredItems = getAppointmentDataProvider(this.key).filteredItems;
                null === (_this$_workSpace = this._workSpace) || void 0 === _this$_workSpace ? void 0 : _this$_workSpace.option("allDayExpanded", this._isAllDayExpanded(filteredItems))
            }
        };
        return config
    }
    getCollectorOffset() {
        if (this._workSpace.needApplyCollectorOffset() && !this.option("adaptivityEnabled")) {
            return this.option("_collectorOffset")
        } else {
            return 0
        }
    }
    getAppointmentDurationInMinutes() {
        return this._getCurrentViewOption("cellDuration")
    }
    _processCurrentView() {
        var views = this.option("views");
        var currentView = this.option("currentView");
        var that = this;
        this._currentView = null;
        each(views, (function(_, view) {
            var isViewIsObject = isObject(view);
            var viewName = isViewIsObject ? view.name : view;
            var viewType = view.type;
            if (currentView === viewName || currentView === viewType) {
                that._currentView = view;
                return false
            }
        }));
        if (!this._currentView) {
            var isCurrentViewValid = !!VIEWS_CONFIG[currentView];
            if (isCurrentViewValid) {
                this._currentView = currentView
            } else {
                this._currentView = views[0]
            }
        }
    }
    _validateCellDuration() {
        var endDayHour = this._getCurrentViewOption("endDayHour");
        var startDayHour = this._getCurrentViewOption("startDayHour");
        var cellDuration = this._getCurrentViewOption("cellDuration");
        if ((endDayHour - startDayHour) * MINUTES_IN_HOUR % cellDuration !== 0) {
            errors.log("W1015")
        }
    }
    _getCurrentViewType() {
        return this._currentView.type || this._currentView
    }
    _getAppointmentsRenderingStrategy() {
        return VIEWS_CONFIG[this._getCurrentViewType()].renderingStrategy
    }
    _renderWorkSpace(groups) {
        this._readyToRenderAppointments && this._toggleSmallClass();
        var $workSpace = $("<div>").appendTo(this.$element());
        var countConfig = this._getViewCountConfig();
        var workSpaceComponent = VIEWS_CONFIG[this._getCurrentViewType()].workSpace;
        var workSpaceConfig = this._workSpaceConfig(groups, countConfig);
        this._workSpace = this._createComponent($workSpace, workSpaceComponent, workSpaceConfig);
        this._allowDragging() && this._workSpace.initDragBehavior(this, this._all);
        this._workSpace._attachTablesEvents();
        this._workSpace.getWorkArea().append(this._appointments.$element());
        this._recalculateWorkspace();
        countConfig.startDate && this._header && this._header.option("currentDate", this._workSpace._getHeaderDate());
        this._appointments.option("_collectorOffset", this.getCollectorOffset())
    }
    _getViewCountConfig() {
        var currentView = this.option("currentView");
        var view = this._getViewByName(currentView);
        var viewCount = view && view.intervalCount || 1;
        var startDate = view && view.startDate || null;
        return {
            intervalCount: viewCount,
            startDate: startDate
        }
    }
    _getViewByName(name) {
        var views = this.option("views");
        for (var i = 0; i < views.length; i++) {
            if (views[i].name === name || views[i].type === name || views[i] === name) {
                return views[i]
            }
        }
    }
    _recalculateWorkspace() {
        this._workSpaceRecalculation = new Deferred;
        this._waitAsyncTemplate(() => {
            triggerResizeEvent(this._workSpace.$element());
            this._workSpace._refreshDateTimeIndication()
        })
    }
    _workSpaceConfig(groups, countConfig) {
        var _currentViewOptions$s2;
        var currentViewOptions = this._getCurrentViewOptions();
        var scrolling = this.option("scrolling");
        var isVirtualScrolling = "virtual" === scrolling.mode || "virtual" === (null === (_currentViewOptions$s2 = currentViewOptions.scrolling) || void 0 === _currentViewOptions$s2 ? void 0 : _currentViewOptions$s2.mode);
        var horizontalVirtualScrollingAllowed = isVirtualScrolling && (!isDefined(scrolling.orientation) || ["horizontal", "both"].filter(item => {
            var _currentViewOptions$s3;
            return scrolling.orientation === item || (null === (_currentViewOptions$s3 = currentViewOptions.scrolling) || void 0 === _currentViewOptions$s3 ? void 0 : _currentViewOptions$s3.orientation) === item
        }).length > 0);
        var crossScrollingEnabled = this.option("crossScrollingEnabled") || horizontalVirtualScrollingAllowed;
        var result = extend({
            key: this.key,
            noDataText: this.option("noDataText"),
            firstDayOfWeek: this.option("firstDayOfWeek"),
            startDayHour: this.option("startDayHour"),
            endDayHour: this.option("endDayHour"),
            tabIndex: this.option("tabIndex"),
            accessKey: this.option("accessKey"),
            focusStateEnabled: this.option("focusStateEnabled"),
            cellDuration: this.option("cellDuration"),
            showAllDayPanel: this.option("showAllDayPanel"),
            showCurrentTimeIndicator: this.option("showCurrentTimeIndicator"),
            indicatorTime: this.option("indicatorTime"),
            indicatorUpdateInterval: this.option("indicatorUpdateInterval"),
            shadeUntilCurrentTime: this.option("shadeUntilCurrentTime"),
            allDayExpanded: this._appointments.option("items"),
            crossScrollingEnabled: crossScrollingEnabled,
            dataCellTemplate: this.option("dataCellTemplate"),
            timeCellTemplate: this.option("timeCellTemplate"),
            resourceCellTemplate: this.option("resourceCellTemplate"),
            dateCellTemplate: this.option("dateCellTemplate"),
            allowMultipleCellSelection: this.option("allowMultipleCellSelection"),
            selectedCellData: this.option("selectedCellData"),
            onSelectionChanged: args => {
                this.option("selectedCellData", args.selectedCellData)
            },
            groupByDate: this._getCurrentViewOption("groupByDate"),
            scrolling: scrolling,
            draggingMode: this.option("_draggingMode"),
            resourceManager: getResourceManager(this.key),
            timeZoneCalculator: getTimeZoneCalculator(this.key),
            schedulerHeight: this.option("height"),
            schedulerWidth: this.option("width"),
            onSelectedCellsClick: this.showAddAppointmentPopup.bind(this),
            onVirtualScrollingUpdated: this._renderAppointments.bind(this),
            getHeaderHeight: () => utils.DOM.getHeaderHeight(this._header),
            onScrollEnd: () => this._appointments.updateResizableArea(),
            renovateRender: this._isRenovatedRender(isVirtualScrolling)
        }, currentViewOptions);
        result.observer = this;
        result.intervalCount = countConfig.intervalCount;
        result.startDate = countConfig.startDate;
        result.groups = groups;
        result.onCellClick = this._createActionByOption("onCellClick");
        result.onCellContextMenu = this._createActionByOption("onCellContextMenu");
        result.currentDate = dateUtils.trimTime(new Date(this._dateOption("currentDate")));
        result.hoursInterval = result.cellDuration / 60;
        result.allDayExpanded = this._isAllDayExpanded(getAppointmentDataProvider(this.key).filteredItems);
        result.dataCellTemplate = result.dataCellTemplate ? this._getTemplate(result.dataCellTemplate) : null;
        result.timeCellTemplate = result.timeCellTemplate ? this._getTemplate(result.timeCellTemplate) : null;
        result.resourceCellTemplate = result.resourceCellTemplate ? this._getTemplate(result.resourceCellTemplate) : null;
        result.dateCellTemplate = result.dateCellTemplate ? this._getTemplate(result.dateCellTemplate) : null;
        return result
    }
    _isRenovatedRender(isVirtualScrolling) {
        return this.option("renovateRender") && hasWindow() || isVirtualScrolling
    }
    _waitAsyncTemplate(callback) {
        if (this._options.silent("templatesRenderAsynchronously")) {
            var timer = setTimeout(() => {
                callback();
                clearTimeout(timer)
            });
            this._asyncTemplatesTimers.push(timer)
        } else {
            callback()
        }
    }
    _getCurrentViewOptions() {
        return this._currentView
    }
    _getCurrentViewOption(optionName) {
        var currentViewOptions = this._getCurrentViewOptions();
        if (currentViewOptions && void 0 !== currentViewOptions[optionName]) {
            return currentViewOptions[optionName]
        }
        return this.option(optionName)
    }
    _getAppointmentTemplate(optionName) {
        var currentViewOptions = this._getCurrentViewOptions();
        if (currentViewOptions && currentViewOptions[optionName]) {
            return this._getTemplate(currentViewOptions[optionName])
        }
        return this._getTemplateByOption(optionName)
    }
    _updateOption(viewName, optionName, value) {
        var currentViewOptions = this._getCurrentViewOptions();
        if (!currentViewOptions || !isDefined(currentViewOptions[optionName])) {
            this["_" + viewName].option(optionName, value)
        }
    }
    _refreshWorkSpace(groups) {
        this._cleanWorkspace();
        delete this._workSpace;
        this._renderWorkSpace(groups);
        if (this._readyToRenderAppointments) {
            this._appointments.option({
                fixedContainer: this._workSpace.getFixedContainer(),
                allDayContainer: this._workSpace.getAllDayContainer()
            });
            this._waitAsyncTemplate(() => this._workSpaceRecalculation.resolve())
        }
    }
    _cleanWorkspace() {
        this._appointments.$element().detach();
        this._workSpace._dispose();
        this._workSpace.$element().remove();
        this.option("selectedCellData", [])
    }
    getWorkSpaceScrollable() {
        return this._workSpace.getScrollable()
    }
    getWorkSpaceScrollableContainer() {
        return this._workSpace.getScrollableContainer()
    }
    getWorkSpaceDateTableOffset() {
        return !this.option("crossScrollingEnabled") || this.option("rtlEnabled") ? this._workSpace.getWorkSpaceLeftOffset() : 0
    }
    getWorkSpace() {
        return this._workSpace
    }
    getHeader() {
        return this._header
    }
    getMaxAppointmentsPerCell() {
        return this._getCurrentViewOption("maxAppointmentsPerCell")
    }
    _cleanPopup() {
        var _this$_appointmentPop;
        null === (_this$_appointmentPop = this._appointmentPopup) || void 0 === _this$_appointmentPop ? void 0 : _this$_appointmentPop.dispose()
    }
    _checkRecurringAppointment(targetAppointment, singleAppointment, exceptionDate, callback, isDeleted, isPopupEditing, dragEvent) {
        var recurrenceRule = ExpressionUtils.getField(this.key, "recurrenceRule", targetAppointment);
        if (!getRecurrenceProcessor().evalRecurrenceRule(recurrenceRule).isValid || !this._editing.allowUpdating) {
            callback();
            return
        }
        var editMode = this.option("recurrenceEditMode");
        switch (editMode) {
            case "series":
                callback();
                break;
            case "occurrence":
                this._excludeAppointmentFromSeries(targetAppointment, singleAppointment, exceptionDate, isDeleted, isPopupEditing, dragEvent);
                break;
            default:
                if (dragEvent) {
                    dragEvent.cancel = new Deferred
                }
                this._showRecurrenceChangeConfirm(isDeleted).done(editingMode => {
                    editingMode === RECURRENCE_EDITING_MODE.SERIES && callback();
                    editingMode === RECURRENCE_EDITING_MODE.OCCURENCE && this._excludeAppointmentFromSeries(targetAppointment, singleAppointment, exceptionDate, isDeleted, isPopupEditing, dragEvent)
                }).fail(() => this._appointments.moveAppointmentBack(dragEvent))
        }
    }
    _excludeAppointmentFromSeries(rawAppointment, newRawAppointment, exceptionDate, isDeleted, isPopupEditing, dragEvent) {
        var appointment = createAppointmentAdapter(this.key, _extends({}, rawAppointment));
        var newAppointment = createAppointmentAdapter(this.key, newRawAppointment);
        newAppointment.recurrenceRule = "";
        newAppointment.recurrenceException = "";
        var canCreateNewAppointment = !isDeleted && !isPopupEditing;
        if (canCreateNewAppointment) {
            var keyPropertyName = getAppointmentDataProvider(this.key).keyName;
            delete newRawAppointment[keyPropertyName];
            this.addAppointment(newRawAppointment)
        }
        appointment.recurrenceException = this._createRecurrenceException(appointment, exceptionDate);
        if (isPopupEditing) {
            this._appointmentPopup.show(newRawAppointment, {
                isToolbarVisible: true,
                action: ACTION_TO_APPOINTMENT.EXCLUDE_FROM_SERIES,
                excludeInfo: {
                    sourceAppointment: rawAppointment,
                    updatedAppointment: appointment.source()
                }
            });
            this._editAppointmentData = rawAppointment
        } else {
            this._updateAppointment(rawAppointment, appointment.source(), () => {
                this._appointments.moveAppointmentBack(dragEvent)
            }, dragEvent)
        }
    }
    _createRecurrenceException(appointment, exceptionDate) {
        var result = [];
        if (appointment.recurrenceException) {
            result.push(appointment.recurrenceException)
        }
        result.push(this._getSerializedDate(exceptionDate, appointment.startDate, appointment.allDay));
        return result.join()
    }
    _getSerializedDate(date, startDate, isAllDay) {
        isAllDay && date.setHours(startDate.getHours(), startDate.getMinutes(), startDate.getSeconds(), startDate.getMilliseconds());
        return dateSerialization.serializeDate(date, UTC_FULL_DATE_FORMAT)
    }
    _showRecurrenceChangeConfirm(isDeleted) {
        var message = messageLocalization.format(isDeleted ? "dxScheduler-confirmRecurrenceDeleteMessage" : "dxScheduler-confirmRecurrenceEditMessage");
        var seriesText = messageLocalization.format(isDeleted ? "dxScheduler-confirmRecurrenceDeleteSeries" : "dxScheduler-confirmRecurrenceEditSeries");
        var occurrenceText = messageLocalization.format(isDeleted ? "dxScheduler-confirmRecurrenceDeleteOccurrence" : "dxScheduler-confirmRecurrenceEditOccurrence");
        this._recurrenceDialog = customDialog({
            messageHtml: message,
            showCloseButton: true,
            showTitle: true,
            buttons: [{
                text: seriesText,
                onClick: function() {
                    return RECURRENCE_EDITING_MODE.SERIES
                }
            }, {
                text: occurrenceText,
                onClick: function() {
                    return RECURRENCE_EDITING_MODE.OCCURENCE
                }
            }],
            popupOptions: {
                onHidden: e => {
                    e.component.$element().remove()
                }
            }
        });
        return this._recurrenceDialog.show()
    }
    _getUpdatedData(rawAppointment) {
        var timeZoneCalculator = getTimeZoneCalculator(this.key);
        var getConvertedFromGrid = date => date ? timeZoneCalculator.createDate(date, {
            path: "fromGrid"
        }) : void 0;
        var isValidDate = date => !isNaN(new Date(date).getTime());
        var targetCell = this.getTargetCellData();
        var appointment = createAppointmentAdapter(this.key, rawAppointment);
        var cellStartDate = getConvertedFromGrid(targetCell.startDate);
        var cellEndDate = getConvertedFromGrid(targetCell.endDate);
        var appointmentStartDate = new Date(appointment.startDate);
        var appointmentEndDate = new Date(appointment.endDate);
        var resultedStartDate = cellStartDate || appointmentStartDate;
        if (!isValidDate(appointmentStartDate)) {
            appointmentStartDate = resultedStartDate
        }
        if (!isValidDate(appointmentEndDate)) {
            appointmentEndDate = cellEndDate
        }
        var duration = appointmentEndDate.getTime() - appointmentStartDate.getTime();
        var isKeepAppointmentHours = this._workSpace.keepOriginalHours() && isValidDate(appointment.startDate) && isValidDate(cellStartDate);
        if (isKeepAppointmentHours) {
            var {
                trimTime: trimTime
            } = dateUtils;
            var startDate = timeZoneCalculator.createDate(appointment.startDate, {
                path: "toGrid"
            });
            var timeInMs = startDate.getTime() - trimTime(startDate).getTime();
            resultedStartDate = new Date(trimTime(targetCell.startDate).getTime() + timeInMs);
            resultedStartDate = timeZoneCalculator.createDate(resultedStartDate, {
                path: "fromGrid"
            })
        }
        var result = createAppointmentAdapter(this.key, {});
        if (void 0 !== targetCell.allDay) {
            result.allDay = targetCell.allDay
        }
        result.startDate = resultedStartDate;
        var resultedEndDate = new Date(resultedStartDate.getTime() + duration);
        if (this.appointmentTakesAllDay(rawAppointment) && !result.allDay && this._workSpace.supportAllDayRow()) {
            resultedEndDate = this._workSpace.calculateEndDate(resultedStartDate)
        }
        if (appointment.allDay && !this._workSpace.supportAllDayRow() && !this._workSpace.keepOriginalHours()) {
            var dateCopy = new Date(resultedStartDate);
            dateCopy.setHours(0);
            resultedEndDate = new Date(dateCopy.getTime() + duration);
            if (0 !== resultedEndDate.getHours()) {
                resultedEndDate.setHours(this._getCurrentViewOption("endDayHour"))
            }
        }
        var timeZoneOffset = timeZoneUtils.getTimezoneOffsetChangeInMs(appointmentStartDate, appointmentEndDate, resultedStartDate, resultedEndDate);
        result.endDate = new Date(resultedEndDate.getTime() - timeZoneOffset);
        var rawResult = result.source();
        getResourceManager(this.key).setResourcesToItem(rawResult, targetCell.groups);
        return rawResult
    }
    getTargetedAppointment(appointment, element) {
        var settings = utils.dataAccessors.getAppointmentSettings(element);
        var info = utils.dataAccessors.getAppointmentInfo(element);
        var appointmentIndex = $(element).data(this._appointments._itemIndexKey());
        var adapter = createAppointmentAdapter(this.key, appointment);
        var targetedAdapter = adapter.clone();
        if (this._isAgenda() && adapter.isRecurrent) {
            var getStartDate = this.getRenderingStrategyInstance().getAppointmentDataCalculator();
            var newStartDate = getStartDate($(element), adapter.startDate).startDate;
            targetedAdapter.startDate = newStartDate;
            targetedAdapter.endDate = new Date(newStartDate.getTime() + adapter.duration)
        } else if (settings) {
            targetedAdapter.startDate = info ? info.sourceAppointment.startDate : adapter.startDate;
            targetedAdapter.endDate = info ? info.sourceAppointment.endDate : adapter.endDate
        }
        var rawTargetedAppointment = targetedAdapter.source();
        if (element) {
            this.setTargetedAppointmentResources(rawTargetedAppointment, element, appointmentIndex)
        }
        return rawTargetedAppointment
    }
    subscribe(subject, action) {
        this._subscribes[subject] = subscribes[subject] = action
    }
    fire(subject) {
        var callback = this._subscribes[subject];
        var args = Array.prototype.slice.call(arguments);
        if (!isFunction(callback)) {
            throw errors.Error("E1031", subject)
        }
        return callback.apply(this, args.slice(1))
    }
    getTargetCellData() {
        return this._workSpace.getDataByDroppableCell()
    }
    _updateAppointment(target, rawAppointment, onUpdatePrevented, dragEvent) {
        var updatingOptions = {
            newData: rawAppointment,
            oldData: extend({}, target),
            cancel: false
        };
        var performFailAction = function(err) {
            if (onUpdatePrevented) {
                onUpdatePrevented.call(this)
            }
            if (err && "Error" === err.name) {
                throw err
            }
        }.bind(this);
        this._actions[StoreEventNames.UPDATING](updatingOptions);
        if (dragEvent && !isDeferred(dragEvent.cancel)) {
            dragEvent.cancel = new Deferred
        }
        return this._processActionResult(updatingOptions, (function(canceled) {
            var deferred = new Deferred;
            if (!canceled) {
                this._expandAllDayPanel(rawAppointment);
                try {
                    deferred = getAppointmentDataProvider(this.key).update(target, rawAppointment).done(() => {
                        dragEvent && dragEvent.cancel.resolve(false)
                    }).always(storeAppointment => this._onDataPromiseCompleted(StoreEventNames.UPDATED, storeAppointment)).fail(() => performFailAction())
                } catch (err) {
                    performFailAction(err);
                    deferred.resolve()
                }
            } else {
                performFailAction();
                deferred.resolve()
            }
            return deferred.promise()
        }))
    }
    _processActionResult(actionOptions, callback) {
        var deferred = new Deferred;
        var resolveCallback = callbackResult => {
            when(fromPromise(callbackResult)).always(deferred.resolve)
        };
        if (isPromise(actionOptions.cancel)) {
            when(fromPromise(actionOptions.cancel)).always(cancel => {
                if (!isDefined(cancel)) {
                    cancel = "rejected" === actionOptions.cancel.state()
                }
                resolveCallback(callback.call(this, cancel))
            })
        } else {
            resolveCallback(callback.call(this, actionOptions.cancel))
        }
        return deferred.promise()
    }
    _expandAllDayPanel(appointment) {
        if (!this._isAllDayExpanded(getAppointmentDataProvider(this.key).filteredItems) && this.appointmentTakesAllDay(appointment)) {
            this._workSpace.option("allDayExpanded", true)
        }
    }
    _onDataPromiseCompleted(handlerName, storeAppointment, appointment) {
        var args = {
            appointmentData: appointment || storeAppointment
        };
        if (storeAppointment instanceof Error) {
            args.error = storeAppointment
        } else {
            this._appointmentPopup.visible && this._appointmentPopup.hide()
        }
        this._actions[handlerName](args);
        this._fireContentReadyAction()
    }
    getAppointmentsInstance() {
        return this._appointments
    }
    getLayoutManager() {
        return this._layoutManager
    }
    getRenderingStrategyInstance() {
        return this.getLayoutManager().getRenderingStrategyInstance()
    }
    getActions() {
        return this._actions
    }
    appointmentTakesAllDay(appointment) {
        return getAppointmentDataProvider(this.key).appointmentTakesAllDay(appointment, this._getCurrentViewOption("startDayHour"), this._getCurrentViewOption("endDayHour"))
    }
    dayHasAppointment(day, rawAppointment, trimTime) {
        var getConvertedToTimeZone = date => getTimeZoneCalculator(this.key).createDate(date, {
            path: "toGrid"
        });
        var appointment = createAppointmentAdapter(this.key, rawAppointment);
        var startDate = new Date(appointment.startDate);
        var endDate = new Date(appointment.endDate);
        startDate = getConvertedToTimeZone(startDate);
        endDate = getConvertedToTimeZone(endDate);
        if (day.getTime() === endDate.getTime()) {
            return startDate.getTime() === endDate.getTime()
        }
        if (trimTime) {
            day = dateUtils.trimTime(day);
            startDate = dateUtils.trimTime(startDate);
            endDate = dateUtils.trimTime(endDate)
        }
        var dayTimeStamp = day.getTime();
        var startDateTimeStamp = startDate.getTime();
        var endDateTimeStamp = endDate.getTime();
        return inArray(dayTimeStamp, [startDateTimeStamp, endDateTimeStamp]) > -1 || startDateTimeStamp < dayTimeStamp && endDateTimeStamp > dayTimeStamp
    }
    setTargetedAppointmentResources(rawAppointment, element, appointmentIndex) {
        var groups = this._getCurrentViewOption("groups");
        if (null !== groups && void 0 !== groups && groups.length) {
            var resourcesSetter = getResourceManager(this.key)._dataAccessors.setter;
            var workSpace = this._workSpace;
            var getGroups;
            var setResourceCallback;
            if (this._isAgenda()) {
                getGroups = function() {
                    var apptSettings = this.getLayoutManager()._positionMap[appointmentIndex];
                    return getCellGroups(apptSettings[0].groupIndex, this.getWorkSpace().option("groups"))
                };
                setResourceCallback = function(_, group) {
                    resourcesSetter[group.name](rawAppointment, group.id)
                }
            } else {
                getGroups = function() {
                    var setting = utils.dataAccessors.getAppointmentSettings(element) || {};
                    return workSpace.getCellDataByCoordinates({
                        left: setting.left,
                        top: setting.top
                    }).groups
                };
                setResourceCallback = function(field, value) {
                    resourcesSetter[field](rawAppointment, value)
                }
            }
            each(getGroups.call(this), setResourceCallback)
        }
    }
    getStartViewDate() {
        return this._workSpace.getStartViewDate()
    }
    getEndViewDate() {
        return this._workSpace.getEndViewDate()
    }
    showAddAppointmentPopup(cellData, cellGroups) {
        var appointmentAdapter = createAppointmentAdapter(this.key, {});
        var timeZoneCalculator = getTimeZoneCalculator(this.key);
        appointmentAdapter.allDay = cellData.allDay;
        appointmentAdapter.startDate = timeZoneCalculator.createDate(cellData.startDate, {
            path: "fromGrid"
        });
        appointmentAdapter.endDate = timeZoneCalculator.createDate(cellData.endDate, {
            path: "fromGrid"
        });
        var resultAppointment = extend(appointmentAdapter.source(), cellGroups);
        this.showAppointmentPopup(resultAppointment, true)
    }
    showAppointmentPopup(rawAppointment, createNewAppointment, rawTargetedAppointment) {
        var appointment = createAppointmentAdapter(this.key, rawTargetedAppointment || rawAppointment);
        var newTargetedAppointment = extend({}, rawAppointment, rawTargetedAppointment);
        var isCreateAppointment = null !== createNewAppointment && void 0 !== createNewAppointment ? createNewAppointment : isEmptyObject(rawAppointment);
        if (isEmptyObject(rawAppointment)) {
            rawAppointment = this.createPopupAppointment()
        }
        if (isCreateAppointment) {
            delete this._editAppointmentData;
            this._editing.allowAdding && this._appointmentPopup.show(rawAppointment, {
                isToolbarVisible: true,
                action: ACTION_TO_APPOINTMENT.CREATE
            })
        } else {
            this._checkRecurringAppointment(rawAppointment, newTargetedAppointment, appointment.startDate, () => {
                this._editAppointmentData = rawAppointment;
                this._appointmentPopup.show(rawAppointment, {
                    isToolbarVisible: this._editing.allowUpdating,
                    action: ACTION_TO_APPOINTMENT.UPDATE
                })
            }, false, true)
        }
    }
    createPopupAppointment() {
        var result = {};
        var toMs = dateUtils.dateToMilliseconds;
        var startDate = this.option("currentDate");
        var endDate = new Date(startDate.getTime() + this.option("cellDuration") * toMs("minute"));
        ExpressionUtils.setField(this.key, "startDate", result, startDate);
        ExpressionUtils.setField(this.key, "endDate", result, endDate);
        return result
    }
    hideAppointmentPopup(saveChanges) {
        var _this$_appointmentPop2;
        if (null !== (_this$_appointmentPop2 = this._appointmentPopup) && void 0 !== _this$_appointmentPop2 && _this$_appointmentPop2.visible) {
            saveChanges && this._appointmentPopup.saveChanges();
            this._appointmentPopup.hide()
        }
    }
    showAppointmentTooltip(appointment, element, targetedAppointment) {
        if (appointment) {
            var settings = utils.dataAccessors.getAppointmentSettings(element);
            var deferredColor = getResourceManager(this.key).getAppointmentColor({
                itemData: targetedAppointment || appointment,
                groupIndex: null === settings || void 0 === settings ? void 0 : settings.groupIndex,
                groups: this.option("groups"),
                workspaceGroups: this.getWorkSpace().option("groups")
            });
            var info = new AppointmentTooltipInfo(appointment, targetedAppointment, deferredColor);
            this.showAppointmentTooltipCore(element, [info])
        }
    }
    showAppointmentTooltipCore(target, data, options) {
        if (this._appointmentTooltip.isAlreadyShown(target)) {
            this.hideAppointmentTooltip()
        } else {
            this._appointmentTooltip.show(target, data, extend(this._getExtraAppointmentTooltipOptions(), options))
        }
    }
    hideAppointmentTooltip() {
        this._appointmentTooltip && this._appointmentTooltip.hide()
    }
    scrollToTime(hours, minutes, date) {
        this._workSpace.scrollToTime(hours, minutes, date)
    }
    scrollTo(date, groups, allDay) {
        this._workSpace.scrollTo(date, groups, allDay)
    }
    _isHorizontalVirtualScrolling() {
        var scrolling = this.option("scrolling");
        var {
            orientation: orientation,
            mode: mode
        } = scrolling;
        var isVirtualScrolling = "virtual" === mode;
        return isVirtualScrolling && ("horizontal" === orientation || "both" === orientation)
    }
    addAppointment(rawAppointment) {
        var appointment = createAppointmentAdapter(this.key, rawAppointment);
        appointment.text = appointment.text || "";
        var serializedAppointment = appointment.source(true);
        var addingOptions = {
            appointmentData: serializedAppointment,
            cancel: false
        };
        this._actions[StoreEventNames.ADDING](addingOptions);
        return this._processActionResult(addingOptions, canceled => {
            if (canceled) {
                return (new Deferred).resolve()
            }
            this._expandAllDayPanel(serializedAppointment);
            return getAppointmentDataProvider(this.key).add(serializedAppointment).always(storeAppointment => this._onDataPromiseCompleted(StoreEventNames.ADDED, storeAppointment))
        })
    }
    updateAppointment(target, appointment) {
        return this._updateAppointment(target, appointment)
    }
    deleteAppointment(rawAppointment) {
        var deletingOptions = {
            appointmentData: rawAppointment,
            cancel: false
        };
        this._actions[StoreEventNames.DELETING](deletingOptions);
        this._processActionResult(deletingOptions, (function(canceled) {
            if (!canceled) {
                getAppointmentDataProvider(this.key).remove(rawAppointment).always(storeAppointment => this._onDataPromiseCompleted(StoreEventNames.DELETED, storeAppointment, rawAppointment))
            }
        }))
    }
    focus() {
        if (this._editAppointmentData) {
            this._appointments.focus()
        } else {
            this._workSpace.focus()
        }
    }
    getFirstDayOfWeek() {
        return isDefined(this.option("firstDayOfWeek")) ? this.option("firstDayOfWeek") : dateLocalization.firstDayOfWeekIndex()
    }
    _validateDayHours() {
        var startDayHour = this._getCurrentViewOption("startDayHour");
        var endDayHour = this._getCurrentViewOption("endDayHour");
        validateDayHours(startDayHour, endDayHour)
    }
}
Scheduler.include(DataHelperMixin);
registerComponent("dxScheduler", Scheduler);
export default Scheduler;
