UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

599 lines (598 loc) • 23 kB
/** * DevExtreme (ui/date_box/ui.date_box.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"), windowUtils = require("../../core/utils/window"), window = windowUtils.getWindow(), registerComponent = require("../../core/component_registrator"), typeUtils = require("../../core/utils/type"), each = require("../../core/utils/iterator").each, compareVersions = require("../../core/utils/version").compare, extend = require("../../core/utils/extend").extend, support = require("../../core/utils/support"), devices = require("../../core/devices"), config = require("../../core/config"), dateUtils = require("../../core/utils/date"), uiDateUtils = require("./ui.date_utils"), dateSerialization = require("../../core/utils/date_serialization"), DropDownEditor = require("../drop_down_editor/ui.drop_down_editor"), dateLocalization = require("../../localization/date"), messageLocalization = require("../../localization/message"), DATEBOX_CLASS = "dx-datebox", DX_AUTO_WIDTH_CLASS = "dx-auto-width", DATEBOX_WRAPPER_CLASS = "dx-datebox-wrapper"; var PICKER_TYPE = { calendar: "calendar", rollers: "rollers", list: "list", "native": "native" }; var TYPE = { date: "date", datetime: "datetime", time: "time" }; var STRATEGY_NAME = { calendar: "Calendar", dateView: "DateView", "native": "Native", calendarWithTime: "CalendarWithTime", list: "List" }; var STRATEGY_CLASSES = { Calendar: require("./ui.date_box.strategy.calendar"), DateView: require("./ui.date_box.strategy.date_view"), Native: require("./ui.date_box.strategy.native"), CalendarWithTime: require("./ui.date_box.strategy.calendar_with_time"), List: require("./ui.date_box.strategy.list") }; var isRealWidthSet = function($element) { var explicitWidth = $element[0].style.width; if (explicitWidth && "auto" !== explicitWidth && "inherit" !== explicitWidth) { return true } return false }; var calculateWidth = function(value, $input, $element) { var IE_ROUNDING_ERROR = 10; var NATIVE_BUTTONS_WIDTH = 48; var $longestValueElement = $("<div>").text(value).css({ fontStyle: $input.css("fontStyle"), fontVariant: $input.css("fontVariant"), fontWeight: $input.css("fontWeight"), fontSize: $input.css("fontSize"), fontFamily: $input.css("fontFamily"), letterSpacing: $input.css("letterSpacing"), border: $input.css("border"), visibility: "hidden", whiteSpace: "nowrap", position: "absolute", "float": "left" }); $longestValueElement.appendTo($element); var elementWidth = parseFloat(window.getComputedStyle($longestValueElement.get(0)).width), rightPadding = parseFloat(window.getComputedStyle($input.get(0)).paddingRight), leftPadding = parseFloat(window.getComputedStyle($input.get(0)).paddingLeft); var width = elementWidth + rightPadding + leftPadding + IE_ROUNDING_ERROR + ("text" !== $input.prop("type") ? NATIVE_BUTTONS_WIDTH : 0); $longestValueElement.remove(); return width }; var DateBox = DropDownEditor.inherit({ _supportedKeys: function() { return extend(this.callBase(), this._strategy.supportedKeys()) }, _setDeprecatedOptions: function() { this.callBase(); extend(this._deprecatedOptions, { maxZoomLevel: { since: "18.1", alias: "calendarOptions.maxZoomLevel" }, minZoomLevel: { since: "18.1", alias: "calendarOptions.minZoomLevel" } }) }, _getDefaultOptions: function() { return extend(this.callBase(), { type: "date", showAnalogClock: true, value: null, dateSerializationFormat: void 0, min: void 0, max: void 0, displayFormat: null, interval: 30, disabledDates: null, maxZoomLevel: "month", minZoomLevel: "century", pickerType: PICKER_TYPE.calendar, invalidDateMessage: messageLocalization.format("dxDateBox-validation-datetime"), dateOutOfRangeMessage: messageLocalization.format("validation-range"), applyButtonText: messageLocalization.format("Done"), adaptivityEnabled: false, calendarOptions: {} }) }, _defaultOptionsRules: function() { return this.callBase().concat([{ device: { platform: "ios" }, options: { showPopupTitle: true } }, { device: { platform: "android" }, options: { buttonsLocation: "bottom after" } }, { device: function() { var realDevice = devices.real(), platform = realDevice.platform; return "ios" === platform || "android" === platform }, options: { pickerType: PICKER_TYPE.native } }, { device: function(_device) { return "win" === _device.platform && _device.version && 8 === _device.version[0] }, options: { buttonsLocation: "bottom after" } }, { device: function(_device2) { return "win" === _device2.platform && _device2.version && 10 === _device2.version[0] }, options: { buttonsLocation: "bottom center" } }, { device: function(currentDevice) { var realDevice = devices.real(), platform = realDevice.platform, version = realDevice.version, isPhone = realDevice.phone; return "generic" === platform && "desktop" !== currentDevice.deviceType || "win" === platform && isPhone || "android" === platform && compareVersions(version, [4, 4]) < 0 }, options: { pickerType: PICKER_TYPE.rollers } }, { device: { platform: "generic", deviceType: "desktop" }, options: { buttonsLocation: "bottom after" } }]) }, _initOptions: function(options) { this._userOptions = extend({}, options); this.callBase(options); this._updatePickerOptions() }, _updatePickerOptions: function() { var pickerType = this.option("pickerType"); var type = this.option("type"); if (pickerType === PICKER_TYPE.list && (type === TYPE.datetime || type === TYPE.date)) { pickerType = PICKER_TYPE.calendar } if (type === TYPE.time && pickerType === PICKER_TYPE.calendar) { pickerType = PICKER_TYPE.list } this.option("showDropDownButton", "generic" !== devices.real().platform || pickerType !== PICKER_TYPE.native); this._pickerType = pickerType }, _init: function() { this._initStrategy(); this.option(extend({}, this._strategy.getDefaultOptions(), this._userOptions)); delete this._userOptions; this.callBase() }, _toLowerCaseFirstLetter: function(string) { return string.charAt(0).toLowerCase() + string.substr(1) }, _initStrategy: function() { var strategyName = this._getStrategyName(this._getFormatType()), strategy = STRATEGY_CLASSES[strategyName]; if (!(this._strategy && this._strategy.NAME === strategyName)) { this._strategy = new strategy(this) } }, _getFormatType: function() { var currentType = this.option("type"); var isTime = /h|m|s/g.test(currentType), isDate = /d|M|Y/g.test(currentType); var type = ""; if (isDate) { type += TYPE.date } if (isTime) { type += TYPE.time } return type }, _getStrategyName: function(type) { var pickerType = this._pickerType; if (pickerType === PICKER_TYPE.rollers) { return STRATEGY_NAME.dateView } else { if (pickerType === PICKER_TYPE.native) { return STRATEGY_NAME.native } } if (type === TYPE.date) { return STRATEGY_NAME.calendar } if (type === TYPE.datetime) { return STRATEGY_NAME.calendarWithTime } return STRATEGY_NAME.list }, _initMarkup: function() { this.$element().addClass(DATEBOX_CLASS); this._renderSubmitElement(); this.callBase(); this._refreshFormatClass(); this._refreshPickerTypeClass(); this._strategy.renderInputMinMax(this._input()) }, _render: function() { this.callBase(); this._updateSize() }, _renderDimensions: function() { this.callBase(); this.$element().toggleClass(DX_AUTO_WIDTH_CLASS, !this.option("width")) }, _refreshFormatClass: function() { var $element = this.$element(); each(TYPE, function(_, item) { $element.removeClass(DATEBOX_CLASS + "-" + item) }); $element.addClass(DATEBOX_CLASS + "-" + this.option("type")) }, _refreshPickerTypeClass: function() { var $element = this.$element(); each(PICKER_TYPE, function(_, item) { $element.removeClass(DATEBOX_CLASS + "-" + item) }); $element.addClass(DATEBOX_CLASS + "-" + this._pickerType) }, _renderSubmitElement: function() { this._$submitElement = $("<input>").attr("type", "hidden").appendTo(this.$element()) }, _getSubmitElement: function() { return this._$submitElement }, _updateSize: function() { var $element = this.$element(), widthOption = this.option("width"), isWidthSet = typeUtils.isDefined(widthOption) || isRealWidthSet($element) && !this._isSizeUpdatable, pickerType = this._pickerType, shouldCalculateWidth = pickerType !== PICKER_TYPE.rollers && "generic" === devices.current().platform; if (!windowUtils.hasWindow() || isWidthSet || !(shouldCalculateWidth && $element.is(":visible"))) { return } var $input = this._input(), format = this._strategy.getDisplayFormat(this.option("displayFormat")), longestValue = dateLocalization.format(uiDateUtils.getLongestDate(format, dateLocalization.getMonthNames(), dateLocalization.getDayNames()), format); $element.width(calculateWidth(longestValue, $input, this.$element())); this._isSizeUpdatable = true }, _attachChildKeyboardEvents: function() { this._strategy.attachKeyboardEvents(this._keyboardProcessor) }, _renderPopup: function() { this.callBase(); this._popup._wrapper().addClass(DATEBOX_WRAPPER_CLASS); this._renderPopupWrapper() }, _popupConfig: function() { var popupConfig = this.callBase(); return extend(this._strategy.popupConfig(popupConfig), { title: this._getPopupTitle(), dragEnabled: false }) }, _renderPopupWrapper: function() { if (!this._popup) { return } var $element = this.$element(); var classPostfixes = extend({}, TYPE, PICKER_TYPE); each(classPostfixes, function(_, item) { $element.removeClass(DATEBOX_WRAPPER_CLASS + "-" + item) }.bind(this)); this._popup._wrapper().addClass(DATEBOX_WRAPPER_CLASS + "-" + this.option("type")).addClass(DATEBOX_WRAPPER_CLASS + "-" + this._pickerType) }, _renderPopupContent: function() { this.callBase(); this._strategy.renderPopupContent() }, _getFirstPopupElement: function() { return this._strategy.getFirstPopupElement() || this.callBase() }, _getLastPopupElement: function() { return this._strategy.getLastPopupElement() || this.callBase() }, _popupShowingHandler: function() { this.callBase(); this._strategy.popupShowingHandler() }, _popupHiddenHandler: function() { this.callBase(); this._strategy.popupHiddenHandler() }, _visibilityChanged: function(visible) { if (visible) { this._updateSize() } }, _clearValueHandler: function(e) { this.option("text", ""); this.callBase(e) }, _readOnlyPropValue: function() { return this.callBase() || this._pickerType === PICKER_TYPE.rollers }, _clearButtonVisibility: function() { return this.callBase() && !this._isNativeType() }, _renderValue: function() { var value = this.dateOption("value"), dateSerializationFormat = this.option("dateSerializationFormat"); this.option("text", this._getDisplayedText(value)); var submitFormat = uiDateUtils.SUBMIT_FORMATS_MAP[this.option("type")]; var submitValue = dateSerializationFormat ? dateSerialization.serializeDate(value, dateSerializationFormat) : uiDateUtils.toStandardDateFormat(value, submitFormat); this._$submitElement.val(submitValue); this._strategy.renderValue(); this.callBase() }, _getDisplayedText: function(value) { var displayedText, mode = this.option("mode"); if ("text" === mode) { var displayFormat = this._strategy.getDisplayFormat(this.option("displayFormat")); displayedText = dateLocalization.format(value, displayFormat) } else { var format = this._getFormatByMode(mode); if (format) { displayedText = dateLocalization.format(value, format) } else { displayedText = uiDateUtils.toStandardDateFormat(value, mode) } } return displayedText }, _getFormatByMode: function(mode) { return support.inputType(mode) ? null : uiDateUtils.FORMATS_MAP[mode] }, _valueChangeEventHandler: function(e) { var text = this.option("text"), parsedDate = this._getParsedDate(text), value = this.dateOption("value") || this._getDateByDefault(), type = this.option("type"), newValue = uiDateUtils.mergeDates(value, parsedDate, type), date = parsedDate && "time" === type ? newValue : parsedDate; if (this._validateValue(date)) { var displayedText = this._getDisplayedText(newValue); if (value && newValue && value.getTime() === newValue.getTime() && displayedText !== text) { this._renderValue() } else { this.dateValue(newValue, e) } } this.validationRequest.fire({ value: newValue, editor: this }) }, _getDateByDefault: function() { return this._strategy.useCurrentDateByDefault() && new Date }, _getParsedDate: function(text) { var displayFormat = this._strategy.getDisplayFormat(this.option("displayFormat")); var parsedText = this._strategy.getParsedText(text, displayFormat); return typeUtils.isDefined(parsedText) ? parsedText : void 0 }, _validateValue: function(value) { var text = this.option("text"), hasText = !!text && null !== value, isDate = !!value && typeUtils.isDate(value) && !isNaN(value.getTime()), isDateInRange = isDate && dateUtils.dateInRange(value, this.dateOption("min"), this.dateOption("max"), this.option("type")), isValid = !hasText || !hasText && !value || isDateInRange, validationMessage = ""; if (!isDate) { validationMessage = this.option("invalidDateMessage") } else { if (!isDateInRange) { validationMessage = this.option("dateOutOfRangeMessage") } } this.option({ isValid: isValid, validationError: isValid ? null : { editorSpecific: true, message: validationMessage } }); return isValid }, _isValueChanged: function(newValue) { var oldValue = this.dateOption("value"), oldTime = oldValue && oldValue.getTime(), newTime = newValue && newValue.getTime(); return oldTime !== newTime }, _renderProps: function() { this.callBase(); this._input().attr("autocomplete", "off") }, _renderOpenedState: function() { if (!this._isNativeType()) { this.callBase() } if (this._strategy.isAdaptivityChanged()) { this._refreshStrategy() } this._strategy.renderOpenedState() }, _getPopupTitle: function() { var placeholder = this.option("placeholder"); if (placeholder) { return placeholder } var type = this.option("type"); if (type === TYPE.time) { return messageLocalization.format("dxDateBox-simulatedDataPickerTitleTime") } if (type === TYPE.date || type === TYPE.datetime) { return messageLocalization.format("dxDateBox-simulatedDataPickerTitleDate") } return "" }, _renderPlaceholder: function() { this._popup && this._popup.option("title", this._getPopupTitle()); this.callBase() }, _refreshStrategy: function() { this._strategy.dispose(); this._initStrategy(); this.option(this._strategy.getDefaultOptions()); this._refresh() }, _applyButtonHandler: function() { this.dateValue(this._strategy.getValue()); this.callBase() }, _dispose: function() { this._strategy && this._strategy.dispose(); this.callBase() }, _isNativeType: function() { return this._pickerType === PICKER_TYPE.native }, _optionChanged: function(args) { switch (args.name) { case "showClearButton": this.callBase.apply(this, arguments); this._updateSize(); break; case "pickerType": this._updatePickerOptions({ pickerType: args.value }); this._refreshStrategy(); this._refreshPickerTypeClass(); this._invalidate(); break; case "type": this._updatePickerOptions({ format: args.value }); this._refreshStrategy(); this._refreshFormatClass(); this._renderPopupWrapper(); this._updateSize(); break; case "placeholder": this._renderPlaceholder(); break; case "min": case "max": this._validateValue(this.dateOption("value")); this._invalidate(); break; case "dateSerializationFormat": case "readOnly": case "interval": case "disabledDates": case "calendarOptions": case "minZoomLevel": case "maxZoomLevel": this._invalidate(); break; case "displayFormat": this._updateValue(); break; case "formatWidthCalculator": break; case "closeOnValueChange": var applyValueMode = args.value ? "instantly" : "useButtons"; this.option("applyValueMode", applyValueMode); break; case "applyValueMode": this._suppressDeprecatedWarnings(); this.option("closeOnValueChange", "instantly" === args.value); this._resumeDeprecatedWarnings(); this.callBase.apply(this, arguments); break; case "text": this._strategy.textChangedHandler(args.value); this.callBase.apply(this, arguments); break; case "isValid": this.callBase.apply(this, arguments); this._updateSize(); break; case "value": this._validateValue(this.dateOption("value")); this.callBase.apply(this, arguments); break; case "showDropDownButton": case "invalidDateMessage": case "dateOutOfRangeMessage": case "adaptivityEnabled": case "showAnalogClock": break; default: this.callBase.apply(this, arguments) } }, _getSerializationFormat: function() { var value = this.option("value"); if (this.option("dateSerializationFormat") && config().forceIsoDateParsing) { return this.option("dateSerializationFormat") } if (typeUtils.isNumeric(value)) { return "number" } if (!typeUtils.isString(value)) { return } return dateSerialization.getDateSerializationFormat(value) }, dateValue: function(value, dxEvent) { if (this._isValueChanged(value) && dxEvent) { this._saveValueChangeEvent(dxEvent) } return this.dateOption("value", value) }, dateOption: function(optionName, value) { if (1 === arguments.length) { return dateSerialization.deserializeDate(this.option(optionName)) } var serializationFormat = this._getSerializationFormat(); this.option(optionName, dateSerialization.serializeDate(value, serializationFormat)) }, reset: function() { this.callBase(); this._updateValue() } }); registerComponent("dxDateBox", DateBox); module.exports = DateBox;