/*!
- * OOjs UI v0.1.0-pre (40de4dabe6)
+ * OOjs UI v0.1.0-pre (05f0fefc3f)
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2014 OOjs Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2014-10-22T23:42:16Z
+ * Date: 2014-11-11T16:29:49Z
*/
( function ( OO ) {
'ooui-dialog-process-error': 'Something went wrong',
// Label for process dialog dismiss error button, visible when describing errors
'ooui-dialog-process-dismiss': 'Dismiss',
- // Label for process dialog retry action button, visible when describing recoverable errors
- 'ooui-dialog-process-retry': 'Try again'
+ // Label for process dialog retry action button, visible when describing only recoverable errors
+ 'ooui-dialog-process-retry': 'Try again',
+ // Label for process dialog retry action button, visible when describing only warnings
+ 'ooui-dialog-process-continue': 'Continue'
};
/**
*
* @constructor
* @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$pending] Element to mark as pending, defaults to this.$element
*/
OO.ui.PendingElement = function OoUiPendingElement( config ) {
- // Config initialisation
+ // Configuration initialization
config = config || {};
// Properties
* @constructor
* @param {Object} [config] Configuration options
* @cfg {Function} [$] jQuery for the frame the widget is in
- * @cfg {string[]} [classes] CSS class names
+ * @cfg {string[]} [classes] CSS class names to add
* @cfg {string} [text] Text to insert
* @cfg {jQuery} [$content] Content elements to append (after text)
*/
*
* @static
* @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the direction for
- * @return {string} Text direction, either `ltr` or `rtl`
+ * @return {string} Text direction, either 'ltr' or 'rtl'
*/
OO.ui.Element.getDir = function ( obj ) {
var isDoc, isWin;
*
* @static
* @param {HTMLElement} el Element to scroll into view
- * @param {Object} [config={}] Configuration config
+ * @param {Object} [config] Configuration options
* @param {string} [config.duration] jQuery animation duration value
* @param {string} [config.direction] Scroll in only one direction, e.g. 'x' or 'y', omit
* to scroll in both directions
* Check if element supports one or more methods.
*
* @param {string|string[]} methods Method or list of methods to check
- * @return boolean All methods are supported
+ * @return {boolean} All methods are supported
*/
OO.ui.Element.prototype.supports = function ( methods ) {
var i, len,
/**
* Scroll element into view.
*
- * @param {Object} [config={}]
+ * @param {Object} [config] Configuration options
*/
OO.ui.Element.prototype.scrollElementIntoView = function ( config ) {
return OO.ui.Element.scrollIntoView( this.$element[0], config );
* @param {Object} [config] Configuration options
*/
OO.ui.Layout = function OoUiLayout( config ) {
- // Initialize config
+ // Configuration initialization
config = config || {};
// Parent constructor
/**
* Check if the widget is disabled.
*
- * @param {boolean} Button is disabled
+ * @return {boolean} Button is disabled
*/
OO.ui.Widget.prototype.isDisabled = function () {
return this.disabled;
* @param {Object} [config] Configuration options
* @cfg {string} [size] Symbolic name of dialog size, `small`, `medium`, `large` or `full`; omit to
* use #static-size
- * @fires initialize
*/
OO.ui.Window = function OoUiWindow( config ) {
// Configuration initialization
* instead of display.
*
* @param {boolean} [show] Make window visible, omit to toggle visibility
- * @fires visible
+ * @fires toggle
* @chainable
*/
OO.ui.Window.prototype.toggle = function ( show ) {
this.getTeardownProcess( data ).execute().done( function () {
// Force redraw by asking the browser to measure the elements' widths
- win.$element.removeClass( 'oo-ui-window-setup' ).width();
+ win.$element.removeClass( 'oo-ui-window-load oo-ui-window-setup' ).width();
win.$content.removeClass( 'oo-ui-window-content-setup' ).width();
win.$element.hide();
win.visible = false;
/**
* Load the frame contents.
*
- * Once the iframe's stylesheets are loaded, the `load` event will be emitted and the returned
- * promise will be resolved. Calling while loading will return a promise but not trigger a new
- * loading cycle. Calling after loading is complete will return a promise that's already been
- * resolved.
+ * Once the iframe's stylesheets are loaded the returned promise will be resolved. Calling while
+ * loading will return a promise but not trigger a new loading cycle. Calling after loading is
+ * complete will return a promise that's already been resolved.
*
* Sounds simple right? Read on...
*
* All this stylesheet injection and polling magic is in #transplantStyles.
*
* @return {jQuery.Promise} Promise resolved when loading is complete
- * @fires load
*/
OO.ui.Window.prototype.load = function () {
var sub, doc, loading,
win = this;
+ this.$element.addClass( 'oo-ui-window-load' );
+
// Non-isolated windows are already "loaded"
if ( !this.loading && !this.isolated ) {
this.loading = $.Deferred().resolve();
// Window opening
if ( opening.state() !== 'rejected' ) {
- // Begin loading the window if it's not loading or loaded already - may take noticable time
- // and we want to do this in paralell with any other preparatory actions
- if ( !win.isLoading() && !win.isLoaded() ) {
- // Finish initializing the window (must be done after manager is attached to DOM)
+ if ( !win.getManager() ) {
win.setManager( this );
- preparing.push( win.load() );
}
+ preparing.push( win.load() );
if ( this.closing ) {
// If a window is currently closing, wait for it to complete
* @param {string|jQuery} message Description of error
* @param {Object} [config] Configuration options
* @cfg {boolean} [recoverable=true] Error is recoverable
+ * @cfg {boolean} [warning=false] Whether this error is a warning or not.
*/
OO.ui.Error = function OoUiElement( message, config ) {
// Configuration initialization
// Properties
this.message = message instanceof jQuery ? message : String( message );
this.recoverable = config.recoverable === undefined || !!config.recoverable;
+ this.warning = !!config.warning;
};
/* Setup */
return this.recoverable;
};
+/**
+ * Check if the error is a warning
+ *
+ * @return {boolean} Error is warning
+ */
+OO.ui.Error.prototype.isWarning = function () {
+ return this.warning;
+};
+
/**
* Get error message as DOM nodes.
*
* @param {Object} [config] Configuration options
*/
OO.ui.Theme = function OoUiTheme( config ) {
- // Initialize config
+ // Configuration initialization
config = config || {};
};
/**
* Get a list of classes to be applied to a widget.
*
- * @localdoc The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or
- * removes, otherwise state transitions will not work properly.
+ * The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or removes,
+ * otherwise state transitions will not work properly.
*
* @param {OO.ui.Element} element Element for which to get classes
* @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
*
* For elements with theme logic hooks, this should be called anytime there's a state change.
*
- * @param {OO.ui.Element} Element for which to update classes
+ * @param {OO.ui.Element} element Element for which to update classes
* @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
*/
OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
* @param {Object} [config] Configuration options
* @cfg {jQuery} [$button] Button node, assigned to #$button, omit to use a generated `<a>`
* @cfg {boolean} [framed=true] Render button with a frame
- * @cfg {number} [tabIndex=0] Button's tab index, use null to have no tabIndex
+ * @cfg {number} [tabIndex=0] Button's tab index. Use 0 to use default ordering, use -1 to prevent
+ * tab focusing.
* @cfg {string} [accessKey] Button's access key
*/
OO.ui.ButtonElement = function OoUiButtonElement( config ) {
* @cfg {jQuery} [$group] Container node, assigned to #$group, omit to use a generated `<div>`
*/
OO.ui.GroupElement = function OoUiGroupElement( config ) {
- // Configuration
+ // Configuration intialization
config = config || {};
// Properties
groupEvent = events[itemEvent];
// Remove existing aggregated event
- if ( itemEvent in this.aggregateItemEvents ) {
+ if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
// Don't allow duplicate aggregations
if ( groupEvent ) {
throw new Error( 'Duplicate item event aggregation for ' + itemEvent );
/**
* Add items.
*
- * Adding an existing item (by value) will move it.
+ * Adding an existing item will move it.
*
* @param {OO.ui.Element[]} items Items
* @param {number} [index] Index to insert items at
!$.isEmptyObject( this.aggregateItemEvents )
) {
remove = {};
- if ( itemEvent in this.aggregateItemEvents ) {
+ if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
remove[itemEvent] = [ 'emit', this.aggregateItemEvents[itemEvent], item ];
}
item.disconnect( this, remove );
!$.isEmptyObject( this.aggregateItemEvents )
) {
remove = {};
- if ( itemEvent in this.aggregateItemEvents ) {
+ if ( Object.prototype.hasOwnProperty.call( this.aggregateItemEvents, itemEvent ) ) {
remove[itemEvent] = [ 'emit', this.aggregateItemEvents[itemEvent], item ];
}
item.disconnect( this, remove );
* @cfg {string} [iconTitle] Icon title text or a function that returns text
*/
OO.ui.IconElement = function OoUiIconElement( config ) {
- // Config intialization
+ // Configuration intialization
config = config || {};
// Properties
};
/**
- * Set icon.
+ * Set icon name.
*
* @param {Object|string|null} icon Symbolic icon name, or map of icon names keyed by language ID;
* use the 'default' key to specify the icon to be used when there is no icon in the user's
};
/**
- * Get icon.
+ * Get icon name.
*
- * @return {string} Icon
+ * @return {string} Icon name
*/
OO.ui.IconElement.prototype.getIcon = function () {
return this.icon;
* @cfg {string} [indicatorTitle] Indicator title text or a function that returns text
*/
OO.ui.IndicatorElement = function OoUiIndicatorElement( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Properties
*
* @static
* @inheritable
- * @property {string|null} Symbolic indicator name or null for no indicator
+ * @property {string|null} Symbolic indicator name
*/
OO.ui.IndicatorElement.static.indicator = null;
};
/**
- * Set indicator.
+ * Set indicator name.
*
* @param {string|null} indicator Symbolic name of indicator to use or null for no indicator
* @chainable
};
/**
- * Get indicator.
+ * Get indicator name.
*
- * @return {string} title Symbolic name of indicator
+ * @return {string} Symbolic name of indicator
*/
OO.ui.IndicatorElement.prototype.getIndicator = function () {
return this.indicator;
* @cfg {boolean} [autoFitLabel=true] Whether to fit the label or not.
*/
OO.ui.LabelElement = function OoUiLabelElement( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Properties
* Set the label.
*
* An empty string will result in the label being hidden. A string containing only whitespace will
- * be converted to a single
+ * be converted to a single ` `.
*
* @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
* text; or null for no label
/**
* Get the label.
*
- * @return {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
+ * @return {jQuery|string|Function|null} Label nodes; text; a function that returns nodes or
* text; or null for no label
*/
OO.ui.LabelElement.prototype.getLabel = function () {
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string[]} [flags=[]] Styling flags, e.g. 'primary', 'destructive' or 'constructive'
+ * @cfg {string|string[]} [flags] Styling flags, e.g. 'primary', 'destructive' or 'constructive'
* @cfg {jQuery} [$flagged] Flagged node, assigned to #$flagged, omit to use #$element
*/
OO.ui.FlaggedElement = function OoUiFlaggedElement( config ) {
- // Config initialization
+ // Configuration initialization
config = config || {};
// Properties
/**
* Get the names of all flags set.
*
- * @return {string[]} flags Flag names
+ * @return {string[]} Flag names
*/
OO.ui.FlaggedElement.prototype.getFlags = function () {
return Object.keys( this.flags );
* @constructor
* @param {Object} [config] Configuration options
* @cfg {jQuery} [$titled] Titled node, assigned to #$titled, omit to use #$element
- * @cfg {string|Function} [title] Title text or a function that returns text
+ * @cfg {string|Function} [title] Title text or a function that returns text. If not provided, the
+ * static property 'title' is used.
*/
OO.ui.TitledElement = function OoUiTitledElement( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Properties
* @cfg {string|Function} [title] Title text or a function that returns text
*/
OO.ui.Tool = function OoUiTool( toolGroup, config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor
/**
* Check if the button is active.
*
- * @param {boolean} Button is active
+ * @return {boolean} Button is active
*/
OO.ui.Tool.prototype.isActive = function () {
return this.active;
$: this.$,
label: OO.ui.msg( 'ooui-dialog-process-dismiss' )
} );
- this.retryButton = new OO.ui.ButtonWidget( {
- $: this.$,
- label: OO.ui.msg( 'ooui-dialog-process-retry' )
- } );
+ this.retryButton = new OO.ui.ButtonWidget( { $: this.$ } );
this.$errors = this.$( '<div>' );
this.$errorsTitle = this.$( '<div>' );
OO.ui.ProcessDialog.prototype.showErrors = function ( errors ) {
var i, len, $item,
items = [],
- recoverable = true;
+ recoverable = true,
+ warning = false;
for ( i = 0, len = errors.length; i < len; i++ ) {
if ( !errors[i].isRecoverable() ) {
recoverable = false;
}
+ if ( errors[i].isWarning() ) {
+ warning = true;
+ }
$item = this.$( '<div>' )
.addClass( 'oo-ui-processDialog-error' )
.append( errors[i].getMessage() );
} else {
this.currentAction.setDisabled( true );
}
+ if ( warning ) {
+ this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-continue' ) );
+ } else {
+ this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-retry' ) );
+ }
this.retryButton.toggle( recoverable );
this.$errorsTitle.after( this.$errorItems );
this.$errors.show().scrollTop( 0 );
* @cfg {boolean} [editable=false] Show controls for adding, removing and reordering pages
*/
OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
- // Initialize configuration
+ // Configuration initialization
config = config || {};
// Parent constructor
/**
* Layout made of a field and optional label.
*
- * @class
- * @extends OO.ui.Layout
- * @mixins OO.ui.LabelElement
- *
* Available label alignment modes include:
* - left: Label is before the field and aligned away from it, best for when the user will be
* scanning for a specific label in a form with many fields
* - inline: Label is after the field and aligned toward it, best for small boolean fields like
* checkboxes or radio buttons
*
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.LabelElement
+ *
* @constructor
* @param {OO.ui.Widget} fieldWidget Field widget
* @param {Object} [config] Configuration options
* @cfg {string} [help] Explanatory text shown as a '?' icon.
*/
OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
- // Config initialization
+ // Configuration initialization
config = $.extend( { align: 'left' }, config );
// Parent constructor
OO.inheritClass( OO.ui.FieldLayout, OO.ui.Layout );
OO.mixinClass( OO.ui.FieldLayout, OO.ui.LabelElement );
+/* Static Properties */
+
+OO.ui.FieldLayout.static.tagName = 'label';
+
/* Methods */
/**
/**
* Set the field alignment mode.
*
+ * @private
* @param {string} value Alignment mode, either 'left', 'right', 'top' or 'inline'
* @chainable
*/
*
* @class
* @extends OO.ui.Layout
- * @mixins OO.ui.LabelElement
* @mixins OO.ui.IconElement
+ * @mixins OO.ui.LabelElement
* @mixins OO.ui.GroupElement
*
* @constructor
* @cfg {OO.ui.FieldLayout[]} [items] Items to add
*/
OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
- // Config initialization
+ // Configuration initialization
config = config || {};
// Parent constructor
OO.ui.GridLayout = function OoUiGridLayout( panels, config ) {
var i, len, widths;
- // Config initialization
+ // Configuration initialization
config = config || {};
// Parent constructor
* @cfg {boolean} [expanded=true] Expand size to fill the entire parent element
*/
OO.ui.PanelLayout = function OoUiPanelLayout( config ) {
- // Config initialization
+ // Configuration initialization
config = $.extend( {
scrollable: false,
padded: false,
* @cfg {OO.ui.Layout[]} [items] Layouts to add
*/
OO.ui.StackLayout = function OoUiStackLayout( config ) {
- // Config initialization
+ // Configuration initialization
config = $.extend( { scrollable: true }, config );
// Parent constructor
* @cfg {boolean} [expanded=false] Whether the collapsible tools are expanded by default
*/
OO.ui.ListToolGroup = function OoUiListToolGroup( toolbar, config ) {
+ // Configuration intialization
+ config = config || {};
+
// Properties (must be set before parent constructor, which calls #populate)
this.allowCollapse = config.allowCollapse;
this.forceExpand = config.forceExpand;
*
* Subclasses must handle `select` and `choose` events on #lookupMenu to make use of selections.
*
+ * Subclasses that set the value of #lookupInput from their `choose` or `select` handler should
+ * be aware that this will cause new suggestions to be looked up for the new value. If this is
+ * not desired, disable lookups with #setLookupsDisabled, then set the value, then re-enable lookups.
+ *
* @class
* @abstract
*
* @constructor
* @param {OO.ui.TextInputWidget} input Input widget
* @param {Object} [config] Configuration options
- * @cfg {jQuery} [$overlay] Overlay layer; defaults to the current window's overlay.
+ * @cfg {jQuery} [$overlay] Overlay for dropdown; defaults to relative positioning
+ * @cfg {jQuery} [$container=input.$element] Element to render menu under
*/
OO.ui.LookupInputWidget = function OoUiLookupInputWidget( input, config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Properties
this.lookupInput = input;
- this.$overlay = config.$overlay || ( this.$.$iframe || this.$element ).closest( '.oo-ui-window' ).children( '.oo-ui-window-overlay' );
- if ( this.$overlay.length === 0 ) {
- this.$overlay = this.$( 'body' );
- }
+ this.$overlay = config.$overlay || this.$element;
this.lookupMenu = new OO.ui.TextInputMenuWidget( this, {
$: OO.ui.Element.getJQuery( this.$overlay ),
input: this.lookupInput,
this.lookupCache = {};
this.lookupQuery = null;
this.lookupRequest = null;
- this.populating = false;
+ this.lookupsDisabled = false;
+ this.lookupInputFocused = false;
// Events
- this.$overlay.append( this.lookupMenu.$element );
-
this.lookupInput.$input.on( {
focus: this.onLookupInputFocus.bind( this ),
blur: this.onLookupInputBlur.bind( this ),
mousedown: this.onLookupInputMouseDown.bind( this )
} );
this.lookupInput.connect( this, { change: 'onLookupInputChange' } );
+ this.lookupMenu.connect( this, { toggle: 'onLookupMenuToggle' } );
// Initialization
this.$element.addClass( 'oo-ui-lookupWidget' );
this.lookupMenu.$element.addClass( 'oo-ui-lookupWidget-menu' );
+ this.$overlay.append( this.lookupMenu.$element );
};
/* Methods */
* @param {jQuery.Event} e Input focus event
*/
OO.ui.LookupInputWidget.prototype.onLookupInputFocus = function () {
- this.openLookupMenu();
+ this.lookupInputFocused = true;
+ this.populateLookupMenu();
};
/**
* @param {jQuery.Event} e Input blur event
*/
OO.ui.LookupInputWidget.prototype.onLookupInputBlur = function () {
- this.lookupMenu.toggle( false );
+ this.closeLookupMenu();
+ this.lookupInputFocused = false;
};
/**
* @param {jQuery.Event} e Input mouse down event
*/
OO.ui.LookupInputWidget.prototype.onLookupInputMouseDown = function () {
- this.openLookupMenu();
+ // Only open the menu if the input was already focused.
+ // This way we allow the user to open the menu again after closing it with Esc
+ // by clicking in the input. Opening (and populating) the menu when initially
+ // clicking into the input is handled by the focus handler.
+ if ( this.lookupInputFocused && !this.lookupMenu.isVisible() ) {
+ this.populateLookupMenu();
+ }
};
/**
* @param {string} value New input value
*/
OO.ui.LookupInputWidget.prototype.onLookupInputChange = function () {
- this.openLookupMenu();
+ if ( this.lookupInputFocused ) {
+ this.populateLookupMenu();
+ }
+};
+
+/**
+ * Handle the lookup menu being shown/hidden.
+ * @param {boolean} visible Whether the lookup menu is now visible.
+ */
+OO.ui.LookupInputWidget.prototype.onLookupMenuToggle = function ( visible ) {
+ if ( !visible ) {
+ // When the menu is hidden, abort any active request and clear the menu.
+ // This has to be done here in addition to closeLookupMenu(), because
+ // MenuWidget will close itself when the user presses Esc.
+ this.abortLookupRequest();
+ this.lookupMenu.clearItems();
+ }
};
/**
};
/**
- * Open the menu.
+ * Disable or re-enable lookups.
+ *
+ * When lookups are disabled, calls to #populateLookupMenu will be ignored.
+ *
+ * @param {boolean} disabled Disable lookups
+ */
+OO.ui.LookupInputWidget.prototype.setLookupsDisabled = function ( disabled ) {
+ this.lookupsDisabled = !!disabled;
+};
+
+/**
+ * Open the menu. If there are no entries in the menu, this does nothing.
*
* @chainable
*/
OO.ui.LookupInputWidget.prototype.openLookupMenu = function () {
- var value = this.lookupInput.getValue();
-
- if ( this.lookupMenu.$input.is( ':focus' ) && $.trim( value ) !== '' ) {
- this.populateLookupMenu();
+ if ( !this.lookupMenu.isEmpty() ) {
this.lookupMenu.toggle( true );
- } else {
- this.lookupMenu
- .clearItems()
- .toggle( false );
}
+ return this;
+};
+/**
+ * Close the menu, empty it, and abort any pending request.
+ *
+ * @chainable
+ */
+OO.ui.LookupInputWidget.prototype.closeLookupMenu = function () {
+ this.lookupMenu.toggle( false );
+ this.abortLookupRequest();
+ this.lookupMenu.clearItems();
return this;
};
/**
- * Populate lookup menu with current information.
+ * Request menu items based on the input's current value, and when they arrive,
+ * populate the menu with these items and show the menu.
+ *
+ * If lookups have been disabled with #setLookupsDisabled, this function does nothing.
*
* @chainable
*/
OO.ui.LookupInputWidget.prototype.populateLookupMenu = function () {
- var widget = this;
+ var widget = this,
+ value = this.lookupInput.getValue();
- if ( !this.populating ) {
- this.populating = true;
+ if ( this.lookupsDisabled ) {
+ return;
+ }
+
+ // If the input is empty, clear the menu
+ if ( value === '' ) {
+ this.closeLookupMenu();
+ // Skip population if there is already a request pending for the current value
+ } else if ( value !== this.lookupQuery ) {
this.getLookupMenuItems()
.done( function ( items ) {
widget.lookupMenu.clearItems();
.addItems( items )
.toggle( true );
widget.initializeLookupMenuSelection();
- widget.openLookupMenu();
} else {
- widget.lookupMenu.toggle( true );
+ widget.lookupMenu.toggle( false );
}
- widget.populating = false;
} )
.fail( function () {
widget.lookupMenu.clearItems();
- widget.populating = false;
} );
}
};
/**
- * Set selection in the lookup menu with current information.
+ * Select and highlight the first selectable item in the menu.
*
* @chainable
*/
* Get lookup menu items for the current query.
*
* @return {jQuery.Promise} Promise object which will be passed menu items as the first argument
- * of the done event
+ * of the done event. If the request was aborted to make way for a subsequent request,
+ * this promise will not be rejected: it will remain pending forever.
*/
OO.ui.LookupInputWidget.prototype.getLookupMenuItems = function () {
var widget = this,
value = this.lookupInput.getValue(),
- deferred = $.Deferred();
+ deferred = $.Deferred(),
+ ourRequest;
- if ( value && value !== this.lookupQuery ) {
- // Abort current request if query has changed
- if ( this.lookupRequest ) {
- this.lookupRequest.abort();
- this.lookupQuery = null;
- this.lookupRequest = null;
- }
- if ( value in this.lookupCache ) {
- deferred.resolve( this.getLookupMenuItemsFromData( this.lookupCache[value] ) );
- } else {
- this.lookupQuery = value;
- this.lookupRequest = this.getLookupRequest()
- .always( function () {
+ this.abortLookupRequest();
+ if ( Object.prototype.hasOwnProperty.call( this.lookupCache, value ) ) {
+ deferred.resolve( this.getLookupMenuItemsFromData( this.lookupCache[value] ) );
+ } else {
+ this.lookupInput.pushPending();
+ this.lookupQuery = value;
+ ourRequest = this.lookupRequest = this.getLookupRequest();
+ ourRequest
+ .always( function () {
+ // We need to pop pending even if this is an old request, otherwise
+ // the widget will remain pending forever.
+ // TODO: this assumes that an aborted request will fail or succeed soon after
+ // being aborted, or at least eventually. It would be nice if we could popPending()
+ // at abort time, but only if we knew that we hadn't already called popPending()
+ // for that request.
+ widget.lookupInput.popPending();
+ } )
+ .done( function ( data ) {
+ // If this is an old request (and aborting it somehow caused it to still succeed),
+ // ignore its success completely
+ if ( ourRequest === widget.lookupRequest ) {
widget.lookupQuery = null;
widget.lookupRequest = null;
- } )
- .done( function ( data ) {
widget.lookupCache[value] = widget.getLookupCacheItemFromData( data );
deferred.resolve( widget.getLookupMenuItemsFromData( widget.lookupCache[value] ) );
- } )
- .fail( function () {
+ }
+ } )
+ .fail( function () {
+ // If this is an old request (or a request failing because it's being aborted),
+ // ignore its failure completely
+ if ( ourRequest === widget.lookupRequest ) {
+ widget.lookupQuery = null;
+ widget.lookupRequest = null;
deferred.reject();
- } );
- this.pushPending();
- this.lookupRequest.always( function () {
- widget.popPending();
+ }
} );
- }
}
return deferred.promise();
};
+/**
+ * Abort the currently pending lookup request, if any.
+ */
+OO.ui.LookupInputWidget.prototype.abortLookupRequest = function () {
+ var oldRequest = this.lookupRequest;
+ if ( oldRequest ) {
+ // First unset this.lookupRequest to the fail handler will notice
+ // that the request is no longer current
+ this.lookupRequest = null;
+ this.lookupQuery = null;
+ oldRequest.abort();
+ }
+};
+
/**
* Get a new request object of the current lookup query value.
*
* @abstract
- * @return {jqXHR} jQuery AJAX object, or promise object with an .abort() method
+ * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
*/
OO.ui.LookupInputWidget.prototype.getLookupRequest = function () {
// Stub, implemented in subclass
};
/**
- * Handle successful lookup request.
- *
- * Overriding methods should call #populateLookupMenu when results are available and cache results
- * for future lookups in #lookupCache as an array of #OO.ui.MenuItemWidget objects.
+ * Get a list of menu item widgets from the data stored by the lookup request's done handler.
*
* @abstract
- * @param {Mixed} data Response from server
+ * @param {Mixed} data Cached result data, usually an array
+ * @return {OO.ui.MenuItemWidget[]} Menu items
*/
-OO.ui.LookupInputWidget.prototype.onLookupRequestDone = function () {
+OO.ui.LookupInputWidget.prototype.getLookupMenuItemsFromData = function () {
// Stub, implemented in subclass
+ return [];
};
/**
- * Get a list of menu item widgets from the data stored by the lookup request's done handler.
+ * Get lookup cache item from server response data.
*
* @abstract
- * @param {Mixed} data Cached result data, usually an array
- * @return {OO.ui.MenuItemWidget[]} Menu items
+ * @param {Mixed} data Response from server
+ * @return {Mixed} Cached result data
*/
-OO.ui.LookupInputWidget.prototype.getLookupMenuItemsFromData = function () {
+OO.ui.LookupInputWidget.prototype.getLookupCacheItemFromData = function () {
// Stub, implemented in subclass
return [];
};
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {OO.ui.ButtonWidget} [items] Buttons to add
+ * @cfg {OO.ui.ButtonWidget[]} [items] Buttons to add
*/
OO.ui.ButtonGroupWidget = function OoUiButtonGroupWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
// Parent constructor
OO.ui.ButtonGroupWidget.super.call( this, config );
* @cfg {boolean} [framed=false] Render button with a frame
*/
OO.ui.ActionWidget = function OoUiActionWidget( config ) {
- // Config intialization
+ // Configuration initialization
config = $.extend( { framed: false }, config );
// Parent constructor
};
/**
- * Icon widget.
- *
- * See OO.ui.IconElement for more information.
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.IconElement
- * @mixins OO.ui.TitledElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.IconWidget = function OoUiIconWidget( config ) {
- // Config intialization
- config = config || {};
-
- // Parent constructor
- OO.ui.IconWidget.super.call( this, config );
-
- // Mixin constructors
- OO.ui.IconElement.call( this, $.extend( {}, config, { $icon: this.$element } ) );
- OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
-
- // Initialization
- this.$element.addClass( 'oo-ui-iconWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.IconWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.IconWidget, OO.ui.IconElement );
-OO.mixinClass( OO.ui.IconWidget, OO.ui.TitledElement );
-
-/* Static Properties */
-
-OO.ui.IconWidget.static.tagName = 'span';
-
-/**
- * Indicator widget.
+ * Dropdown menu of options.
*
- * See OO.ui.IndicatorElement for more information.
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.IndicatorElement
- * @mixins OO.ui.TitledElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
- // Config intialization
- config = config || {};
-
- // Parent constructor
- OO.ui.IndicatorWidget.super.call( this, config );
-
- // Mixin constructors
- OO.ui.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$element } ) );
- OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
-
- // Initialization
- this.$element.addClass( 'oo-ui-indicatorWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.IndicatorWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.IndicatorElement );
-OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.TitledElement );
-
-/* Static Properties */
-
-OO.ui.IndicatorWidget.static.tagName = 'span';
-
-/**
- * Inline menu of options.
- *
- * Inline menus provide a control for accessing a menu and compose a menu within the widget, which
+ * Dropdown menus provide a control for accessing a menu and compose a menu within the widget, which
* can be accessed using the #getMenu method.
*
* Use with OO.ui.MenuItemWidget.
* @param {Object} [config] Configuration options
* @cfg {Object} [menu] Configuration options to pass to menu widget
*/
-OO.ui.InlineMenuWidget = function OoUiInlineMenuWidget( config ) {
+OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
// Configuration initialization
config = $.extend( { indicator: 'down' }, config );
// Parent constructor
- OO.ui.InlineMenuWidget.super.call( this, config );
+ OO.ui.DropdownWidget.super.call( this, config );
// Mixin constructors
OO.ui.IconElement.call( this, config );
// Initialization
this.$handle
- .addClass( 'oo-ui-inlineMenuWidget-handle' )
+ .addClass( 'oo-ui-dropdownWidget-handle' )
.append( this.$icon, this.$label, this.$indicator );
this.$element
- .addClass( 'oo-ui-inlineMenuWidget' )
+ .addClass( 'oo-ui-dropdownWidget' )
.append( this.$handle, this.menu.$element );
};
/* Setup */
-OO.inheritClass( OO.ui.InlineMenuWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.IconElement );
-OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.IndicatorElement );
-OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.LabelElement );
-OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.TitledElement );
+OO.inheritClass( OO.ui.DropdownWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.TitledElement );
/* Methods */
*
* @return {OO.ui.MenuWidget} Menu of widget
*/
-OO.ui.InlineMenuWidget.prototype.getMenu = function () {
+OO.ui.DropdownWidget.prototype.getMenu = function () {
return this.menu;
};
*
* @param {OO.ui.MenuItemWidget} item Selected menu item
*/
-OO.ui.InlineMenuWidget.prototype.onMenuSelect = function ( item ) {
+OO.ui.DropdownWidget.prototype.onMenuSelect = function ( item ) {
var selectedLabel;
if ( !item ) {
*
* @param {jQuery.Event} e Mouse click event
*/
-OO.ui.InlineMenuWidget.prototype.onClick = function ( e ) {
+OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
// Skip clicks within the menu
if ( $.contains( this.menu.$element[0], e.target ) ) {
return;
return false;
};
+/**
+ * Icon widget.
+ *
+ * See OO.ui.IconElement for more information.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.IconWidget = function OoUiIconWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.IconWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.IconElement.call( this, $.extend( {}, config, { $icon: this.$element } ) );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-iconWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IconWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.TitledElement );
+
+/* Static Properties */
+
+OO.ui.IconWidget.static.tagName = 'span';
+
+/**
+ * Indicator widget.
+ *
+ * See OO.ui.IndicatorElement for more information.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.IndicatorWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$element } ) );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-indicatorWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IndicatorWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.TitledElement );
+
+/* Static Properties */
+
+OO.ui.IndicatorWidget.static.tagName = 'span';
+
/**
* Base class for input widgets.
*
* @param {Object} [config] Configuration options
* @cfg {string} [name=''] HTML input name
* @cfg {string} [value=''] Input value
- * @cfg {boolean} [readOnly=false] Prevent changes
* @cfg {Function} [inputFilter] Filter function to apply to the input. Takes a string argument and returns a string.
*/
OO.ui.InputWidget = function OoUiInputWidget( config ) {
- // Config intialization
- config = $.extend( { readOnly: false }, config );
+ // Configuration initialization
+ config = config || {};
// Parent constructor
OO.ui.InputWidget.super.call( this, config );
// Properties
this.$input = this.getInputElement( config );
this.value = '';
- this.readOnly = false;
this.inputFilter = config.inputFilter;
// Events
this.$input
.attr( 'name', config.name )
.prop( 'disabled', this.isDisabled() );
- this.setReadOnly( config.readOnly );
- this.$element.addClass( 'oo-ui-inputWidget' ).append( this.$input );
+ this.$element.addClass( 'oo-ui-inputWidget' ).append( this.$input, $( '<span>' ) );
this.setValue( config.value );
};
/**
* @event change
- * @param value
+ * @param {string} value
*/
/* Methods */
/**
* Get input element.
*
+ * @private
* @param {Object} [config] Configuration options
* @return {jQuery} Input element
*/
/**
* Sanitize incoming value.
*
- * Ensures value is a string, and converts undefined and null to empty strings.
+ * Ensures value is a string, and converts undefined and null to empty string.
*
+ * @private
* @param {string} value Original value
* @return {string} Sanitized value
*/
}
};
-/**
- * Check if the widget is read-only.
- *
- * @return {boolean}
- */
-OO.ui.InputWidget.prototype.isReadOnly = function () {
- return this.readOnly;
-};
-
-/**
- * Set the read-only state of the widget.
- *
- * This should probably change the widgets's appearance and prevent it from being used.
- *
- * @param {boolean} state Make input read-only
- * @chainable
- */
-OO.ui.InputWidget.prototype.setReadOnly = function ( state ) {
- this.readOnly = !!state;
- this.$input.prop( 'readOnly', this.readOnly );
- return this;
-};
-
/**
* @inheritdoc
*/
};
/**
- * A button that is an input widget. Intended to be used within FormLayouts.
+ * A button that is an input widget. Intended to be used within a OO.ui.FormLayout.
*
* @class
* @extends OO.ui.InputWidget
* @param {Object} [config] Configuration options
* @cfg {string} [type='button'] HTML tag `type` attribute, may be 'button', 'submit' or 'reset'
* @cfg {boolean} [useInputTag=false] Whether to use `<input/>` rather than `<button/>`. Only useful
- * if you need IE 6 support in a form with multiple buttons. By using this option, you sacrifice
- * icons and indicators, as well as the ability to have non-plaintext label or a label different
- * from the value.
+ * if you need IE 6 support in a form with multiple buttons. If you use this option, icons and
+ * indicators will not be displayed, it won't be possible to have a non-plaintext label, and it
+ * won't be possible to set a value (which will internally become identical to the label).
*/
OO.ui.ButtonInputWidget = function OoUiButtonInputWidget( config ) {
// Configuration initialization
config = $.extend( { type: 'button', useInputTag: false }, config );
+ // Properties (must be set before parent constructor, which calls #setValue)
+ this.useInputTag = config.useInputTag;
+
// Parent constructor
OO.ui.ButtonInputWidget.super.call( this, config );
OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$input } ) );
OO.ui.FlaggedElement.call( this, config );
- // Properties
- this.useInputTag = config.useInputTag;
-
// Events
this.$input.on( {
click: this.onClick.bind( this ),
/**
* Get input element.
*
+ * @private
* @param {Object} [config] Configuration options
* @return {jQuery} Input element
*/
OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
+ // Configuration intialization
+ config = config || {};
+
var html = '<' + ( config.useInputTag ? 'input' : 'button' ) + ' type="' + config.type + '">';
+
return this.$( html );
};
/**
- * Set the label.
+ * Set label value.
*
* Overridden to support setting the 'value' of `<input/>` elements.
*
return this;
};
+/**
+ * Set the value of the input.
+ *
+ * Overridden to disable for `<input/>` elements, which have value identical to the label.
+ *
+ * @param {string} value New value
+ * @chainable
+ */
+OO.ui.ButtonInputWidget.prototype.setValue = function ( value ) {
+ if ( !this.useInputTag ) {
+ OO.ui.ButtonInputWidget.super.prototype.setValue.call( this, value );
+ }
+ return this;
+};
+
/**
* Handles mouse click events.
*
/**
* Get input element.
*
+ * @private
* @return {jQuery} Input element
*/
OO.ui.CheckboxInputWidget.prototype.getInputElement = function () {
* @param {Object} [config] Configuration options
* @cfg {string} [type='text'] HTML tag `type` attribute
* @cfg {string} [placeholder] Placeholder text
+ * @cfg {boolean} [readOnly=false] Prevent changes
* @cfg {boolean} [multiline=false] Allow multiple lines of text
* @cfg {boolean} [autosize=false] Automatically resize to fit content
* @cfg {boolean} [maxRows=10] Maximum number of rows to make visible when autosizing
*/
OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
// Configuration initialization
- config = config || {};
+ config = $.extend( { readOnly: false }, config );
// Parent constructor
OO.ui.TextInputWidget.super.call( this, config );
OO.ui.PendingElement.call( this, config );
// Properties
+ this.readOnly = false;
this.multiline = !!config.multiline;
this.autosize = !!config.autosize;
this.maxRows = config.maxRows !== undefined ? config.maxRows : 10;
this.$element
.addClass( 'oo-ui-textInputWidget' )
.append( this.$icon, this.$indicator );
+ this.setReadOnly( config.readOnly );
if ( config.placeholder ) {
this.$input.attr( 'placeholder', config.placeholder );
}
return this;
};
+/**
+ * Check if the widget is read-only.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isReadOnly = function () {
+ return this.readOnly;
+};
+
+/**
+ * Set the read-only state of the widget.
+ *
+ * This should probably change the widgets's appearance and prevent it from being used.
+ *
+ * @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;
+};
+
/**
* Automatically adjust the size of the text input.
*
/**
* Get input element.
*
+ * @private
* @param {Object} [config] Configuration options
* @return {jQuery} Input element
*/
OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
+ // Configuration initialization
+ config = config || {};
+
var type = config.type || 'text';
+
return config.multiline ? this.$( '<textarea>' ) : this.$( '<input type="' + type + '" />' );
};
/**
* Sets the validation pattern to use.
- * @param validate {RegExp|string|null} Regular expression (or symbolic name referencing
+ * @param {RegExp|string|null} validate Regular expression (or symbolic name referencing
* one, see #static-validationPatterns)
*/
OO.ui.TextInputWidget.prototype.setValidation = function ( validate ) {
* @param {Object} [config] Configuration options
* @cfg {Object} [menu] Configuration options to pass to menu widget
* @cfg {Object} [input] Configuration options to pass to input widget
- * @cfg {jQuery} [$overlay] Overlay layer; defaults to the current window's overlay.
+ * @cfg {jQuery} [$overlay] Overlay layer; defaults to relative positioning
*/
OO.ui.ComboBoxWidget = function OoUiComboBoxWidget( config ) {
// Configuration initialization
OO.ui.ComboBoxWidget.super.call( this, config );
// Properties
- this.$overlay = config.$overlay || ( this.$.$iframe || this.$element ).closest( '.oo-ui-window' ).children( '.oo-ui-window-overlay' );
- if ( this.$overlay.length === 0 ) {
- this.$overlay = this.$( 'body' );
- }
+ this.$overlay = config.$overlay || this.$element;
this.input = new OO.ui.TextInputWidget( $.extend(
{ $: this.$, indicator: 'down', disabled: this.isDisabled() },
config.input
) );
this.menu = new OO.ui.TextInputMenuWidget( this.input, $.extend(
- { $: this.$, widget: this, input: this.input, disabled: this.isDisabled() },
+ {
+ $: OO.ui.Element.getJQuery( this.$overlay ),
+ widget: this,
+ input: this.input,
+ disabled: this.isDisabled()
+ },
config.menu
) );
* Handle menu item change events.
*/
OO.ui.ComboBoxWidget.prototype.onMenuItemsChange = function () {
+ var match = this.menu.getItemFromData( this.input.getValue() );
+ this.menu.selectItem( match );
this.$element.toggleClass( 'oo-ui-comboBoxWidget-empty', this.menu.isEmpty() );
};
* @param {Object} [config] Configuration options
*/
OO.ui.LabelWidget = function OoUiLabelWidget( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor
* @constructor
* @param {Mixed} data Option data
* @param {Object} [config] Configuration options
- * @cfg {string} [rel] Value for `rel` attribute in DOM, allowing per-option styling
*/
OO.ui.OptionWidget = function OoUiOptionWidget( data, config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor
// Initialization
this.$element
.data( 'oo-ui-optionWidget', this )
- .attr( 'rel', config.rel )
.attr( 'role', 'option' )
.addClass( 'oo-ui-optionWidget' )
.append( this.$label );
- this.$element
- .prepend( this.$icon )
- .append( this.$indicator );
};
/* Setup */
* @cfg {boolean} [movable] Allow modification from outline controls
*/
OO.ui.OutlineItemWidget = function OoUiOutlineItemWidget( data, config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor
* @cfg {boolean} [padded] Add padding to the body
*/
OO.ui.PopupWidget = function OoUiPopupWidget( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor
* @cfg {number} [progress=0] Initial progress
*/
OO.ui.ProgressBarWidget = function OoUiProgressBarWidget( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor
* @cfg {OO.ui.OptionWidget[]} [items] Options to add
*/
OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor
OO.ui.SelectWidget.prototype.getItemFromData = function ( data ) {
var hash = OO.getHash( data );
- if ( hash in this.hashes ) {
+ if ( Object.prototype.hasOwnProperty.call( this.hashes, hash ) ) {
return this.hashes[hash];
}
for ( i = 0, len = items.length; i < len; i++ ) {
item = items[i];
hash = OO.getHash( item.getData() );
- if ( hash in this.hashes ) {
+ if ( Object.prototype.hasOwnProperty.call( this.hashes, hash ) ) {
// Remove item with same value
remove.push( this.hashes[hash] );
}
for ( i = 0, len = items.length; i < len; i++ ) {
item = items[i];
hash = OO.getHash( item.getData() );
- if ( hash in this.hashes ) {
+ if ( Object.prototype.hasOwnProperty.call( this.hashes, hash ) ) {
// Remove existing item
delete this.hashes[hash];
}
* @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu
*/
OO.ui.MenuWidget = function OoUiMenuWidget( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor
* @cfg {jQuery} [$container=input.$element] Element to render menu under
*/
OO.ui.TextInputMenuWidget = function OoUiTextInputMenuWidget( input, config ) {
+ // Configuration intialization
+ config = config || {};
+
// Parent constructor
OO.ui.TextInputMenuWidget.super.call( this, config );
* @param {Object} [config] Configuration options
*/
OO.ui.OutlineWidget = function OoUiOutlineWidget( config ) {
- // Config intialization
+ // Configuration initialization
config = config || {};
// Parent constructor