/*!
- * OOjs UI v0.18.2
+ * OOjs UI v0.18.4-fix (d4045dee45)
* https://www.mediawiki.org/wiki/OOjs_UI
*
- * Copyright 2011–2016 OOjs UI Team and other contributors.
+ * Copyright 2011–2017 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-12-06T23:32:53Z
+ * Date: 2017-01-19T20:22:26Z
*/
( function ( OO ) {
/**
* @property {number}
+ * @private
*/
OO.ui.elementId = 0;
/**
* Generate a unique ID for element
*
- * @return {string} [id]
+ * @return {string} ID
*/
OO.ui.generateElementId = function () {
- OO.ui.elementId += 1;
+ OO.ui.elementId++;
return 'oojsui-' + OO.ui.elementId;
};
/**
* Get a localized message.
*
- * In environments that provide a localization system, this function should be overridden to
- * return the message translated in the user's language. The default implementation always returns
- * English messages.
- *
* After the message key, message parameters may optionally be passed. In the default implementation,
* any occurrences of $1 are replaced with the first parameter, $2 with the second parameter, etc.
* Alternative implementations of OO.ui.msg may use any substitution system they like, as long as
* they support unnamed, ordered message parameters.
*
+ * In environments that provide a localization system, this function should be overridden to
+ * return the message translated in the user's language. The default implementation always returns
+ * English messages. An example of doing this with [jQuery.i18n](https://github.com/wikimedia/jquery.i18n)
+ * follows.
+ *
+ * @example
+ * var i, iLen, button,
+ * messagePath = 'oojs-ui/dist/i18n/',
+ * languages = [ $.i18n().locale, 'ur', 'en' ],
+ * languageMap = {};
+ *
+ * for ( i = 0, iLen = languages.length; i < iLen; i++ ) {
+ * languageMap[ languages[ i ] ] = messagePath + languages[ i ].toLowerCase() + '.json';
+ * }
+ *
+ * $.i18n().load( languageMap ).done( function() {
+ * // Replace the built-in `msg` only once we've loaded the internationalization.
+ * // OOjs UI uses `OO.ui.deferMsg` for all initially-loaded messages. So long as
+ * // you put off creating any widgets until this promise is complete, no English
+ * // will be displayed.
+ * OO.ui.msg = $.i18n;
+ *
+ * // A button displaying "OK" in the default locale
+ * button = new OO.ui.ButtonWidget( {
+ * label: OO.ui.msg( 'ooui-dialog-message-accept' ),
+ * icon: 'check'
+ * } );
+ * $( 'body' ).append( button.$element );
+ *
+ * // A button displaying "OK" in Urdu
+ * $.i18n().locale = 'ur';
+ * button = new OO.ui.ButtonWidget( {
+ * label: OO.ui.msg( 'ooui-dialog-message-accept' ),
+ * icon: 'check'
+ * } );
+ * $( 'body' ).append( button.$element );
+ * } );
+ *
* @param {string} key Message key
* @param {...Mixed} [params] Message parameters
* @return {string} Translated message with parameters substituted
return false;
};
+/**
+ * Check if the user has a 'mobile' device.
+ *
+ * For our purposes this means the user is primarily using an
+ * on-screen keyboard, touch input instead of a mouse and may
+ * have a physically small display.
+ *
+ * It is left up to implementors to decide how to compute this
+ * so the default implementation always returns false.
+ *
+ * @return {boolean} Use is on a mobile device
+ */
+OO.ui.isMobile = function () {
+ return false;
+};
+
/*!
* Mixin namespace.
*/
this.$element = config.$element ||
$( document.createElement( this.getTagName() ) );
this.elementGroup = null;
- this.debouncedUpdateThemeClassesHandler = OO.ui.debounce( this.debouncedUpdateThemeClasses );
// Initialization
if ( Array.isArray( config.classes ) ) {
};
/**
- * Gather the dynamic state (focus, value of form inputs, scroll position, etc.) of a HTML DOM node
+ * Gather the dynamic state (focus, value of form inputs, scroll position, etc.) of an HTML DOM node
* (and its children) that represent an Element of the same class and the given configuration,
* generated by the PHP implementation.
*
animations = {};
callback = typeof config.complete === 'function' && config.complete;
+ if ( callback ) {
+ OO.ui.warnDeprecation( 'Element#scrollIntoView: The `complete` callback config option is deprecated. Use the return promise instead.' );
+ }
container = this.getClosestScrollableContainer( el, config.direction );
$container = $( container );
elementDimensions = this.getDimensions( el );
* guaranteeing that theme updates do not occur within an element's constructor
*/
OO.ui.Element.prototype.updateThemeClasses = function () {
- this.debouncedUpdateThemeClassesHandler();
-};
-
-/**
- * @private
- * @localdoc This method is called directly from the QUnit tests instead of #updateThemeClasses, to
- * make them synchronous.
- */
-OO.ui.Element.prototype.debouncedUpdateThemeClasses = function () {
- OO.ui.theme.updateElementClasses( this );
+ OO.ui.theme.queueUpdateElementClasses( this );
};
/**
* @return {jQuery.Promise} Promise which resolves when the scroll is complete
*/
OO.ui.Element.prototype.scrollElementIntoView = function ( config ) {
+ if (
+ !this.isElementAttached() ||
+ !this.isVisible() ||
+ ( this.getElementGroup() && !this.getElementGroup().isVisible() )
+ ) {
+ return $.Deferred().resolve();
+ }
return OO.ui.Element.static.scrollIntoView( this.$element[ 0 ], config );
};
/* Static Properties */
/**
- * Whether this widget will behave reasonably when wrapped in a HTML `<label>`. If this is true,
+ * Whether this widget will behave reasonably when wrapped in an HTML `<label>`. If this is true,
* wrappers such as OO.ui.FieldLayout may use a `<label>` instead of implementing own label click
* handling.
*
*
* @constructor
*/
-OO.ui.Theme = function OoUiTheme() {};
+OO.ui.Theme = function OoUiTheme() {
+ this.elementClassesQueue = [];
+ this.debouncedUpdateQueuedElementClasses = OO.ui.debounce( this.updateQueuedElementClasses );
+};
/* Setup */
.addClass( classes.on.join( ' ' ) );
};
+/**
+ * @private
+ */
+OO.ui.Theme.prototype.updateQueuedElementClasses = function () {
+ var i;
+ for ( i = 0; i < this.elementClassesQueue.length; i++ ) {
+ this.updateElementClasses( this.elementClassesQueue[ i ] );
+ }
+ // Clear the queue
+ this.elementClassesQueue = [];
+};
+
+/**
+ * Queue #updateElementClasses to be called for this element.
+ *
+ * @localdoc QUnit tests override this method to directly call #queueUpdateElementClasses,
+ * to make them synchronous.
+ *
+ * @param {OO.ui.Element} element Element for which to update classes
+ */
+OO.ui.Theme.prototype.queueUpdateElementClasses = function ( element ) {
+ // Keep items in the queue unique. Use lastIndexOf to start checking from the end because that's
+ // the most common case (this method is often called repeatedly for the same element).
+ if ( this.elementClassesQueue.lastIndexOf( element ) !== -1 ) {
+ return;
+ }
+ this.elementClassesQueue.push( element );
+ this.debouncedUpdateQueuedElementClasses();
+};
+
/**
* Get the transition duration in milliseconds for dialogs opening/closing
*
* @deprecated since 0.16.0
*/
OO.ui.mixin.LabelElement.prototype.fitLabel = function () {
+ OO.ui.warnDeprecation( 'LabelElement#fitLabel: This is a deprecated no-op.' );
return this;
};
// Initial clip after visible
this.clip();
} else {
- this.$clippable.css( { width: '', height: '', overflowX: '', overflowY: '' } );
+ this.$clippable.css( {
+ width: '',
+ height: '',
+ maxWidth: '',
+ maxHeight: '',
+ overflowX: '',
+ overflowY: ''
+ } );
OO.ui.Element.static.reconsiderScrollbars( this.$clippable[ 0 ] );
this.$clippableScrollableContainer = null;
extraHeight = $container.outerHeight() - this.$clippable.outerHeight();
extraWidth = $container.outerWidth() - this.$clippable.outerWidth();
ccOffset = $container.offset();
- $scrollableContainer = this.$clippableScrollableContainer.is( 'html, body' ) ?
- this.$clippableWindow : this.$clippableScrollableContainer;
- scOffset = $scrollableContainer.offset() || { top: 0, left: 0 };
+ if ( this.$clippableScrollableContainer.is( 'html, body' ) ) {
+ $scrollableContainer = this.$clippableWindow;
+ scOffset = { top: 0, left: 0 };
+ } else {
+ $scrollableContainer = this.$clippableScrollableContainer;
+ scOffset = $scrollableContainer.offset();
+ }
scHeight = $scrollableContainer.innerHeight() - buffer;
scWidth = $scrollableContainer.innerWidth() - buffer;
ccWidth = $container.outerWidth() + buffer;
/**
* Set popup alignment
*
- * @param {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
+ * @param {string} [align=center] Alignment of the popup, `center`, `force-left`, `force-right`,
* `backwards` or `forwards`.
*/
OO.ui.PopupWidget.prototype.setAlignment = function ( align ) {
- // Validate alignment and transform deprecated values
- if ( [ 'left', 'right', 'force-left', 'force-right', 'backwards', 'forwards', 'center' ].indexOf( align ) > -1 ) {
- this.align = { left: 'force-right', right: 'force-left' }[ align ] || align;
+ // Transform values deprecated since v0.11.0
+ if ( align === 'left' || align === 'right' ) {
+ OO.ui.warnDeprecation( 'PopupWidget#setAlignment parameter value `' + align + '` is deprecated. Use `force-right` or `force-left` instead.' );
+ align = { left: 'force-right', right: 'force-left' }[ align ];
+ }
+
+ // Validate alignment
+ if ( [ 'force-left', 'force-right', 'backwards', 'forwards', 'center' ].indexOf( align ) > -1 ) {
+ this.align = align;
} else {
this.align = 'center';
}
* OO.ui.MenuOptionWidget. The DropdownWidget takes care of opening and displaying the menu so that
* users can interact with it.
*
- * If you want to use this within a HTML form, such as a OO.ui.FormLayout, use
+ * If you want to use this within an HTML form, such as a OO.ui.FormLayout, use
* OO.ui.DropdownInputWidget instead.
*
* @example
* an interface for adding, removing and selecting options.
* Please see the [OOjs UI documentation on MediaWiki][1] for more information.
*
- * If you want to use this within a HTML form, such as a OO.ui.FormLayout, use
+ * If you want to use this within an HTML form, such as a OO.ui.FormLayout, use
* OO.ui.RadioSelectInputWidget instead.
*
* @example
* CheckboxMultiselectWidget provides an interface for adding, removing and selecting options.
* Please see the [OOjs UI documentation on MediaWiki][1] for more information.
*
- * If you want to use this within a HTML form, such as a OO.ui.FormLayout, use
+ * If you want to use this within an HTML form, such as a OO.ui.FormLayout, use
* OO.ui.CheckboxMultiselectInputWidget instead.
*
* @example
}
if ( !this.isElementInViewport( this.$floatableContainer, this.$floatableClosestScrollable ) ) {
- this.$floatable.addClass( 'oo-ui-floatableElement-hidden' );
+ this.$floatable.addClass( 'oo-ui-element-hidden' );
return;
} else {
- this.$floatable.removeClass( 'oo-ui-floatableElement-hidden' );
+ this.$floatable.removeClass( 'oo-ui-element-hidden' );
}
if ( !this.needsCustomPosition ) {
OO.inheritClass( OO.ui.FloatingMenuSelectWidget, OO.ui.MenuSelectWidget );
OO.mixinClass( OO.ui.FloatingMenuSelectWidget, OO.ui.mixin.FloatableElement );
-// For backwards compatibility
-OO.ui.TextInputMenuSelectWidget = OO.ui.FloatingMenuSelectWidget;
-
/* Methods */
/**
return this;
};
+/*
+ * The old name for the FloatingMenuSelectWidget widget, provided for backwards-compatibility.
+ *
+ * @class
+ * @extends OO.ui.FloatingMenuSelectWidget
+ *
+ * @constructor
+ * @deprecated since v0.12.5.
+ */
+OO.ui.TextInputMenuSelectWidget = function OoUiTextInputMenuSelectWidget() {
+ OO.ui.warnDeprecation( 'TextInputMenuSelectWidget is deprecated. Use the FloatingMenuSelectWidget instead.' );
+ // Parent constructor
+ OO.ui.TextInputMenuSelectWidget.parent.apply( this, arguments );
+};
+
+OO.inheritClass( OO.ui.TextInputMenuSelectWidget, OO.ui.FloatingMenuSelectWidget );
+
/**
* Progress bars visually display the status of an operation, such as a download,
* and can be either determinate or indeterminate:
* in {@link OO.ui.FieldLayout field layouts} that use the {@link OO.ui.FieldLayout#align inline}
* alignment. For more information, please see the [OOjs UI documentation on MediaWiki][1].
*
- * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ * This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
* @example
* // An example of selected, unselected, and disabled checkbox inputs
/**
* DropdownInputWidget is a {@link OO.ui.DropdownWidget DropdownWidget} intended to be used
- * within a HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
+ * within an HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
* of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
* more information about input widgets.
*
* with {@link OO.ui.RadioOptionWidget radio options} instead of this class. For more information,
* please see the [OOjs UI documentation on MediaWiki][1].
*
- * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ * This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
* @example
* // An example of selected, unselected, and disabled radio inputs
/**
* RadioSelectInputWidget is a {@link OO.ui.RadioSelectWidget RadioSelectWidget} intended to be used
- * within a HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
+ * within an HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
* of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
* more information about input widgets.
*
* 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.
+ * This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
* @example
* // Example of a text input widget
blur: this.onBlur.bind( this ),
focus: this.onFocus.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.$input.attr( 'rows', config.rows );
}
if ( this.label || config.autosize ) {
+ this.isWaitingToBeAttached = true;
this.installParentChangeDetector();
}
};
* @param {jQuery.Event} e Focus event
*/
OO.ui.TextInputWidget.prototype.onFocus = function () {
+ if ( this.isWaitingToBeAttached ) {
+ // If we've received focus, then we must be attached to the document, and if
+ // isWaitingToBeAttached is still true, that means the handler never fired. Fire it now.
+ this.onElementAttach();
+ }
this.setValidityFlag( true );
};
* @param {jQuery.Event} e Element attach event
*/
OO.ui.TextInputWidget.prototype.onElementAttach = function () {
+ this.isWaitingToBeAttached = false;
// Any previously calculated size is now probably invalid if we reattached elsewhere
this.valCache = null;
this.adjustSize();
if ( MutationObserver ) {
// The new way. If only it wasn't so ugly.
- if ( this.$element.closest( 'html' ).length ) {
+ if ( this.isElementAttached() ) {
// 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,
onRemove = function () {
// If the node was attached somewhere else, report it
- if ( widget.$element.closest( 'html' ).length ) {
+ if ( widget.isElementAttached() ) {
widget.onElementAttach();
}
mutationObserver.disconnect();
var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError,
idealHeight, newHeight, scrollWidth, property;
+ if ( this.isWaitingToBeAttached ) {
+ // #onElementAttach will be called soon, which calls this method
+ return this;
+ }
+
if ( this.multiline && this.$input.val() !== this.valCache ) {
if ( this.autosize ) {
this.$clone
*/
OO.ui.TextInputWidget.prototype.positionLabel = function () {
var after, rtl, property;
+
+ if ( this.isWaitingToBeAttached ) {
+ // #onElementAttach will be called soon, which calls this method
+ return this;
+ }
+
// Clear old values
this.$input
// Clear old values if present
* - by choosing a value from the menu. The value of the chosen option will then appear in the text
* input field.
*
- * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+ * This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
* For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
*
autocomplete: false
}, config );
+ // ComboBoxInputWidget shouldn't support multiline
+ config.multiline = false;
+
// Parent constructor
OO.ui.ComboBoxInputWidget.parent.call( this, config );
this.notices = [];
this.$field = $( '<div>' );
this.$messages = $( '<ul>' );
+ this.$header = $( '<div>' );
this.$body = $( '<' + ( hasInputWidget ? 'label' : 'div' ) + '>' );
this.align = null;
if ( config.help ) {
this.$element
.addClass( 'oo-ui-fieldLayout' )
.toggleClass( 'oo-ui-fieldLayout-disabled', this.fieldWidget.isDisabled() )
- .append( this.$help, this.$body );
+ .append( this.$body );
this.$body.addClass( 'oo-ui-fieldLayout-body' );
+ this.$header.addClass( 'oo-ui-fieldLayout-header' );
this.$messages.addClass( 'oo-ui-fieldLayout-messages' );
this.$field
.addClass( 'oo-ui-fieldLayout-field' )
value = 'left';
}
// Reorder elements
- if ( value === 'inline' ) {
- this.$body.append( this.$field, this.$label );
+ if ( value === 'top' ) {
+ this.$header.append( this.$label, this.$help );
+ this.$body.append( this.$header, this.$field );
+ } else if ( value === 'inline' ) {
+ this.$header.append( this.$label, this.$help );
+ this.$body.append( this.$field, this.$header );
} else {
- this.$body.append( this.$label, this.$field );
+ this.$header.append( this.$label );
+ this.$body.append( this.$header, this.$help, this.$field );
}
// Set classes. The following classes can be used here:
// * oo-ui-fieldLayout-align-left
OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { $label: $( '<div>' ) } ) );
OO.ui.mixin.GroupElement.call( this, config );
+ // Properties
+ this.$header = $( '<div>' );
if ( config.help ) {
this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
classes: [ 'oo-ui-fieldsetLayout-help' ],
}
// Initialization
+ this.$header
+ .addClass( 'oo-ui-fieldsetLayout-header' )
+ .append( this.$icon, this.$label, this.$help );
this.$group.addClass( 'oo-ui-fieldsetLayout-group' );
this.$element
.addClass( 'oo-ui-fieldsetLayout' )
- .prepend( this.$label, this.$help, this.$icon, this.$group );
+ .prepend( this.$header, this.$group );
if ( Array.isArray( config.items ) ) {
this.addItems( config.items );
}
OO.mixinClass( OO.ui.HorizontalLayout, OO.ui.mixin.GroupElement );
}( OO ) );
+
+//# sourceMappingURL=oojs-ui-core.js.map
\ No newline at end of file