summaryrefslogtreecommitdiff
path: root/vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js')
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js535
1 files changed, 0 insertions, 535 deletions
diff --git a/vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js b/vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js
deleted file mode 100644
index 1ba26641..00000000
--- a/vendor/oojs/oojs-ui/src/widgets/TextInputWidget.js
+++ /dev/null
@@ -1,535 +0,0 @@
-/**
- * TextInputWidgets, like HTML text inputs, can be configured with options that customize the
- * size of the field as well as its presentation. In addition, these widgets can be configured
- * with {@link OO.ui.IconElement icons}, {@link OO.ui.IndicatorElement indicators}, an optional
- * validation-pattern (used to determine if an input value is valid or not) and an input filter,
- * which modifies incoming values rather than validating them.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
- *
- * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
- *
- * @example
- * // Example of a text input widget
- * var textInput = new OO.ui.TextInputWidget( {
- * value: 'Text input'
- * } )
- * $( 'body' ).append( textInput.$element );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
- *
- * @class
- * @extends OO.ui.InputWidget
- * @mixins OO.ui.IconElement
- * @mixins OO.ui.IndicatorElement
- * @mixins OO.ui.PendingElement
- * @mixins OO.ui.LabelElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {string} [type='text'] The value of the HTML `type` attribute
- * @cfg {string} [placeholder] Placeholder text
- * @cfg {boolean} [autofocus=false] Use an HTML `autofocus` attribute to
- * instruct the browser to focus this widget.
- * @cfg {boolean} [readOnly=false] Prevent changes to the value of the text input.
- * @cfg {number} [maxLength] Maximum number of characters allowed in the input.
- * @cfg {boolean} [multiline=false] Allow multiple lines of text
- * @cfg {boolean} [autosize=false] Automatically resize the text input to fit its content.
- * Use the #maxRows config to specify a maximum number of displayed rows.
- * @cfg {boolean} [maxRows=10] Maximum number of rows to display when #autosize is set to true.
- * @cfg {string} [labelPosition='after'] The position of the inline label relative to that of
- * the value or placeholder text: `'before'` or `'after'`
- * @cfg {boolean} [required=false] Mark the field as required
- * @cfg {RegExp|Function|string} [validate] Validation pattern: when string, a symbolic name of a
- * pattern defined by the class: 'non-empty' (the value cannot be an empty string) or 'integer'
- * (the value must contain only numbers); when RegExp, a regular expression that must match the
- * value for it to be considered valid; when Function, a function receiving the value as parameter
- * that must return true, or promise resolving to true, for it to be considered valid.
- */
-OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
- // Configuration initialization
- config = $.extend( {
- type: 'text',
- labelPosition: 'after',
- maxRows: 10
- }, config );
-
- // Parent constructor
- OO.ui.TextInputWidget.super.call( this, config );
-
- // Mixin constructors
- OO.ui.IconElement.call( this, config );
- OO.ui.IndicatorElement.call( this, config );
- OO.ui.PendingElement.call( this, config );
- OO.ui.LabelElement.call( this, config );
-
- // Properties
- this.readOnly = false;
- this.multiline = !!config.multiline;
- this.autosize = !!config.autosize;
- this.maxRows = config.maxRows;
- this.validate = null;
-
- // Clone for resizing
- if ( this.autosize ) {
- this.$clone = this.$input
- .clone()
- .insertAfter( this.$input )
- .attr( 'aria-hidden', 'true' )
- .addClass( 'oo-ui-element-hidden' );
- }
-
- this.setValidation( config.validate );
- this.setLabelPosition( config.labelPosition );
-
- // Events
- this.$input.on( {
- keypress: this.onKeyPress.bind( this ),
- blur: this.onBlur.bind( this )
- } );
- this.$input.one( {
- focus: this.onElementAttach.bind( this )
- } );
- this.$icon.on( 'mousedown', this.onIconMouseDown.bind( this ) );
- this.$indicator.on( 'mousedown', this.onIndicatorMouseDown.bind( this ) );
- this.on( 'labelChange', this.updatePosition.bind( this ) );
- this.connect( this, { change: 'onChange' } );
-
- // Initialization
- this.$element
- .addClass( 'oo-ui-textInputWidget' )
- .append( this.$icon, this.$indicator );
- this.setReadOnly( !!config.readOnly );
- if ( config.placeholder ) {
- this.$input.attr( 'placeholder', config.placeholder );
- }
- if ( config.maxLength !== undefined ) {
- this.$input.attr( 'maxlength', config.maxLength );
- }
- if ( config.autofocus ) {
- this.$input.attr( 'autofocus', 'autofocus' );
- }
- if ( config.required ) {
- this.$input.attr( 'required', 'required' );
- this.$input.attr( 'aria-required', 'true' );
- }
- if ( this.label || config.autosize ) {
- this.installParentChangeDetector();
- }
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.TextInputWidget, OO.ui.InputWidget );
-OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IconElement );
-OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IndicatorElement );
-OO.mixinClass( OO.ui.TextInputWidget, OO.ui.PendingElement );
-OO.mixinClass( OO.ui.TextInputWidget, OO.ui.LabelElement );
-
-/* Static properties */
-
-OO.ui.TextInputWidget.static.validationPatterns = {
- 'non-empty': /.+/,
- integer: /^\d+$/
-};
-
-/* Events */
-
-/**
- * An `enter` event is emitted when the user presses 'enter' inside the text box.
- *
- * Not emitted if the input is multiline.
- *
- * @event enter
- */
-
-/* Methods */
-
-/**
- * Handle icon mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- * @fires icon
- */
-OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
- if ( e.which === 1 ) {
- this.$input[ 0 ].focus();
- return false;
- }
-};
-
-/**
- * Handle indicator mouse down events.
- *
- * @private
- * @param {jQuery.Event} e Mouse down event
- * @fires indicator
- */
-OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
- if ( e.which === 1 ) {
- this.$input[ 0 ].focus();
- return false;
- }
-};
-
-/**
- * Handle key press events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- * @fires enter If enter key is pressed and input is not multiline
- */
-OO.ui.TextInputWidget.prototype.onKeyPress = function ( e ) {
- if ( e.which === OO.ui.Keys.ENTER && !this.multiline ) {
- this.emit( 'enter', e );
- }
-};
-
-/**
- * Handle blur events.
- *
- * @private
- * @param {jQuery.Event} e Blur event
- */
-OO.ui.TextInputWidget.prototype.onBlur = function () {
- this.setValidityFlag();
-};
-
-/**
- * Handle element attach events.
- *
- * @private
- * @param {jQuery.Event} e Element attach event
- */
-OO.ui.TextInputWidget.prototype.onElementAttach = function () {
- // Any previously calculated size is now probably invalid if we reattached elsewhere
- this.valCache = null;
- this.adjustSize();
- this.positionLabel();
-};
-
-/**
- * Handle change events.
- *
- * @param {string} value
- * @private
- */
-OO.ui.TextInputWidget.prototype.onChange = function () {
- this.setValidityFlag();
- this.adjustSize();
-};
-
-/**
- * Check if the input is {@link #readOnly read-only}.
- *
- * @return {boolean}
- */
-OO.ui.TextInputWidget.prototype.isReadOnly = function () {
- return this.readOnly;
-};
-
-/**
- * Set the {@link #readOnly read-only} state of the input.
- *
- * @param {boolean} state Make input read-only
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.setReadOnly = function ( state ) {
- this.readOnly = !!state;
- this.$input.prop( 'readOnly', this.readOnly );
- return this;
-};
-
-/**
- * Support function for making #onElementAttach work across browsers.
- *
- * This whole function could be replaced with one line of code using the DOMNodeInsertedIntoDocument
- * event, but it's not supported by Firefox and allegedly deprecated, so we only use it as fallback.
- *
- * Due to MutationObserver performance woes, #onElementAttach is only somewhat reliably called the
- * first time that the element gets attached to the documented.
- */
-OO.ui.TextInputWidget.prototype.installParentChangeDetector = function () {
- var mutationObserver, onRemove, topmostNode, fakeParentNode,
- MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
- widget = this;
-
- if ( MutationObserver ) {
- // The new way. If only it wasn't so ugly.
-
- if ( this.$element.closest( 'html' ).length ) {
- // Widget is attached already, do nothing. This breaks the functionality of this function when
- // the widget is detached and reattached. Alas, doing this correctly with MutationObserver
- // would require observation of the whole document, which would hurt performance of other,
- // more important code.
- return;
- }
-
- // Find topmost node in the tree
- topmostNode = this.$element[0];
- while ( topmostNode.parentNode ) {
- topmostNode = topmostNode.parentNode;
- }
-
- // We have no way to detect the $element being attached somewhere without observing the entire
- // DOM with subtree modifications, which would hurt performance. So we cheat: we hook to the
- // parent node of $element, and instead detect when $element is removed from it (and thus
- // probably attached somewhere else). If there is no parent, we create a "fake" one. If it
- // doesn't get attached, we end up back here and create the parent.
-
- mutationObserver = new MutationObserver( function ( mutations ) {
- var i, j, removedNodes;
- for ( i = 0; i < mutations.length; i++ ) {
- removedNodes = mutations[ i ].removedNodes;
- for ( j = 0; j < removedNodes.length; j++ ) {
- if ( removedNodes[ j ] === topmostNode ) {
- setTimeout( onRemove, 0 );
- return;
- }
- }
- }
- } );
-
- onRemove = function () {
- // If the node was attached somewhere else, report it
- if ( widget.$element.closest( 'html' ).length ) {
- widget.onElementAttach();
- }
- mutationObserver.disconnect();
- widget.installParentChangeDetector();
- };
-
- // Create a fake parent and observe it
- fakeParentNode = $( '<div>' ).append( this.$element )[0];
- mutationObserver.observe( fakeParentNode, { childList: true } );
- } else {
- // Using the DOMNodeInsertedIntoDocument event is much nicer and less magical, and works for
- // detachment and reattachment, but it's not supported by Firefox and allegedly deprecated.
- this.$element.on( 'DOMNodeInsertedIntoDocument', this.onElementAttach.bind( this ) );
- }
-};
-
-/**
- * Automatically adjust the size of the text input.
- *
- * This only affects #multiline inputs that are {@link #autosize autosized}.
- *
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.adjustSize = function () {
- var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError, idealHeight;
-
- if ( this.multiline && this.autosize && this.$input.val() !== this.valCache ) {
- this.$clone
- .val( this.$input.val() )
- .attr( 'rows', '' )
- // Set inline height property to 0 to measure scroll height
- .css( 'height', 0 );
-
- this.$clone.removeClass( 'oo-ui-element-hidden' );
-
- this.valCache = this.$input.val();
-
- scrollHeight = this.$clone[ 0 ].scrollHeight;
-
- // Remove inline height property to measure natural heights
- this.$clone.css( 'height', '' );
- innerHeight = this.$clone.innerHeight();
- outerHeight = this.$clone.outerHeight();
-
- // Measure max rows height
- this.$clone
- .attr( 'rows', this.maxRows )
- .css( 'height', 'auto' )
- .val( '' );
- maxInnerHeight = this.$clone.innerHeight();
-
- // Difference between reported innerHeight and scrollHeight with no scrollbars present
- // Equals 1 on Blink-based browsers and 0 everywhere else
- measurementError = maxInnerHeight - this.$clone[ 0 ].scrollHeight;
- idealHeight = Math.min( maxInnerHeight, scrollHeight + measurementError );
-
- this.$clone.addClass( 'oo-ui-element-hidden' );
-
- // Only apply inline height when expansion beyond natural height is needed
- if ( idealHeight > innerHeight ) {
- // Use the difference between the inner and outer height as a buffer
- this.$input.css( 'height', idealHeight + ( outerHeight - innerHeight ) );
- } else {
- this.$input.css( 'height', '' );
- }
- }
- return this;
-};
-
-/**
- * @inheritdoc
- * @private
- */
-OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
- return config.multiline ? $( '<textarea>' ) : $( '<input type="' + config.type + '" />' );
-};
-
-/**
- * Check if the input supports multiple lines.
- *
- * @return {boolean}
- */
-OO.ui.TextInputWidget.prototype.isMultiline = function () {
- return !!this.multiline;
-};
-
-/**
- * Check if the input automatically adjusts its size.
- *
- * @return {boolean}
- */
-OO.ui.TextInputWidget.prototype.isAutosizing = function () {
- return !!this.autosize;
-};
-
-/**
- * Select the entire text of the input.
- *
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.select = function () {
- this.$input.select();
- return this;
-};
-
-/**
- * Set the validation pattern.
- *
- * The validation pattern is either a regular expression, a function, or the symbolic name of a
- * pattern defined by the class: 'non-empty' (the value cannot be an empty string) or 'integer' (the
- * value must contain only numbers).
- *
- * @param {RegExp|Function|string|null} validate Regular expression, function, or the symbolic name
- * of a pattern (either ‘integer’ or ‘non-empty’) defined by the class.
- */
-OO.ui.TextInputWidget.prototype.setValidation = function ( validate ) {
- if ( validate instanceof RegExp || validate instanceof Function ) {
- this.validate = validate;
- } else {
- this.validate = this.constructor.static.validationPatterns[ validate ] || /.*/;
- }
-};
-
-/**
- * Sets the 'invalid' flag appropriately.
- *
- * @param {boolean} [isValid] Optionally override validation result
- */
-OO.ui.TextInputWidget.prototype.setValidityFlag = function ( isValid ) {
- var widget = this,
- setFlag = function ( valid ) {
- if ( !valid ) {
- widget.$input.attr( 'aria-invalid', 'true' );
- } else {
- widget.$input.removeAttr( 'aria-invalid' );
- }
- widget.setFlags( { invalid: !valid } );
- };
-
- if ( isValid !== undefined ) {
- setFlag( isValid );
- } else {
- this.isValid().done( setFlag );
- }
-};
-
-/**
- * Check if a value is valid.
- *
- * This method returns a promise that resolves with a boolean `true` if the current value is
- * considered valid according to the supplied {@link #validate validation pattern}.
- *
- * @return {jQuery.Promise} A promise that resolves to a boolean `true` if the value is valid.
- */
-OO.ui.TextInputWidget.prototype.isValid = function () {
- if ( this.validate instanceof Function ) {
- var result = this.validate( this.getValue() );
- if ( $.isFunction( result.promise ) ) {
- return result.promise();
- } else {
- return $.Deferred().resolve( !!result ).promise();
- }
- } else {
- return $.Deferred().resolve( !!this.getValue().match( this.validate ) ).promise();
- }
-};
-
-/**
- * Set the position of the inline label relative to that of the value: `‘before’` or `‘after’`.
- *
- * @param {string} labelPosition Label position, 'before' or 'after'
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.setLabelPosition = function ( labelPosition ) {
- this.labelPosition = labelPosition;
- this.updatePosition();
- return this;
-};
-
-/**
- * Deprecated alias of #setLabelPosition
- *
- * @deprecated Use setLabelPosition instead.
- */
-OO.ui.TextInputWidget.prototype.setPosition =
- OO.ui.TextInputWidget.prototype.setLabelPosition;
-
-/**
- * Update the position of the inline label.
- *
- * This method is called by #setLabelPosition, and can also be called on its own if
- * something causes the label to be mispositioned.
- *
- *
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.updatePosition = function () {
- var after = this.labelPosition === 'after';
-
- this.$element
- .toggleClass( 'oo-ui-textInputWidget-labelPosition-after', !!this.label && after )
- .toggleClass( 'oo-ui-textInputWidget-labelPosition-before', !!this.label && !after );
-
- if ( this.label ) {
- this.positionLabel();
- }
-
- return this;
-};
-
-/**
- * Position the label by setting the correct padding on the input.
- *
- * @private
- * @chainable
- */
-OO.ui.TextInputWidget.prototype.positionLabel = function () {
- // Clear old values
- this.$input
- // Clear old values if present
- .css( {
- 'padding-right': '',
- 'padding-left': ''
- } );
-
- if ( this.label ) {
- this.$element.append( this.$label );
- } else {
- this.$label.detach();
- return;
- }
-
- var after = this.labelPosition === 'after',
- rtl = this.$element.css( 'direction' ) === 'rtl',
- property = after === rtl ? 'padding-left' : 'padding-right';
-
- this.$input.css( property, this.$label.outerWidth( true ) );
-
- return this;
-};