UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

687 lines (543 loc) • 24.3 kB
"use strict"; var noop = require("../../core/utils/common").noop, Class = require("../../core/class"), extend = require("../../core/utils/extend").extend, errors = require("../widget/ui.errors"), dateUtils = require("../../core/utils/date"), isNumeric = require("../../core/utils/type").isNumeric, themes = require("../themes"); var toMs = dateUtils.dateToMilliseconds; var abstract = Class.abstract; var APPOINTMENT_MIN_SIZE = 2, COMPACT_APPOINTMENT_DEFAULT_SIZE = 15, APPOINTMENT_DEFAULT_HEIGHT = 20, APPOINTMENT_DEFAULT_WIDTH = 40, COMPACT_THEME_APPOINTMENT_DEFAULT_HEIGHT = 18, COMPACT_THEME_APPOINTMENT_DEFAULT_OFFSET = 22, COMPACT_APPOINTMENT_DEFAULT_OFFSET = 3; var BaseRenderingStrategy = Class.inherit({ ctor: function ctor(instance) { this.instance = instance; }, getAppointmentMinSize: function getAppointmentMinSize() { return APPOINTMENT_MIN_SIZE; }, keepAppointmentSettings: function keepAppointmentSettings() { return false; }, getDeltaTime: abstract, getAppointmentGeometry: function getAppointmentGeometry(coordinates) { return coordinates; }, needCorrectAppointmentDates: function needCorrectAppointmentDates() { return true; }, getDirection: function getDirection() { return "horizontal"; }, createTaskPositionMap: function createTaskPositionMap(items) { delete this._maxAppointmentCountPerCell; var length = items && items.length; if (!length) return; this._defaultWidth = this.instance._cellWidth; this._defaultHeight = this.instance._cellHeight; this._allDayHeight = this.instance._allDayCellHeight; var map = []; for (var i = 0; i < length; i++) { var coordinates = this._getItemPosition(items[i]); if (this._isRtl()) { coordinates = this._correctRtlCoordinates(coordinates); } map.push(coordinates); } var positionArray = this._getSortedPositions(map), resultPositions = this._getResultPositions(positionArray); return this._getExtendedPositionMap(map, resultPositions); }, _getDeltaWidth: function _getDeltaWidth(args, initialSize) { var cellWidth = this._defaultWidth || this.getAppointmentMinSize(), initialWidth = initialSize.width; return Math.round((args.width - initialWidth) / cellWidth); }, _correctRtlCoordinates: function _correctRtlCoordinates(coordinates) { var width = coordinates[0].width || this._getAppointmentMaxWidth(); if (!coordinates[0].appointmentReduced) { coordinates[0].left -= width; } this._correctRtlCoordinatesParts(coordinates, width); return coordinates; }, _correctRtlCoordinatesParts: noop, _getAppointmentMaxWidth: function _getAppointmentMaxWidth() { return this._defaultWidth; }, _getItemPosition: function _getItemPosition(item) { var position = this._getAppointmentCoordinates(item), allDay = this.isAllDay(item), result = [], startDate = new Date(this.instance.fire("getField", "startDate", item)), isRecurring = !!item.recurrenceRule; for (var j = 0; j < position.length; j++) { var height = this.calculateAppointmentHeight(item, position[j]), width = this.calculateAppointmentWidth(item, position[j], isRecurring), resultWidth = width, appointmentReduced = null, multiWeekAppointmentParts = [], initialRowIndex = position[j].rowIndex, initialCellIndex = position[j].cellIndex; if (this._needVerifyItemSize() || allDay) { var currentMaxAllowedPosition = position[j].hMax; if (this.isAppointmentGreaterThan(currentMaxAllowedPosition, { left: position[j].left, width: width })) { appointmentReduced = "head"; initialRowIndex = position[j].rowIndex; initialCellIndex = position[j].cellIndex; resultWidth = this._reduceMultiWeekAppointment(width, { left: position[j].left, right: currentMaxAllowedPosition }); multiWeekAppointmentParts = this._getAppointmentParts({ sourceAppointmentWidth: width, reducedWidth: resultWidth, height: height }, position[j], startDate); if (this._isRtl()) { position[j].left = currentMaxAllowedPosition; } } } extend(position[j], { height: height, width: resultWidth, allDay: allDay, rowIndex: initialRowIndex, cellIndex: initialCellIndex, appointmentReduced: appointmentReduced }); result = this._getAppointmentPartsPosition(multiWeekAppointmentParts, position[j], result); } return result; }, _getAppointmentPartsPosition: function _getAppointmentPartsPosition(appointmentParts, position, result) { if (appointmentParts.length) { appointmentParts.unshift(position); result = result.concat(appointmentParts); } else { result.push(position); } return result; }, _getAppointmentCoordinates: function _getAppointmentCoordinates(itemData) { var coordinates = [{ top: 0, left: 0 }]; this.instance.fire("needCoordinates", { startDate: this._startDate(itemData), originalStartDate: this._startDate(itemData, true), appointmentData: itemData, callback: function callback(value) { coordinates = value; } }); return coordinates; }, _isRtl: function _isRtl() { return this.instance.option("rtlEnabled"); }, _getAppointmentParts: function _getAppointmentParts() { return []; }, _getCompactAppointmentParts: function _getCompactAppointmentParts(appointmentWidth) { var cellWidth = this._defaultWidth || this.getAppointmentMinSize(); return Math.round(appointmentWidth / cellWidth); }, _reduceMultiWeekAppointment: function _reduceMultiWeekAppointment(sourceAppointmentWidth, bound) { if (this._isRtl()) { sourceAppointmentWidth = Math.floor(bound.left - bound.right); } else { sourceAppointmentWidth = bound.right - Math.floor(bound.left); } return sourceAppointmentWidth; }, calculateAppointmentHeight: function calculateAppointmentHeight() { return 0; }, calculateAppointmentWidth: function calculateAppointmentWidth() { return 0; }, isAppointmentGreaterThan: function isAppointmentGreaterThan(etalon, comparisonParameters) { var result = comparisonParameters.left + comparisonParameters.width - etalon; if (this._isRtl()) { result = etalon + comparisonParameters.width - comparisonParameters.left; } return result > this._defaultWidth / 2; }, isAllDay: function isAllDay() { return false; }, _getSortedPositions: function _getSortedPositions(arr) { var result = [], // unstable sorting fix __tmpIndex = 0; for (var i = 0, arrLength = arr.length; i < arrLength; i++) { for (var j = 0, itemLength = arr[i].length; j < itemLength; j++) { var item = arr[i][j]; var start = { i: i, j: j, top: item.top, left: item.left, isStart: true, allDay: item.allDay, __tmpIndex: __tmpIndex }; __tmpIndex++; var end = { i: i, j: j, top: item.top + item.height, left: item.left + item.width, isStart: false, allDay: item.allDay, __tmpIndex: __tmpIndex }; result.push(start, end); __tmpIndex++; } } result.sort(function (a, b) { return this._sortCondition(a, b); }.bind(this)); return result; }, _fixUnstableSorting: function _fixUnstableSorting(comparisonResult, a, b) { if (comparisonResult === 0) { if (a.__tmpIndex < b.__tmpIndex) return -1; if (a.__tmpIndex > b.__tmpIndex) return 1; } return comparisonResult; }, _sortCondition: abstract, _rowCondition: function _rowCondition(a, b) { var columnCondition = this._normalizeCondition(a.left, b.left), rowCondition = this._normalizeCondition(a.top, b.top); return columnCondition ? columnCondition : rowCondition ? rowCondition : a.isStart - b.isStart; }, _columnCondition: function _columnCondition(a, b) { var columnCondition = this._normalizeCondition(a.left, b.left), rowCondition = this._normalizeCondition(a.top, b.top); return rowCondition ? rowCondition : columnCondition ? columnCondition : a.isStart - b.isStart; }, _normalizeCondition: function _normalizeCondition(first, second) { // NOTE: ie & ff pixels var result = first - second; return Math.abs(result) > 1.001 ? result : 0; }, _getResultPositions: function _getResultPositions(sortedArray) { var stack = [], indexes = [], result = [], intersectPositions = [], intersectPositionCount = 0, sortedIndex = 0, position; for (var i = 0; i < sortedArray.length; i++) { var current = sortedArray[i], j; if (current.isStart) { position = undefined; for (j = 0; j < indexes.length; j++) { if (!indexes[j]) { position = j; indexes[j] = true; break; } } if (position === undefined) { position = indexes.length; indexes.push(true); for (j = 0; j < stack.length; j++) { stack[j].count++; } } stack.push({ index: position, count: indexes.length, i: current.i, j: current.j, sortedIndex: this._skipSortedIndex(position) ? null : sortedIndex++ }); if (intersectPositionCount < indexes.length) { intersectPositionCount = indexes.length; } } else { var removeIndex = this._findIndexByKey(stack, "i", "j", current.i, current.j), resultItem = stack[removeIndex]; stack.splice(removeIndex, 1); indexes[resultItem.index] = false; intersectPositions.push(resultItem); if (!stack.length) { indexes = []; for (var k = 0; k < intersectPositions.length; k++) { intersectPositions[k].count = intersectPositionCount; } intersectPositions = []; intersectPositionCount = 0; } result.push(resultItem); } } return result.sort(function (a, b) { var columnCondition = a.j - b.j, rowCondition = a.i - b.i; return rowCondition ? rowCondition : columnCondition; }); }, _skipSortedIndex: function _skipSortedIndex(index) { return this.instance.fire("getMaxAppointmentsPerCell") && index > this._getMaxAppointmentCountPerCell() - 1; }, _findIndexByKey: function _findIndexByKey(arr, iKey, jKey, iValue, jValue) { var result = 0; for (var i = 0, len = arr.length; i < len; i++) { if (arr[i][iKey] === iValue && arr[i][jKey] === jValue) { result = i; break; } } return result; }, _getExtendedPositionMap: function _getExtendedPositionMap(map, positions) { var positionCounter = 0, result = []; for (var i = 0, mapLength = map.length; i < mapLength; i++) { var resultString = []; for (var j = 0, itemLength = map[i].length; j < itemLength; j++) { map[i][j].index = positions[positionCounter].index; map[i][j].sortedIndex = positions[positionCounter].sortedIndex; map[i][j].count = positions[positionCounter++].count; resultString.push(map[i][j]); this._checkLongCompactAppointment(map[i][j], resultString); } result.push(resultString); } return result; }, _checkLongCompactAppointment: noop, _splitLongCompactAppointment: function _splitLongCompactAppointment(item, result) { var appointmentCountPerCell = this._getMaxAppointmentCountPerCell(); var compactCount = 0; if (appointmentCountPerCell !== undefined && item.index > appointmentCountPerCell - 1) { item.isCompact = true; compactCount = this._getCompactAppointmentParts(item.width); for (var k = 1; k < compactCount; k++) { var compactPart = extend(true, {}, item); compactPart.left = this._getCompactLeftCoordinate(item.left, k); compactPart.cellIndex = compactPart.cellIndex + k; compactPart.sortedIndex = null; result.push(compactPart); } } return result; }, _startDate: function _startDate(appointment, skipNormalize, position) { var startDate = position && position.startDate, viewStartDate = this.instance._getStartDate(appointment, skipNormalize), text = this.instance.fire("getField", "text", appointment); if (startDate && viewStartDate > startDate || !startDate) { startDate = viewStartDate; } if (isNaN(startDate.getTime())) { throw errors.Error("E1032", text); } return startDate; }, _endDate: function _endDate(appointment, position, isRecurring) { var endDate = this.instance._getEndDate(appointment), realStartDate = this._startDate(appointment, true), viewStartDate = this._startDate(appointment, false, position); endDate = this._checkWrongEndDate(appointment, realStartDate, endDate); if (viewStartDate.getTime() >= endDate.getTime() || isRecurring) { var recurrencePartStartDate = position ? position.startDate : realStartDate, fullDuration = endDate.getTime() - realStartDate.getTime(); fullDuration = this._adjustDurationByDaylightDiff(fullDuration, realStartDate, endDate); endDate = new Date((viewStartDate.getTime() >= recurrencePartStartDate.getTime() ? recurrencePartStartDate.getTime() : viewStartDate.getTime()) + fullDuration); if (!dateUtils.sameDate(realStartDate, endDate) && recurrencePartStartDate.getTime() < viewStartDate.getTime()) { var headDuration = dateUtils.trimTime(endDate).getTime() - recurrencePartStartDate.getTime(), tailDuration = fullDuration - headDuration || fullDuration; endDate = new Date(dateUtils.trimTime(viewStartDate).getTime() + tailDuration); } } if (!this.isAllDay(appointment)) { var viewEndDate = dateUtils.roundToHour(this.instance.fire("getEndViewDate")); if (endDate > viewEndDate) { endDate = viewEndDate; } } return endDate; }, _adjustDurationByDaylightDiff: function _adjustDurationByDaylightDiff(duration, startDate, endDate) { var daylightDiff = this.instance.fire("getDaylightOffset", startDate, endDate); if (daylightDiff !== 0) { duration += daylightDiff * toMs("minute"); } return duration; }, _checkWrongEndDate: function _checkWrongEndDate(appointment, startDate, endDate) { if (!endDate || startDate.getTime() >= endDate.getTime()) { endDate = new Date(startDate.getTime() + this.instance.getAppointmentDurationInMinutes() * 60000); this.instance.fire("setField", "endDate", appointment, endDate); } return endDate; }, _getAppointmentDurationInMs: function _getAppointmentDurationInMs(startDate, endDate, allDay) { var result; this.instance.fire("getAppointmentDurationInMs", { startDate: startDate, endDate: endDate, allDay: allDay, callback: function callback(duration) { result = duration; } }); return result; }, _getMaxNeighborAppointmentCount: function _getMaxNeighborAppointmentCount() { var overlappingMode = this.instance.fire("getMaxAppointmentsPerCell"); if (!overlappingMode) { var outerAppointmentWidth = this.getCompactAppointmentDefaultSize() + this.getCompactAppointmentDefaultOffset(); return Math.floor(this.getCompactAppointmentGroupMaxWidth() / outerAppointmentWidth); } else { return 0; } }, _markAppointmentAsVirtual: function _markAppointmentAsVirtual(coordinates, isAllDay) { var countFullWidthAppointmentInCell = this._getMaxAppointmentCountPerCell(); if (coordinates.count - countFullWidthAppointmentInCell > this._getMaxNeighborAppointmentCount()) { coordinates.virtual = { top: coordinates.top, left: coordinates.left, index: coordinates.groupIndex + "-" + coordinates.rowIndex + "-" + coordinates.cellIndex, isAllDay: isAllDay }; } }, getCompactAppointmentGroupMaxWidth: function getCompactAppointmentGroupMaxWidth() { var widthInPercents = 75; return widthInPercents * this.getDefaultCellWidth() / 100; }, getDefaultCellWidth: function getDefaultCellWidth() { return this._defaultWidth; }, getCompactAppointmentDefaultSize: function getCompactAppointmentDefaultSize() { return COMPACT_APPOINTMENT_DEFAULT_SIZE; }, getCompactAppointmentDefaultOffset: function getCompactAppointmentDefaultOffset() { return COMPACT_APPOINTMENT_DEFAULT_OFFSET; }, getAppointmentDataCalculator: noop, _customizeCoordinates: function _customizeCoordinates(coordinates, height, appointmentCountPerCell, topOffset, isAllDay) { var index = coordinates.index, appointmentHeight = height / appointmentCountPerCell, appointmentTop = coordinates.top + index * appointmentHeight, top = appointmentTop + topOffset, width = coordinates.width, left = coordinates.left, compactAppointmentDefaultSize, compactAppointmentDefaultOffset; if (coordinates.isCompact) { compactAppointmentDefaultSize = this.getCompactAppointmentDefaultSize(); compactAppointmentDefaultOffset = this.getCompactAppointmentDefaultOffset(); top = coordinates.top + compactAppointmentDefaultOffset; left = coordinates.left + (index - appointmentCountPerCell) * (compactAppointmentDefaultSize + compactAppointmentDefaultOffset) + compactAppointmentDefaultOffset; appointmentHeight = compactAppointmentDefaultSize; width = compactAppointmentDefaultSize; this._markAppointmentAsVirtual(coordinates, isAllDay); } return { height: appointmentHeight, width: width, top: top, left: left, empty: this._isAppointmentEmpty(height, width) }; }, _isAppointmentEmpty: function _isAppointmentEmpty(height, width) { return height < this._getAppointmentDefaultHeight() || width < this._getAppointmentDefaultWidth(); }, _calculateGeometryConfig: function _calculateGeometryConfig(coordinates) { var overlappingMode = this.instance.fire("getMaxAppointmentsPerCell"), offsets = this._getOffsets(), appointmentDefaultOffset = this._getAppointmentDefaultOffset(); var appointmentCountPerCell = this._getAppointmentCount(overlappingMode, coordinates); var ratio = this._getDefaultRatio(coordinates, appointmentCountPerCell); var maxHeight = this._getMaxHeight(); if (!appointmentCountPerCell) { appointmentCountPerCell = coordinates.count; ratio = (maxHeight - offsets.unlimited) / maxHeight; } var topOffset = (1 - ratio) * maxHeight; if (overlappingMode === "auto" || isNumeric(overlappingMode)) { ratio = 1; maxHeight = maxHeight - appointmentDefaultOffset; topOffset = appointmentDefaultOffset; } return { height: ratio * maxHeight, appointmentCountPerCell: appointmentCountPerCell, offset: topOffset }; }, _getAppointmentCount: noop, _getDefaultRatio: noop, _getOffsets: noop, _getMaxHeight: noop, _needVerifyItemSize: function _needVerifyItemSize() { return false; }, _getMaxAppointmentCountPerCell: function _getMaxAppointmentCountPerCell() { if (!this._maxAppointmentCountPerCell) { var overlappingMode = this.instance.fire("getMaxAppointmentsPerCell"), appointmentCountPerCell; if (!overlappingMode) { appointmentCountPerCell = 2; } if (isNumeric(overlappingMode)) { appointmentCountPerCell = overlappingMode; } if (overlappingMode === "auto") { appointmentCountPerCell = this._getDynamicAppointmentCountPerCell(); } if (overlappingMode === "unlimited") { appointmentCountPerCell = undefined; } this._maxAppointmentCountPerCell = appointmentCountPerCell; } return this._maxAppointmentCountPerCell; }, _getDynamicAppointmentCountPerCell: function _getDynamicAppointmentCountPerCell() { var cellHeight = this.instance.fire("getCellHeight"); return Math.floor((cellHeight - this._getAppointmentDefaultOffset()) / this._getAppointmentDefaultHeight()); }, _isCompactTheme: function _isCompactTheme() { return (themes.current() || "").split(".")[2] === "compact"; }, _getAppointmentDefaultOffset: function _getAppointmentDefaultOffset() { return this._isCompactTheme() ? COMPACT_THEME_APPOINTMENT_DEFAULT_OFFSET : this.instance.option("_appointmentOffset"); }, _getAppointmentDefaultHeight: function _getAppointmentDefaultHeight() { return this._isCompactTheme() ? COMPACT_THEME_APPOINTMENT_DEFAULT_HEIGHT : APPOINTMENT_DEFAULT_HEIGHT; }, _getAppointmentDefaultWidth: function _getAppointmentDefaultWidth() { return APPOINTMENT_DEFAULT_WIDTH; }, _needVerticalGroupBounds: function _needVerticalGroupBounds() { return false; }, _needHorizontalGroupBounds: function _needHorizontalGroupBounds() { return false; } }); module.exports = BaseRenderingStrategy;