UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,115 lines (1,114 loc) • 80.1 kB
/** * DevExtreme (ui/scheduler/ui.scheduler.work_space.js) * Version: 18.1.3 * Build date: Tue May 15 2018 * * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; var $ = require("../../core/renderer"), domAdapter = require("../../core/dom_adapter"), eventsEngine = require("../../events/core/events_engine"), dataUtils = require("../../core/element_data"), dateUtils = require("../../core/utils/date"), typeUtils = require("../../core/utils/type"), windowUtils = require("../../core/utils/window"), getPublicElement = require("../../core/utils/dom").getPublicElement, extend = require("../../core/utils/extend").extend, each = require("../../core/utils/iterator").each, messageLocalization = require("../../localization/message"), dateLocalization = require("../../localization/date"), toMs = dateUtils.dateToMilliseconds, Widget = require("../widget/ui.widget"), abstract = Widget.abstract, noop = require("../../core/utils/common").noop, isDefined = require("../../core/utils/type").isDefined, publisherMixin = require("./ui.scheduler.publisher_mixin"), eventUtils = require("../../events/utils"), pointerEvents = require("../../events/pointer"), errors = require("../widget/ui.errors"), clickEvent = require("../../events/click"), contextMenuEvent = require("../../events/contextmenu"), dragEvents = require("../../events/drag"), Scrollable = require("../scroll_view/ui.scrollable"), HorizontalGroupedStrategy = require("./ui.scheduler.work_space.grouped.strategy.horizontal"), VerticalGroupedStrategy = require("./ui.scheduler.work_space.grouped.strategy.vertical"), tableCreator = require("./ui.scheduler.table_creator"), VerticalShader = require("./ui.scheduler.current_time_shader.vertical"); var COMPONENT_CLASS = "dx-scheduler-work-space", GROUPED_WORKSPACE_CLASS = "dx-scheduler-work-space-grouped", VERTICAL_GROUPED_WORKSPACE_CLASS = "dx-scheduler-work-space-vertical-grouped", WORKSPACE_HORIZONTAL_GROUP_TABLE_CLASS = "dx-scheduler-work-space-horizontal-group-table", WORKSPACE_WITH_BOTH_SCROLLS_CLASS = "dx-scheduler-work-space-both-scrollbar", WORKSPACE_WITH_COUNT_CLASS = "dx-scheduler-work-space-count", WORKSPACE_WITH_ODD_CELLS_CLASS = "dx-scheduler-work-space-odd-cells", WORKSPACE_WITH_OVERLAPPING_CLASS = "dx-scheduler-work-space-overlapping", TIME_PANEL_CLASS = "dx-scheduler-time-panel", TIME_PANEL_CELL_CLASS = "dx-scheduler-time-panel-cell", TIME_PANEL_ROW_CLASS = "dx-scheduler-time-panel-row", ALL_DAY_PANEL_CLASS = "dx-scheduler-all-day-panel", ALL_DAY_TABLE_CLASS = "dx-scheduler-all-day-table", FIXED_CONTAINER_CLASS = "dx-scheduler-fixed-appointments", ALL_DAY_CONTAINER_CLASS = "dx-scheduler-all-day-appointments", ALL_DAY_TITLE_CLASS = "dx-scheduler-all-day-title", ALL_DAY_TITLE_HIDDEN_CLASS = "dx-scheduler-all-day-title-hidden", ALL_DAY_TABLE_CELL_CLASS = "dx-scheduler-all-day-table-cell", ALL_DAY_TABLE_ROW_CLASS = "dx-scheduler-all-day-table-row", WORKSPACE_WITH_ALL_DAY_CLASS = "dx-scheduler-work-space-all-day", WORKSPACE_WITH_COLLAPSED_ALL_DAY_CLASS = "dx-scheduler-work-space-all-day-collapsed", WORKSPACE_WITH_MOUSE_SELECTION_CLASS = "dx-scheduler-work-space-mouse-selection", HORIZONTAL_SIZES_CLASS = "dx-scheduler-cell-sizes-horizontal", VERTICAL_SIZES_CLASS = "dx-scheduler-cell-sizes-vertical", HEADER_PANEL_CLASS = "dx-scheduler-header-panel", HEADER_PANEL_CELL_CLASS = "dx-scheduler-header-panel-cell", HEADER_ROW_CLASS = "dx-scheduler-header-row", GROUP_ROW_CLASS = "dx-scheduler-group-row", GROUP_HEADER_CLASS = "dx-scheduler-group-header", GROUP_HEADER_CONTENT_CLASS = "dx-scheduler-group-header-content", DATE_TABLE_CLASS = "dx-scheduler-date-table", DATE_TABLE_CELL_CLASS = "dx-scheduler-date-table-cell", DATE_TABLE_ROW_CLASS = "dx-scheduler-date-table-row", DATE_TABLE_FOCUSED_CELL_CLASS = "dx-scheduler-focused-cell", DATE_TABLE_DROPPABLE_CELL_CLASS = "dx-scheduler-date-table-droppable-cell", SCHEDULER_HEADER_SCROLLABLE_CLASS = "dx-scheduler-header-scrollable", SCHEDULER_SIDEBAR_SCROLLABLE_CLASS = "dx-scheduler-sidebar-scrollable", SCHEDULER_DATE_TABLE_SCROLLABLE_CLASS = "dx-scheduler-date-table-scrollable", SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME = eventUtils.addNamespace(pointerEvents.down, "dxSchedulerWorkSpace"), SCHEDULER_CELL_DXDRAGENTER_EVENT_NAME = eventUtils.addNamespace(dragEvents.enter, "dxSchedulerDateTable"), SCHEDULER_CELL_DXDROP_EVENT_NAME = eventUtils.addNamespace(dragEvents.drop, "dxSchedulerDateTable"), SCHEDULER_CELL_DXCLICK_EVENT_NAME = eventUtils.addNamespace(clickEvent.name, "dxSchedulerDateTable"), SCHEDULER_CELL_DXPOINTERDOWN_EVENT_NAME = eventUtils.addNamespace(pointerEvents.down, "dxSchedulerDateTable"), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME = eventUtils.addNamespace(pointerEvents.up, "dxSchedulerDateTable"), SCHEDULER_CELL_DXPOINTERMOVE_EVENT_NAME = eventUtils.addNamespace(pointerEvents.move, "dxSchedulerDateTable"), CELL_DATA = "dxCellData", DATE_TABLE_MIN_CELL_WIDTH = 75, DATE_TABLE_CELL_BORDER = 1, DAY_MS = toMs("day"), HOUR_MS = toMs("hour"); var formatWeekday = function(date) { return dateLocalization.getDayNames("abbreviated")[date.getDay()] }; var SchedulerWorkSpace = Widget.inherit({ _supportedKeys: function() { var clickHandler = function(e) { e.preventDefault(); e.stopPropagation(); if (this._focusedCells && this._focusedCells.length) { var $itemElement = $(this.option("focusedElement")); e.target = this._focusedCells; this._showPopup = true; this._cellClickAction({ event: e, cellElement: $(this._focusedCells), cellData: this.getCellData($itemElement) }) } }, arrowPressHandler = function(e, cell) { e.preventDefault(); e.stopPropagation(); this._moveToCell(cell, e.shiftKey) }; return extend(this.callBase(), { enter: clickHandler, space: clickHandler, downArrow: function(e) { var $cell = this._getCellFromNextRow("next", e.shiftKey); arrowPressHandler.call(this, e, $cell) }, upArrow: function(e) { var $cell = this._getCellFromNextRow("prev", e.shiftKey); arrowPressHandler.call(this, e, $cell) }, rightArrow: function(e) { var $rightCell = this._getRightCell(e.shiftKey); arrowPressHandler.call(this, e, $rightCell) }, leftArrow: function(e) { var $leftCell = this._getLeftCell(e.shiftKey); arrowPressHandler.call(this, e, $leftCell) } }) }, _isRTL: function() { return this.option("rtlEnabled") }, _getFocusedCell: function() { return this._$focusedCell || this._$dateTable.find("." + DATE_TABLE_CELL_CLASS).eq(0) }, _getAllFocusedCells: function() { return this._focusedCells || this._$dateTable.find("." + DATE_TABLE_CELL_CLASS).eq(0) }, _getCellFromNextRow: function(direction) { var $currentCell = this._$focusedCell; if (isDefined($currentCell)) { var cellIndex = $currentCell.index(), $row = $currentCell.parent(), $cell = $row[direction]().children().eq(cellIndex); $cell = this._checkForViewBounds($cell); return $cell } }, _checkForViewBounds: function($item) { if (!$item.length) { $item = this._$focusedCell } return $item }, _getRightCell: function(isMultiSelection) { if (!isDefined(this._$focusedCell)) { return } var $rightCell, $focusedCell = this._$focusedCell, groupCount = this._getGroupCount(), rowCellCount = isMultiSelection ? this._getCellCount() : this._getTotalCellCount(groupCount), lastIndexInRow = rowCellCount - 1, edgeCellIndex = this._isRTL() ? 0 : lastIndexInRow, currentIndex = $focusedCell.index(), direction = this._isRTL() ? "prev" : "next"; if (currentIndex === edgeCellIndex || isMultiSelection && this._isGroupEndCell($focusedCell)) { var $row = $focusedCell.parent(), sign = this._isRTL() ? 1 : -1; $rightCell = $row[direction]().children().eq(currentIndex + sign * lastIndexInRow); $rightCell = this._checkForViewBounds($rightCell) } else { $rightCell = $focusedCell[direction]() } return $rightCell }, _isGroupEndCell: function($cell) { var cellsInRow = this._getCellCount(), currentCellIndex = $cell.index(), result = currentCellIndex % cellsInRow; return this._isRTL() ? 0 === result : result === cellsInRow - 1 }, _getLeftCell: function(isMultiSelection) { if (!isDefined(this._$focusedCell)) { return } var $leftCell, $focusedCell = this._$focusedCell, groupCount = this._getGroupCount(), rowCellCount = isMultiSelection ? this._getCellCount() : this._getTotalCellCount(groupCount), lastIndexInRow = rowCellCount - 1, edgeCellIndex = this._isRTL() ? lastIndexInRow : 0, currentIndex = $focusedCell.index(), direction = this._isRTL() ? "next" : "prev"; if (currentIndex === edgeCellIndex || isMultiSelection && this._isGroupStartCell($focusedCell)) { var $row = $focusedCell.parent(), sign = this._isRTL() ? -1 : 1; $leftCell = $row[direction]().children().eq(currentIndex + sign * lastIndexInRow); $leftCell = this._checkForViewBounds($leftCell) } else { $leftCell = $focusedCell[direction]() } return $leftCell }, _isGroupStartCell: function($cell) { var cellsInRow = this._getCellCount(), currentCellIndex = $cell.index(), result = currentCellIndex % cellsInRow; return this._isRTL() ? result === cellsInRow - 1 : 0 === result }, _moveToCell: function($cell, isMultiSelection) { isMultiSelection = isMultiSelection && this.option("allowMultipleCellSelection"); this._setFocusedCell($cell, isMultiSelection); this._dateTableScrollable.scrollToElement($cell) }, _setFocusedCell: function($cell, isMultiSelection) { if (!isDefined($cell) || !$cell.length) { return } this._releaseFocusedCell(); this._focusedCells = []; if (isMultiSelection) { $cell = this._correctCellForGroup($cell); var $targetCells = this._getCellsBetween($cell, this._$prevCell); this._focusedCells = $targetCells.toArray() } else { this._focusedCells = [$cell.get(0)]; this._$prevCell = $cell } var $focusedCells = $(this._focusedCells); this._toggleFocusClass(true, $focusedCells); this.setAria("label", "Add appointment", $focusedCells); this._toggleFocusedCellClass(true, $cell); this._$focusedCell = $cell; var selectedCellData = this.getFocusedCellData(); this.option("selectedCellData", selectedCellData); this._selectionChangedAction({ selectedCellData: selectedCellData }) }, _correctCellForGroup: function($cell) { var $focusedCell = this._$focusedCell, cellGroupIndex = this._getGroupIndexByCell($cell), focusedCellGroupIndex = this._getGroupIndexByCell($focusedCell), isDifferentTables = this._hasAllDayClass($cell) !== this._hasAllDayClass($focusedCell); return focusedCellGroupIndex !== cellGroupIndex || isDifferentTables ? $focusedCell : $cell }, _getCellsBetween: function($first, $last) { var isAllDayTable = this._hasAllDayClass($last), $cells = this._getCells(isAllDayTable), firstIndex = $cells.index($first), lastIndex = $cells.index($last); if (firstIndex > lastIndex) { var buffer = firstIndex; firstIndex = lastIndex; lastIndex = buffer } $cells = $cells.slice(firstIndex, lastIndex + 1); if (this._getGroupCount() > 1) { var result = [], focusedGroupIndex = this._getGroupIndexByCell($first); each($cells, function(_, cell) { var groupIndex = this._getGroupIndexByCell($(cell)); if (focusedGroupIndex === groupIndex) { result.push(cell) } }.bind(this)); $cells = $(result) } return $cells }, _hasAllDayClass: function($cell) { return $cell.hasClass(ALL_DAY_TABLE_CELL_CLASS) }, _getGroupIndexByCell: function($cell) { var cellsInRow = this._getCellCount(), currentCellIndex = $cell.index() + 1, groupIndex = Math.ceil(currentCellIndex / cellsInRow); return groupIndex }, _toggleFocusedCellClass: function(isFocused, $element) { var $focusTarget = $element && $element.length ? $element : this._focusTarget(); $focusTarget.toggleClass(DATE_TABLE_FOCUSED_CELL_CLASS, isFocused) }, _releaseFocusedCell: function($cell) { $cell = $cell || $(this._focusedCells); if (isDefined($cell)) { this._toggleFocusClass(false, $cell); this._toggleFocusedCellClass(false, $cell); this.setAria("label", void 0, $cell) } this.option("selectedCellData", []) }, _focusInHandler: function(e) { if ($(e.target).is(this._focusTarget()) && false !== this._isCellClick) { delete this._isCellClick; delete this._contextMenuHandled; this.callBase.apply(this, arguments); var $cell = this._getFocusedCell(); this._setFocusedCell($cell) } }, _focusOutHandler: function() { this.callBase.apply(this, arguments); if (!this._contextMenuHandled) { this._releaseFocusedCell() } }, _focusTarget: function() { return this.$element() }, _activeStateUnit: "." + DATE_TABLE_CELL_CLASS + ", ." + ALL_DAY_TABLE_CELL_CLASS, _getDefaultOptions: function() { return extend(this.callBase(), { currentDate: new Date, intervalCount: 1, startDate: null, firstDayOfWeek: void 0, startDayHour: 0, endDayHour: 24, hoursInterval: .5, activeStateEnabled: true, hoverStateEnabled: true, groups: [], showAllDayPanel: true, allDayExpanded: false, onCellClick: null, crossScrollingEnabled: false, dataCellTemplate: null, timeCellTemplate: null, resourceCellTemplate: null, dateCellTemplate: null, allowMultipleCellSelection: true, indicatorTime: new Date, indicatorUpdateInterval: 5 * toMs("minute"), shadeUntilCurrentTime: true, groupOrientation: "horizontal", selectedCellData: [] }) }, _optionChanged: function(args) { switch (args.name) { case "dateCellTemplate": case "resourceCellTemplate": case "dataCellTemplate": case "timeCellTemplate": case "startDayHour": case "endDayHour": case "hoursInterval": case "firstDayOfWeek": case "currentDate": case "startDate": this._cleanWorkSpace(); break; case "groups": this._cleanView(); this._removeAllDayElements(); this._initGrouping(); this.repaint(); break; case "groupOrientation": this._initGroupedStrategy(); this._createAllDayPanelElements(); this._removeAllDayElements(); this._cleanWorkSpace(); break; case "showAllDayPanel": if (this._isVerticalGroupedWorkSpace()) { this._cleanView(); this._removeAllDayElements(); this._initGrouping(); this.repaint() } else { this._toggleAllDayVisibility() } break; case "allDayExpanded": this._changeAllDayVisibility(); this.notifyObserver("allDayPanelToggled"); this._attachTablesEvents(); this.headerPanelOffsetRecalculate(); this._updateScrollable(); break; case "onSelectionChanged": this._createSelectionChangedAction(); break; case "onCellClick": this._createCellClickAction(); break; case "onCellContextMenu": this._attachContextMenuEvent(); break; case "intervalCount": this._cleanWorkSpace(); this._toggleWorkSpaceCountClass(); this._toggleFixedScrollableClass(); break; case "crossScrollingEnabled": this._toggleHorizontalScrollClass(); this._dateTableScrollable.option(this._dateTableScrollableConfig()); break; case "width": this.callBase(args); this._dimensionChanged(); break; case "allowMultipleCellSelection": break; case "selectedCellData": break; default: this.callBase(args) } }, _cleanWorkSpace: function() { this._cleanView(); this._toggleGroupedClass(); this._toggleWorkSpaceWithOddCells(); this._renderView() }, _init: function() { this.callBase(); this._initGrouping(); this._toggleHorizontalScrollClass(); this._toggleWorkSpaceCountClass(); this._toggleWorkSpaceWithOddCells(); this._toggleWorkSpaceOverlappingClass(); this.$element().addClass(COMPONENT_CLASS).addClass(this._getElementClass()) }, _initGrouping: function() { this._initGroupedStrategy(); this._toggleGroupingDirectionClass() }, _initGroupedStrategy: function() { var strategyName = this.option("groups").length ? this.option("groupOrientation") : this._getDefaultGroupStrategy(); var Strategy = "vertical" === strategyName ? VerticalGroupedStrategy : HorizontalGroupedStrategy; this._groupedStrategy = new Strategy(this) }, _getDefaultGroupStrategy: function() { return "horizontal" }, _isVerticalGroupedWorkSpace: function() { return !!this.option("groups").length && "vertical" === this.option("groupOrientation") }, _toggleHorizontalScrollClass: function() { this.$element().toggleClass(WORKSPACE_WITH_BOTH_SCROLLS_CLASS, this.option("crossScrollingEnabled")) }, _toggleWorkSpaceCountClass: function() { this.$element().toggleClass(WORKSPACE_WITH_COUNT_CLASS, this._isWorkSpaceWithCount()) }, _isWorkSpaceWithCount: function() { return this.option("intervalCount") > 1 }, _toggleWorkSpaceWithOddCells: function() { this.$element().toggleClass(WORKSPACE_WITH_ODD_CELLS_CLASS, this._isWorkspaceWithOddCells()) }, _isWorkspaceWithOddCells: function() { return .5 === this.option("hoursInterval") }, _toggleWorkSpaceOverlappingClass: function() { this.$element().toggleClass(WORKSPACE_WITH_OVERLAPPING_CLASS, this._isWorkSpaceWithOverlapping()) }, _isWorkSpaceWithOverlapping: function() { return null !== this.invoke("getMaxAppointmentsPerCell") }, _toggleGroupingDirectionClass: function() { this.$element().toggleClass(VERTICAL_GROUPED_WORKSPACE_CLASS, this._isVerticalGroupedWorkSpace()) }, _getRealGroupOrientation: function() { return this._isVerticalGroupedWorkSpace() ? "vertical" : "horizontal" }, _getTimePanelClass: function() { return TIME_PANEL_CLASS }, _getDateTableClass: function() { return DATE_TABLE_CLASS }, _getDateTableRowClass: function() { return DATE_TABLE_ROW_CLASS }, _getDateTableCellClass: function(i, j) { var cellClass = DATE_TABLE_CELL_CLASS + " " + HORIZONTAL_SIZES_CLASS + " " + VERTICAL_SIZES_CLASS; return this._needApplyLastGroupCellClass() ? this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, j + 1, i, j) : cellClass }, _needApplyLastGroupCellClass: function() { return true }, _getGroupRowClass: function() { return GROUP_ROW_CLASS }, _getGroupHeaderClass: function() { return GROUP_HEADER_CLASS }, _getGroupHeaderContentClass: function() { return GROUP_HEADER_CONTENT_CLASS }, _initWorkSpaceUnits: function() { this._$headerPanel = $("<table>"); this._$thead = $("<thead>").appendTo(this._$headerPanel); this._$fixedContainer = $("<div>").addClass(FIXED_CONTAINER_CLASS); this._$allDayContainer = $("<div>").addClass(ALL_DAY_CONTAINER_CLASS); this._initAllDayPanelElements(); this._createAllDayPanelElements(); this._$timePanel = $("<table>").addClass(this._getTimePanelClass()); this._$dateTable = $("<table>"); this._$groupTable = $("<table>").addClass(WORKSPACE_HORIZONTAL_GROUP_TABLE_CLASS) }, _initAllDayPanelElements: function() { this._allDayTitles = []; this._allDayTables = []; this._allDayPanels = [] }, _createAllDayPanelElements: function() { var groupCount = this._getGroupCount(); if (this._isVerticalGroupedWorkSpace() && 0 !== groupCount) { for (var i = 0; i < groupCount; i++) { var $allDayTitle = $("<div>").addClass(ALL_DAY_TITLE_CLASS).text(messageLocalization.format("dxScheduler-allDay")); this._allDayTitles.push($allDayTitle); this._$allDayTable = $("<table>"); this._allDayTables.push(this._$allDayTable); this._$allDayPanel = $("<div>").addClass(ALL_DAY_PANEL_CLASS).append(this._$allDayTable); this._allDayPanels.push(this._$allDayPanel) } } else { this._$allDayTitle = $("<div>").addClass(ALL_DAY_TITLE_CLASS).text(messageLocalization.format("dxScheduler-allDay")).appendTo(this.$element()); this._$allDayTable = $("<table>"); this._$allDayPanel = $("<div>").addClass(ALL_DAY_PANEL_CLASS).append(this._$allDayTable) } }, _initDateTableScrollable: function() { var $dateTableScrollable = $("<div>").addClass(SCHEDULER_DATE_TABLE_SCROLLABLE_CLASS); this._dateTableScrollable = this._createComponent($dateTableScrollable, Scrollable, this._dateTableScrollableConfig()) }, _dateTableScrollableConfig: function() { var config = { useKeyboard: false, useNative: false, bounceEnabled: false, updateManually: true, pushBackValue: 0 }; if (this._needCreateCrossScrolling()) { config = extend(config, this._createCrossScrollingConfig()) } return config }, _createCrossScrollingConfig: function() { var headerScrollableOnScroll, sidebarScrollableOnScroll, config = {}; config.direction = "both"; config.onStart = function(e) { if (this._headerScrollable) { headerScrollableOnScroll = this._headerScrollable.option("onScroll"); this._headerScrollable.option("onScroll", void 0) } if (this._sidebarScrollable) { sidebarScrollableOnScroll = this._sidebarScrollable.option("onScroll"); this._sidebarScrollable.option("onScroll", void 0) } }.bind(this); config.onScroll = function(e) { this._sidebarScrollable && this._sidebarScrollable.scrollTo({ top: e.scrollOffset.top }); this._headerScrollable && this._headerScrollable.scrollTo({ left: e.scrollOffset.left }) }.bind(this); config.onEnd = function() { this.notifyObserver("updateResizableArea", {}); this._headerScrollable && this._headerScrollable.option("onScroll", headerScrollableOnScroll); this._sidebarScrollable && this._sidebarScrollable.option("onScroll", sidebarScrollableOnScroll) }.bind(this); return config }, _createWorkSpaceElements: function() { if (this.option("crossScrollingEnabled")) { this._createWorkSpaceScrollableElements() } else { this._createWorkSpaceStaticElements() } }, _createWorkSpaceStaticElements: function() { if (this._isVerticalGroupedWorkSpace()) { this._dateTableScrollable.$content().append(this._$allDayContainer, this._$groupTable, this._$timePanel, this._$dateTable); this.$element().append(this._$fixedContainer, this._$headerPanel, this._dateTableScrollable.$element()) } else { this._dateTableScrollable.$content().append(this._$timePanel, this._$dateTable); this.$element().append(this._$fixedContainer, this._$headerPanel, this._$allDayContainer, this._$allDayPanel, this._dateTableScrollable.$element()) } }, _createWorkSpaceScrollableElements: function() { this.$element().append(this._$fixedContainer); this._createHeaderScrollable(); this._createSidebarScrollable(); this.$element().append(this._dateTableScrollable.$element()); this._headerScrollable.$content().append(this._$headerPanel); this._dateTableScrollable.$content().append(this._$dateTable); if (this._isVerticalGroupedWorkSpace()) { this._dateTableScrollable.$content().prepend(this._$allDayContainer); this._sidebarScrollable.$content().append(this._$groupTable, this._$timePanel) } else { this._headerScrollable.$content().append(this._$allDayContainer, this._$allDayPanel) } this._sidebarScrollable.$content().append(this._$timePanel) }, _createHeaderScrollable: function() { var dateTableScrollableOnScroll, $headerScrollable = $("<div>").addClass(SCHEDULER_HEADER_SCROLLABLE_CLASS).appendTo(this.$element()); this._headerScrollable = this._createComponent($headerScrollable, Scrollable, { useKeyboard: false, showScrollbar: false, direction: "horizontal", useNative: false, updateManually: true, bounceEnabled: false, pushBackValue: 0, onStart: function(e) { dateTableScrollableOnScroll = this._dateTableScrollable.option("onScroll"); this._dateTableScrollable.option("onScroll", void 0) }.bind(this), onScroll: function(e) { this._dateTableScrollable.scrollTo({ left: e.scrollOffset.left }) }.bind(this), onEnd: function(e) { this._dateTableScrollable.option("onScroll", dateTableScrollableOnScroll) }.bind(this) }) }, _createSidebarScrollable: function() { var dateTableScrollableOnScroll, $timePanelScrollable = $("<div>").addClass(SCHEDULER_SIDEBAR_SCROLLABLE_CLASS).appendTo(this.$element()); this._sidebarScrollable = this._createComponent($timePanelScrollable, Scrollable, { useKeyboard: false, showScrollbar: false, direction: "vertical", useNative: false, updateManually: true, bounceEnabled: false, pushBackValue: 0, onStart: function(e) { dateTableScrollableOnScroll = this._dateTableScrollable.option("onScroll"); this._dateTableScrollable.option("onScroll", void 0) }.bind(this), onScroll: function(e) { this._dateTableScrollable.scrollTo({ top: e.scrollOffset.top }) }.bind(this), onEnd: function(e) { this._dateTableScrollable.option("onScroll", dateTableScrollableOnScroll) }.bind(this) }) }, _visibilityChanged: function(visible) { if (visible && this._isVerticalGroupedWorkSpace()) { this._setHorizontalGroupHeaderCellsHeight() } if (visible && this._needCreateCrossScrolling()) { this._setTableSizes() } }, _attachTableClasses: function() { this._addTableClass(this._$dateTable, this._getDateTableClass()); if (this._isVerticalGroupedWorkSpace()) { var groupCount = this._getGroupCount(); for (var i = 0; i < groupCount; i++) { this._addTableClass(this._allDayTables[i], ALL_DAY_TABLE_CLASS) } } else { this._addTableClass(this._$allDayTable, ALL_DAY_TABLE_CLASS) } }, _attachHeaderTableClasses: function() { this._addTableClass(this._$headerPanel, HEADER_PANEL_CLASS) }, _addTableClass: function($el, className) { $el && !$el.hasClass(className) && $el.addClass(className) }, _setTableSizes: function() { this._attachTableClasses(); var cellWidth = this.getCellWidth(); if (cellWidth < DATE_TABLE_MIN_CELL_WIDTH) { cellWidth = DATE_TABLE_MIN_CELL_WIDTH } var minWidth = this._groupedStrategy.getWorkSpaceMinWidth(), $headerCells = this._$headerPanel.find("tr").last().find("th"); var width = cellWidth * $headerCells.length; if (width < minWidth) { width = minWidth } this._$headerPanel.width(width); this._$dateTable.width(width); this._$allDayTable && this._$allDayTable.width(width); this._attachHeaderTableClasses(); if (this._isVerticalGroupedWorkSpace()) { this._setHorizontalGroupHeaderCellsHeight() } }, _dimensionChanged: function() { if (this.option("crossScrollingEnabled")) { this._setTableSizes() } this.headerPanelOffsetRecalculate(); this._cleanCellDataCache(); this._cleanAllowedPositions() }, _needCreateCrossScrolling: function() { return this.option("crossScrollingEnabled") }, _getElementClass: noop, _getRowCount: noop, _getCellCount: noop, _initMarkup: function() { this._initWorkSpaceUnits(); this._initDateTableScrollable(); this._createWorkSpaceElements(); this.callBase(); if (!this.option("crossScrollingEnabled")) { this._attachTableClasses(); this._attachHeaderTableClasses() } this._toggleGroupedClass(); this._toggleFixedScrollableClass(); this._renderView(); this._attachEvents(); this._setFocusOnCellByOption(this.option("selectedCellData")) }, _render: function() { this.callBase(); this._renderDateTimeIndication(); this._setIndicationUpdateInterval() }, _toggleGroupedClass: function() { this.$element().toggleClass(GROUPED_WORKSPACE_CLASS, this._getGroupCount() > 0) }, _toggleFixedScrollableClass: noop, _renderView: function() { this._setFirstViewDate(); this._applyCellTemplates(this._renderGroupHeader()); this._renderDateHeader(); this._renderTimePanel(); if (this._isVerticalGroupedWorkSpace()) { var groupCount = this._getGroupCount(); for (var i = 0; i < groupCount; i++) { this._renderAllDayPanel(i) } } this._renderAllDayPanel(); this._renderDateTable(); if (this._isVerticalGroupedWorkSpace() && windowUtils.hasWindow()) { this._setHorizontalGroupHeaderCellsHeight() } this._shader = new VerticalShader }, _renderDateTimeIndication: noop, _setIndicationUpdateInterval: noop, _refreshDateTimeIndication: noop, _setFocusOnCellByOption: function(data) { var cells = []; this._releaseFocusedCell(); for (var i = 0; i < data.length; i++) { var groups = data[i].groups, groupIndex = groups ? this._getGroupIndexByResourceId(groups) : 0, allDay = !!data[i].allDay, coordinates = this.getCoordinatesByDate(data[i].startDate, groupIndex, allDay), $cell = this._getCellByCoordinates(coordinates, groupIndex); if (isDefined($cell)) { this._toggleFocusClass(true, $cell); cells.push($cell) } } this._focusedCells = cells }, _getGroupIndexByResourceId: function(id) { var groups = this.option("groups"), groupKey = Object.keys(id)[0], groupValue = id[groupKey], tree = this.invoke("createResourcesTree", groups), index = 0; for (var i = 0; i < tree.length; i++) { if (tree[i].name === groupKey && tree[i].value === groupValue) { index = tree[i].leafIndex } } return index }, _setFirstViewDate: function() { var firstDayOfWeek = isDefined(this._firstDayOfWeek()) ? this._firstDayOfWeek() : dateLocalization.firstDayOfWeekIndex(); this._firstViewDate = dateUtils.getFirstWeekDate(this._getViewStartByOptions(), firstDayOfWeek); this._setStartDayHour(this._firstViewDate) }, _getViewStartByOptions: function() { if (!this.option("startDate")) { return this.option("currentDate") } else { var startDate = dateUtils.trimTime(this._getStartViewDate()), currentDate = this.option("currentDate"), diff = startDate.getTime() <= currentDate.getTime() ? 1 : -1, endDate = new Date(startDate.getTime() + this._getIntervalDuration() * diff); while (!this._dateInRange(currentDate, startDate, endDate, diff)) { startDate = endDate; endDate = new Date(startDate.getTime() + this._getIntervalDuration() * diff) } return diff > 0 ? startDate : endDate } }, _getStartViewDate: function() { return this.option("startDate") }, _dateInRange: function(date, startDate, endDate, diff) { return diff > 0 ? dateUtils.dateInRange(date, startDate, new Date(endDate.getTime() - 1)) : dateUtils.dateInRange(date, endDate, startDate, "date") }, _getIntervalDuration: function() { return toMs("day") * this.option("intervalCount") }, _setStartDayHour: function(date) { var startDayHour = this.option("startDayHour"); if (isDefined(startDayHour)) { date.setHours(startDayHour, startDayHour % 1 * 60, 0, 0) } }, _firstDayOfWeek: function() { return this.option("firstDayOfWeek") }, _attachEvents: function() { this._createSelectionChangedAction(); this._attachClickEvent(); this._attachContextMenuEvent() }, _attachClickEvent: function() { var that = this; var pointerDownAction = this._createAction(function(e) { that._pointerDownHandler(e.event) }); this._createCellClickAction(); var cellSelector = "." + DATE_TABLE_CELL_CLASS + ",." + ALL_DAY_TABLE_CELL_CLASS; var $element = this.$element(); eventsEngine.off($element, SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME); eventsEngine.off($element, SCHEDULER_CELL_DXCLICK_EVENT_NAME); eventsEngine.on($element, SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME, function(e) { if (eventUtils.isMouseEvent(e) && e.which > 1) { e.preventDefault(); return } pointerDownAction({ event: e }) }); eventsEngine.on($element, SCHEDULER_CELL_DXCLICK_EVENT_NAME, cellSelector, function(e) { var $cell = $(e.target); that._cellClickAction({ event: e, cellElement: getPublicElement($cell), cellData: that.getCellData($cell) }) }) }, _createCellClickAction: function() { var that = this; this._cellClickAction = this._createActionByOption("onCellClick", { afterExecute: function(e) { that._moveToClosestNonStub(e.args[0].event) } }) }, _createSelectionChangedAction: function() { this._selectionChangedAction = this._createActionByOption("onSelectionChanged") }, _moveToClosestNonStub: function(e) { var $target = $(e.target); if (this._showPopup && this._hasFocusClass($target)) { delete this._showPopup; this._showAddAppointmentPopup($target) } }, _pointerDownHandler: function(e) { var $target = $(e.target); if (!$target.hasClass(DATE_TABLE_CELL_CLASS) && !$target.hasClass(ALL_DAY_TABLE_CELL_CLASS)) { this._isCellClick = false; return } this._isCellClick = true; if ($target.hasClass(DATE_TABLE_FOCUSED_CELL_CLASS)) { this._showPopup = true } else { this._setFocusedCell($target) } }, _showAddAppointmentPopup: function($cell) { var firstCellData = this.getCellData($cell.first()), lastCellData = this.getCellData($cell.last()); var args = { startDate: firstCellData.startDate, endDate: lastCellData.endDate }; if (isDefined(lastCellData.allDay)) { args.allDay = lastCellData.allDay } extend(args, lastCellData.groups); this.notifyObserver("showAddAppointmentPopup", args) }, _attachContextMenuEvent: function() { this._createContextMenuAction(); var cellSelector = "." + DATE_TABLE_CELL_CLASS + ",." + ALL_DAY_TABLE_CELL_CLASS, $element = this.$element(), eventName = eventUtils.addNamespace(contextMenuEvent.name, this.NAME); eventsEngine.off($element, eventName, cellSelector); eventsEngine.on($element, eventName, cellSelector, this._contextMenuHandler.bind(this)) }, _contextMenuHandler: function(e) { var $cell = $(e.target); this._contextMenuAction({ event: e, cellElement: getPublicElement($cell), cellData: this.getCellData($cell) }); this._contextMenuHandled = true }, _createContextMenuAction: function() { this._contextMenuAction = this._createActionByOption("onCellContextMenu") }, _getGroupHeaderContainer: function() { if (this._isVerticalGroupedWorkSpace()) { return this._$groupTable } return this._$thead }, _getDateHeaderContainer: function() { return this._$thead }, _renderGroupHeader: function() { var $container = this._getGroupHeaderContainer(), groupCount = this._getGroupCount(), cellTemplates = []; if (groupCount) { var groupRows = this._makeGroupRows(this.option("groups")); this._attachGroupCountAttr(groupCount, groupRows); $container.append(groupRows.elements); cellTemplates = groupRows.cellTemplates } else { this._detachGroupCountAttr() } return cellTemplates }, _applyCellTemplates: function(templates) { templates.forEach(function(template) { template() }) }, _detachGroupCountAttr: function() { var groupedAttr = this._groupedStrategy.getGroupCountAttr(); this.$element().removeAttr(groupedAttr.attr) }, _attachGroupCountAttr: function(groupRowCount, groupRows) { var groupedAttr = this._groupedStrategy.getGroupCountAttr(groupRowCount, groupRows); this.$element().attr(groupedAttr.attr, groupedAttr.count) }, headerPanelOffsetRecalculate: function() { if (!this.option("resourceCellTemplate") && !this.option("dateCellTemplate")) { return } var headerPanelHeight = this.getHeaderPanelHeight(), headerHeight = this.invoke("getHeaderHeight"), allDayPanelHeight = this.supportAllDayRow() && this.option("showAllDayPanel") ? this._groupedStrategy.getAllDayTableHeight() : 0; headerPanelHeight && this._headerScrollable && this._headerScrollable.$element().height(headerPanelHeight + allDayPanelHeight); headerPanelHeight && this._dateTableScrollable.$element().css({ paddingBottom: allDayPanelHeight + headerPanelHeight + "px", marginBottom: -1 * (parseInt(headerPanelHeight, 10) + allDayPanelHeight) + "px" }); headerPanelHeight && this._sidebarScrollable && this._sidebarScrollable.$element().css({ paddingBottom: allDayPanelHeight + headerPanelHeight + "px", marginBottom: -1 * (parseInt(headerPanelHeight, 10) + allDayPanelHeight) + "px" }); this._$allDayTitle && this._$allDayTitle.css("top", headerHeight + headerPanelHeight + "px") }, _makeGroupRows: function(groups) { var tableCreatorStrategy = this._isVerticalGroupedWorkSpace() ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; return tableCreator.makeGroupedTable(tableCreatorStrategy, groups, { groupRowClass: this._getGroupRowClass(), groupHeaderClass: this._getGroupHeaderClass(), groupHeaderContentClass: this._getGroupHeaderContentClass() }, this._getCellCount() || 1, this.option("resourceCellTemplate")) }, _getDateHeaderTemplate: function() { return this.option("dateCellTemplate") }, _renderDateHeader: function() { var $container = this._getDateHeaderContainer(), $headerRow = $("<tr>").addClass(HEADER_ROW_CLASS), count = this._getCellCount(), cellTemplate = this._getDateHeaderTemplate(), repeatCount = this._calculateHeaderCellRepeatCount(), templateCallbacks = []; for (var j = 0; j < repeatCount; j++) { for (var i = 0; i < count; i++) { var text = this._getHeaderText(i), $cell = $("<th>").addClass(this._getHeaderPanelCellClass(i)).attr("title", text); if (cellTemplate && cellTemplate.render) { templateCallbacks.push(cellTemplate.render.bind(cellTemplate, { model: { text: text, date: this._getDateByIndex(i) }, index: j * repeatCount + i, container: getPublicElement($cell) })) } else { $cell.text(text) } $headerRow.append($cell) } } $container.append($headerRow); this._applyCellTemplates(templateCallbacks); return $headerRow }, _getHeaderPanelCellClass: function(i) { var cellClass = HEADER_PANEL_CELL_CLASS + " " + HORIZONTAL_SIZES_CLASS; return this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, i + 1) }, _calculateHeaderCellRepeatCount: function() { return this._groupedStrategy.calculateHeaderCellRepeatCount() }, _renderAllDayPanel: function(index) { var cellCount = this._getCellCount(); if (!this._isVerticalGroupedWorkSpace()) { cellCount *= this._getGroupCount() || 1 } var cellTemplates = this._renderTableBody({ container: this._allDayPanels.length ? getPublicElement(this._allDayTables[index]) : getPublicElement(this._$allDayTable), rowCount: 1, cellCount: cellCount, cellClass: this._getAllDayPanelCellClass.bind(this), rowClass: ALL_DAY_TABLE_ROW_CLASS, cellTemplate: this.option("dataCellTemplate"), getCellData: this._getAllDayCellData.bind(this), groupIndex: index }, true); this._toggleAllDayVisibility(); this._applyCellTemplates(cellTemplates) }, _getAllDayPanelCellClass: function(i, j) { var cellClass = ALL_DAY_TABLE_CELL_CLASS + " " + HORIZONTAL_SIZES_CLASS; return this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, j + 1) }, _getAllDayCellData: function(cell, rowIndex, cellIndex, groupIndex) { var startDate = this._getDateByCellIndexes(rowIndex, cellIndex); startDate = dateUtils.trimTime(startDate); var data = { startDate: startDate, endDate: new Date(startDate.getTime() + DAY_MS), allDay: true }; var groups = this._getCellGroups(groupIndex || this._getGroupIndex(rowIndex, cellIndex)); if (groups.length) { data.groups = {} } for (var i = 0; i < groups.length; i++) { data.groups[groups[i].name] = groups[i].id } return { key: CELL_DATA, value: data } }, _toggleAllDayVisibility: function() { var showAllDayPanel = this.option("showAllDayPanel"); this._$allDayPanel.toggle(showAllDayPanel); this._$allDayTitle && this._$allDayTitle.toggleClass(ALL_DAY_TITLE_HIDDEN_CLASS, !showAllDayPanel); this.$element().toggleClass(WORKSPACE_WITH_ALL_DAY_CLASS, showAllDayPanel); this._changeAllDayVisibility(); showAllDayPanel && this._updateScrollable() }, _changeAllDayVisibility: function() { this.$element().toggleClass(WORKSPACE_WITH_COLLAPSED_ALL_DAY_CLASS, !this.option("allDayExpanded") && this.option("showAllDayPanel")) }, _updateScrollable: function() { this._dateTableScrollable.update(); this._headerScrollable && this._headerScrollable.update(); this._sidebarScrollable && this._sidebarScrollable.update() }, _renderTimePanel: function() { var repeatCount = this._groupedStrategy.calculateTimeCellRepeatCount(); this._renderTableBody({ container: getPublicElement(this._$timePanel), rowCount: this._getTimePanelRowCount() * repeatCount, cellCount: 1, cellClass: this._getTimeCellClass.bind(this), rowClass: TIME_PANEL_ROW_CLASS, cellTemplate: this.option("timeCellTemplate"), getCellText: this._getTimeText.bind(this), groupCount: this._getGroupCount(), allDayElements: this._insertAllDayRowsIntoDateTable() ? this._allDayTitles : void 0 }) }, _getTimePanelRowCount: function() { return this._getCellCountInDay() }, _getCellCountInDay: function() { return Math.ceil(this._calculateDayDuration() / this.option("hoursInterval")) }, _calculateDayDuration: function() { return this.option("endDayHour") - this.option("startDayHour") }, _getTimeCellClass: function(i) { var cellClass = TIME_PANEL_CELL_CLASS + " " + VERTICAL_SIZES_CLASS; return this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, i, i) }, _getTimeText: function(i) { var startViewDate = this._getTimeCellDate(i), index = i % this._getRowCount(); if (index % 2 === 0) { return dateLocalization.format(startViewDate, "shorttime")