UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

775 lines (646 loc) • 24.5 kB
"use strict"; var $ = require("../../core/renderer"), domAdapter = require("../../core/dom_adapter"), eventsEngine = require("../../events/core/events_engine"), domUtils = require("../../core/utils/dom"), focused = require("../widget/selectors").focused, isDefined = require("../../core/utils/type").isDefined, extend = require("../../core/utils/extend").extend, inArray = require("../../core/utils/array").inArray, each = require("../../core/utils/iterator").each, themes = require("../themes"), Editor = require("../editor/editor"), eventUtils = require("../../events/utils"), pointerEvents = require("../../events/pointer"), clickEvent = require("../../events/click"); var TEXTEDITOR_CLASS = "dx-texteditor", TEXTEDITOR_INPUT_CLASS = "dx-texteditor-input", TEXTEDITOR_INPUT_SELECTOR = "." + TEXTEDITOR_INPUT_CLASS, TEXTEDITOR_CONTAINER_CLASS = "dx-texteditor-container", TEXTEDITOR_BUTTONS_CONTAINER_CLASS = "dx-texteditor-buttons-container", TEXTEDITOR_PLACEHOLDER_CLASS = "dx-placeholder", TEXTEDITOR_SHOW_CLEAR_BUTTON_CLASS = "dx-show-clear-button", TEXTEDITOR_ICON_CLASS = "dx-icon", TEXTEDITOR_CLEAR_ICON_CLASS = "dx-icon-clear", TEXTEDITOR_CLEAR_BUTTON_CLASS = "dx-clear-button-area", TEXTEDITOR_EMPTY_INPUT_CLASS = "dx-texteditor-empty", STATE_INVISIBLE_CLASS = "dx-state-invisible"; var EVENTS_LIST = ["KeyDown", "KeyPress", "KeyUp", "Change", "Cut", "Copy", "Paste", "Input"]; var CONTROL_KEYS = ["Tab", "Enter", "Shift", "Control", "Alt", "Escape", "PageUp", "PageDown", "End", "Home", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", // IE9 fallback: "Esc", "Left", "Up", "Right", "Down"]; /** * @name dxTextEditor * @publicName dxTextEditor * @inherits Editor * @hidden */ var TextEditorBase = Editor.inherit({ _supportedKeys: function _supportedKeys() { var stop = function stop(e) { e.stopPropagation(); }; return { space: stop, enter: stop, leftArrow: stop, rightArrow: stop }; }, _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), { /** * @name dxTextEditorOptions.value * @publicName value * @type any * @default "" */ value: "", /** * @name dxTextEditorOptions.spellcheck * @publicName spellcheck * @type boolean * @default false */ spellcheck: false, /** * @name dxTextEditorOptions.showClearButton * @publicName showClearButton * @type boolean * @default false */ showClearButton: false, /** * @name dxTextEditorOptions.valueChangeEvent * @publicName valueChangeEvent * @type string * @default "change" */ valueChangeEvent: "change", /** * @name dxTextEditorOptions.placeholder * @publicName placeholder * @type string * @default "" */ placeholder: "", /** * @name dxTextEditorOptions.inputAttr * @publicName inputAttr * @type object * @default {} */ inputAttr: {}, /** * @name dxTextEditorOptions.onFocusIn * @publicName onFocusIn * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onFocusIn: null, /** * @name dxTextEditorOptions.onFocusOut * @publicName onFocusOut * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onFocusOut: null, /** * @name dxTextEditorOptions.onKeyDown * @publicName onKeyDown * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @default null * @action */ onKeyDown: null, /** * @name dxTextEditorOptions.onKeyPress * @publicName onKeyPress * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onKeyPress: null, /** * @name dxTextEditorOptions.onKeyUp * @publicName onKeyUp * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onKeyUp: null, /** * @name dxTextEditorOptions.onChange * @publicName onChange * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onChange: null, /** * @name dxTextEditorOptions.onInput * @publicName onInput * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onInput: null, /** * @name dxTextEditorOptions.onCut * @publicName onCut * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onCut: null, /** * @name dxTextEditorOptions.onCopy * @publicName onCopy * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onCopy: null, /** * @name dxTextEditorOptions.onPaste * @publicName onPaste * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onPaste: null, /** * @name dxTextEditorOptions.onEnterKey * @publicName onEnterKey * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field5 event:event * @action */ onEnterKey: null, mode: "text", /** * @name dxTextEditorOptions.hoverStateEnabled * @publicName hoverStateEnabled * @type boolean * @default true * @inheritdoc */ hoverStateEnabled: true, /** * @name dxTextEditorOptions.focusStateEnabled * @publicName focusStateEnabled * @type boolean * @default true * @inheritdoc */ focusStateEnabled: true, /** * @name dxTextEditorOptions.text * @publicName text * @type string * @readonly */ text: undefined, valueFormat: function valueFormat(value) { return value; } /** * @name dxTextEditorOptions.name * @publicName name * @type string * @hidden false * @inheritdoc */ }); }, _defaultOptionsRules: function _defaultOptionsRules() { return this.callBase().concat([{ device: function device() { var currentTheme = (themes.current() || "").split(".")[0]; return currentTheme === "android5"; }, options: { validationMessageOffset: { v: -8 } } }]); }, _input: function _input() { return this.$element().find(TEXTEDITOR_INPUT_SELECTOR).first(); }, _inputWrapper: function _inputWrapper() { return this.$element(); }, _buttonsContainer: function _buttonsContainer() { return this._inputWrapper().find("." + TEXTEDITOR_BUTTONS_CONTAINER_CLASS).eq(0); }, _isControlKey: function _isControlKey(key) { return CONTROL_KEYS.indexOf(key) !== -1; }, _initMarkup: function _initMarkup() { this.$element().addClass(TEXTEDITOR_CLASS); this._renderInput(); this._renderInputType(); this._renderPlaceholderMarkup(); this._renderProps(); this.callBase(); this._renderValue(); }, _render: function _render() { this._renderPlaceholder(); this._refreshValueChangeEvent(); this._renderEvents(); this._renderEnterKeyAction(); this._renderEmptinessEvent(); this.callBase(); }, _renderInput: function _renderInput() { $("<div>").addClass(TEXTEDITOR_CONTAINER_CLASS).append(this._createInput()).append($("<div>").addClass(TEXTEDITOR_BUTTONS_CONTAINER_CLASS)).appendTo(this.$element()); }, _createInput: function _createInput() { var $input = $("<input>"); this._applyInputAttributes($input, this.option("inputAttr")); return $input; }, _setSubmitElementName: function _setSubmitElementName(name) { var inputAttrName = this.option("inputAttr.name"); return this.callBase(name || inputAttrName || ""); }, _applyInputAttributes: function _applyInputAttributes($input, customAttributes) { $input.attr("autocomplete", "off").attr(customAttributes).addClass(TEXTEDITOR_INPUT_CLASS).css("minHeight", this.option("height") ? "0" : ""); }, _renderValue: function _renderValue() { this._renderInputValue(); this._renderInputAddons(); }, _renderInputValue: function _renderInputValue(value) { value = value || this.option("value"); var text = this.option("text"), displayValue = this.option("displayValue"), valueFormat = this.option("valueFormat"); if (displayValue !== undefined && value !== null) { text = valueFormat(displayValue); } else if (!isDefined(text)) { text = valueFormat(value); } this.option("text", text); // fallback to empty string is required to support WebKit native date picker in some basic scenarios // can not be covered by QUnit if (this._input().val() !== (isDefined(text) ? text : "")) { this._renderDisplayText(text); } else { this._toggleEmptinessEventHandler(); } }, _renderDisplayText: function _renderDisplayText(text) { this._input().val(text); this._toggleEmptinessEventHandler(); }, _isValueValid: function _isValueValid() { if (this._input().length) { var validity = this._input().get(0).validity; if (validity) { return validity.valid; } } return true; }, _toggleEmptiness: function _toggleEmptiness(isEmpty) { this.$element().toggleClass(TEXTEDITOR_EMPTY_INPUT_CLASS, isEmpty); this._togglePlaceholder(isEmpty); }, _togglePlaceholder: function _togglePlaceholder(isEmpty) { if (!this._$placeholder) { return; } this._$placeholder.toggleClass(STATE_INVISIBLE_CLASS, !isEmpty); }, _renderProps: function _renderProps() { this._toggleReadOnlyState(); this._toggleSpellcheckState(); this._toggleTabIndex(); }, _toggleDisabledState: function _toggleDisabledState(value) { this.callBase.apply(this, arguments); var $input = this._input(); if (value) { $input.attr("disabled", true); } else { $input.removeAttr("disabled"); } }, _toggleTabIndex: function _toggleTabIndex() { var $input = this._input(), disabled = this.option("disabled"), focusStateEnabled = this.option("focusStateEnabled"); if (disabled || !focusStateEnabled) { $input.attr("tabIndex", -1); } else { $input.removeAttr("tabIndex"); } }, _toggleReadOnlyState: function _toggleReadOnlyState() { this._input().prop("readOnly", this._readOnlyPropValue()); this.callBase(); }, _readOnlyPropValue: function _readOnlyPropValue() { return this.option("readOnly"); }, _toggleSpellcheckState: function _toggleSpellcheckState() { this._input().prop("spellcheck", this.option("spellcheck")); }, _renderPlaceholder: function _renderPlaceholder() { this._renderPlaceholderMarkup(); this._attachPlaceholderEvents(); }, _renderPlaceholderMarkup: function _renderPlaceholderMarkup() { if (this._$placeholder) { this._$placeholder.remove(); this._$placeholder = null; } var $input = this._input(), placeholderText = this.option("placeholder"), $placeholder = this._$placeholder = $('<div>').attr("data-dx_placeholder", placeholderText); $placeholder.insertAfter($input); $placeholder.addClass(TEXTEDITOR_PLACEHOLDER_CLASS); }, _attachPlaceholderEvents: function _attachPlaceholderEvents() { var that = this, startEvent = eventUtils.addNamespace(pointerEvents.up, that.NAME); eventsEngine.on(that._$placeholder, startEvent, function () { eventsEngine.trigger(that._input(), "focus"); }); that._toggleEmptinessEventHandler(); }, _placeholder: function _placeholder() { return this._$placeholder || $(); }, _renderInputAddons: function _renderInputAddons() { this._renderClearButton(); }, _renderClearButton: function _renderClearButton() { var clearButtonVisibility = this._clearButtonVisibility(); this.$element().toggleClass(TEXTEDITOR_SHOW_CLEAR_BUTTON_CLASS, clearButtonVisibility); if (clearButtonVisibility) { if (!this._$clearButton || this._$clearButton && !this._$clearButton.closest(this.$element()).length) { this._$clearButton = this._createClearButton(); } this._$clearButton.prependTo(this._buttonsContainer()); } if (this._$clearButton) { this._$clearButton.toggleClass(STATE_INVISIBLE_CLASS, !clearButtonVisibility); } }, _clearButtonVisibility: function _clearButtonVisibility() { return this.option("showClearButton") && !this.option("readOnly"); }, _createClearButton: function _createClearButton() { var $clearButton = $("<span>").addClass(TEXTEDITOR_CLEAR_BUTTON_CLASS).append($("<span>").addClass(TEXTEDITOR_ICON_CLASS).addClass(TEXTEDITOR_CLEAR_ICON_CLASS)); eventsEngine.on($clearButton, eventUtils.addNamespace(pointerEvents.down, this.NAME), function (e) { if (e.pointerType === "mouse") { e.preventDefault(); } }); eventsEngine.on($clearButton, eventUtils.addNamespace(clickEvent.name, this.NAME), this._clearValueHandler.bind(this)); return $clearButton; }, _clearValueHandler: function _clearValueHandler(e) { var $input = this._input(); e.stopPropagation(); this._valueChangeEventHandler(e); this.reset(); !focused($input) && eventsEngine.trigger($input, "focus"); eventsEngine.trigger($input, "input"); }, _renderEvents: function _renderEvents() { var that = this, $input = that._input(); each(EVENTS_LIST, function (_, event) { if (that.hasActionSubscription("on" + event)) { var action = that._createActionByOption("on" + event, { excludeValidators: ["readOnly"] }); eventsEngine.on($input, eventUtils.addNamespace(event.toLowerCase(), that.NAME), function (e) { if (that._disposed) { return; } action({ event: e }); }); } }); }, _refreshEvents: function _refreshEvents() { var that = this, $input = this._input(); each(EVENTS_LIST, function (_, event) { eventsEngine.off($input, eventUtils.addNamespace(event.toLowerCase(), that.NAME)); }); this._renderEvents(); }, _keyPressHandler: function _keyPressHandler() { this.option("text", this._input().val()); }, _renderValueChangeEvent: function _renderValueChangeEvent() { var keyPressEvent = eventUtils.addNamespace(this._renderValueEventName(), this.NAME + "TextChange"), valueChangeEvent = eventUtils.addNamespace(this.option("valueChangeEvent"), this.NAME + "ValueChange"); eventsEngine.on(this._input(), keyPressEvent, this._keyPressHandler.bind(this)); eventsEngine.on(this._input(), valueChangeEvent, this._valueChangeEventHandler.bind(this)); }, _cleanValueChangeEvent: function _cleanValueChangeEvent() { var eventNamespace = this.NAME + "ValueChange", keyPressEvent = eventUtils.addNamespace(this._renderValueEventName(), this.NAME + "TextChange"); eventsEngine.off(this._input(), "." + eventNamespace); eventsEngine.off(this._input(), keyPressEvent); }, _refreshValueChangeEvent: function _refreshValueChangeEvent() { this._cleanValueChangeEvent(); this._renderValueChangeEvent(); }, _renderValueEventName: function _renderValueEventName() { return "input change keypress"; }, _focusTarget: function _focusTarget() { return this._input(); }, _focusClassTarget: function _focusClassTarget() { return this.$element(); }, _toggleFocusClass: function _toggleFocusClass(isFocused, $element) { this.callBase(isFocused, this._focusClassTarget($element)); }, _hasFocusClass: function _hasFocusClass(element) { return this.callBase($(element || this.$element())); }, _renderEmptinessEvent: function _renderEmptinessEvent() { var $input = this._input(); eventsEngine.on($input, "input blur", this._toggleEmptinessEventHandler.bind(this)); }, _toggleEmptinessEventHandler: function _toggleEmptinessEventHandler() { var text = this._input().val(), isEmpty = (text === "" || text === null) && this._isValueValid(); this._toggleEmptiness(isEmpty); }, _valueChangeEventHandler: function _valueChangeEventHandler(e, formattedValue) { this._saveValueChangeEvent(e); this.option("value", arguments.length > 1 ? formattedValue : this._input().val()); this._saveValueChangeEvent(undefined); }, _renderEnterKeyAction: function _renderEnterKeyAction() { this._enterKeyAction = this._createActionByOption("onEnterKey", { excludeValidators: ["readOnly"] }); eventsEngine.off(this._input(), "keyup.onEnterKey.dxTextEditor"); eventsEngine.on(this._input(), "keyup.onEnterKey.dxTextEditor", this._enterKeyHandlerUp.bind(this)); }, _enterKeyHandlerUp: function _enterKeyHandlerUp(e) { if (this._disposed) { return; } if (e.which === 13) { this._enterKeyAction({ event: e }); } }, _updateValue: function _updateValue() { this.option("text", undefined); this._renderValue(); }, _dispose: function _dispose() { this._enterKeyAction = undefined; this.callBase(); }, _getSubmitElement: function _getSubmitElement() { return this._input(); }, _optionChanged: function _optionChanged(args) { var name = args.name; if (inArray(name.replace("on", ""), EVENTS_LIST) > -1) { this._refreshEvents(); return; } switch (name) { case "valueChangeEvent": this._refreshValueChangeEvent(); this._refreshFocusEvent(); this._refreshEvents(); break; case "onValueChanged": this._createValueChangeAction(); break; case "readOnly": this.callBase(args); this._renderInputAddons(); break; case "focusStateEnabled": this.callBase(args); this._toggleTabIndex(); break; case "spellcheck": this._toggleSpellcheckState(); break; case "mode": this._renderInputType(); break; case "onEnterKey": this._renderEnterKeyAction(); break; case "placeholder": this._renderPlaceholder(); break; case "showClearButton": this._renderInputAddons(); break; case "text": break; case "value": this._updateValue(); this.callBase(args); break; case "inputAttr": this._applyInputAttributes(this._input(), args.value); break; case "valueFormat": this._invalidate(); break; default: this.callBase(args); } }, _renderInputType: function _renderInputType() { // B218621, B231875 this._setInputType(this.option("mode")); }, _setInputType: function _setInputType(type) { var input = this._input(); if (type === "search") { type = "text"; } try { input.prop("type", type); } catch (e) { input.prop("type", "text"); } }, /** * @name dxTextEditorMethods.focus * @publicName focus() */ focus: function focus() { eventsEngine.trigger(this._input(), "focus"); }, /** * @name dxTextEditorMethods.blur * @publicName blur() */ blur: function blur() { if (this._input().is(domAdapter.getActiveElement())) { domUtils.resetActiveElement(); } }, reset: function reset() { this.option("value", ""); }, on: function on(eventName, eventHandler) { var result = this.callBase(eventName, eventHandler), event = eventName.charAt(0).toUpperCase() + eventName.substr(1); if (EVENTS_LIST.indexOf(event) >= 0) { this._refreshEvents(); } return result; } }); module.exports = TextEditorBase;