/*!
- * OOjs UI v0.1.0-pre (30b0407428)
+ * OOjs UI v0.1.0-pre (0d358b167a)
* 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-09-30T23:08:05Z
+ * Date: 2014-10-17T23:41:06Z
*/
( function ( OO ) {
this.$ = config.$ || OO.ui.Element.getJQuery( document );
this.$element = this.$( this.$.context.createElement( this.getTagName() ) );
this.elementGroup = null;
- this.debouncedUpdateThemeClassesHandler = OO.ui.bind(
- this.debouncedUpdateThemeClasses, this
- );
+ this.debouncedUpdateThemeClassesHandler = this.debouncedUpdateThemeClasses.bind( this );
this.updateThemeClassesPending = false;
// Initialization
/**
* HTML tag name.
*
- * This may be ignored if getTagName is overridden.
+ * This may be ignored if #getTagName is overridden.
*
* @static
* @inheritable
/**
* Get the offset between two elements.
*
+ * The two elements may be in a different frame, but in that case the frame $element is in must
+ * be contained in the frame $anchor is in.
+ *
* @static
- * @param {jQuery} $from
- * @param {jQuery} $to
+ * @param {jQuery} $element Element whose position to get
+ * @param {jQuery} $anchor Element to get $element's position relative to
* @return {Object} Translated position coordinates, containing top and left properties
*/
-OO.ui.Element.getRelativePosition = function ( $from, $to ) {
- var from = $from.offset(),
- to = $to.offset();
- return { top: Math.round( from.top - to.top ), left: Math.round( from.left - to.left ) };
+OO.ui.Element.getRelativePosition = function ( $element, $anchor ) {
+ var iframe, iframePos,
+ pos = $element.offset(),
+ anchorPos = $anchor.offset(),
+ elementDocument = this.getDocument( $element ),
+ anchorDocument = this.getDocument( $anchor );
+
+ // If $element isn't in the same document as $anchor, traverse up
+ while ( elementDocument !== anchorDocument ) {
+ iframe = elementDocument.defaultView.frameElement;
+ if ( !iframe ) {
+ throw new Error( '$element frame is not contained in $anchor frame' );
+ }
+ iframePos = $( iframe ).offset();
+ pos.left += iframePos.left;
+ pos.top += iframePos.top;
+ elementDocument = iframe.ownerDocument;
+ }
+ pos.left -= anchorPos.left;
+ pos.top -= anchorPos.top;
+ return pos;
};
/**
}
};
+/**
+ * Bind a handler for an event on a DOM element.
+ *
+ * Used to be for working around a jQuery bug (jqbug.com/14180),
+ * but obsolete as of jQuery 1.11.0.
+ *
+ * @static
+ * @deprecated Use jQuery#on instead.
+ * @param {HTMLElement|jQuery} el DOM element
+ * @param {string} event Event to bind
+ * @param {Function} callback Callback to call when the event fires
+ */
+OO.ui.Element.onDOMEvent = function ( el, event, callback ) {
+ $( el ).on( event, callback );
+};
+
+/**
+ * Unbind a handler bound with #static-method-onDOMEvent.
+ *
+ * @deprecated Use jQuery#off instead.
+ * @static
+ * @param {HTMLElement|jQuery} el DOM element
+ * @param {string} event Event to unbind
+ * @param {Function} [callback] Callback to unbind
+ */
+OO.ui.Element.offDOMEvent = function ( el, event, callback ) {
+ $( el ).off( event, callback );
+};
+
/* Methods */
+/**
+ * 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
+ */
+OO.ui.Element.prototype.supports = function ( methods ) {
+ var i, len,
+ support = 0;
+
+ methods = $.isArray( methods ) ? methods : [ methods ];
+ for ( i = 0, len = methods.length; i < len; i++ ) {
+ if ( $.isFunction( this[methods[i]] ) ) {
+ support++;
+ }
+ }
+
+ return methods.length === support;
+};
+
/**
* Update the theme-provided classes.
*
OO.ui.Element.offDOMEvent( this.$element, event, callback );
};
-( function () {
- /**
- * Bind a handler for an event on a DOM element.
- *
- * Used to be for working around a jQuery bug (jqbug.com/14180),
- * but obsolete as of jQuery 1.11.0.
- *
- * @static
- * @deprecated Use jQuery#on instead.
- * @param {HTMLElement|jQuery} el DOM element
- * @param {string} event Event to bind
- * @param {Function} callback Callback to call when the event fires
- */
- OO.ui.Element.onDOMEvent = function ( el, event, callback ) {
- $( el ).on( event, callback );
- };
-
- /**
- * Unbind a handler bound with #static-method-onDOMEvent.
- *
- * @deprecated Use jQuery#off instead.
- * @static
- * @param {HTMLElement|jQuery} el DOM element
- * @param {string} event Event to unbind
- * @param {Function} [callback] Callback to unbind
- */
- OO.ui.Element.offDOMEvent = function ( el, event, callback ) {
- $( el ).off( event, callback );
- };
-}() );
-
/**
* Container for elements.
*
this.loading = null;
this.size = config.size || this.constructor.static.size;
this.$frame = this.$( '<div>' );
+ this.$overlay = this.$( '<div>' );
// Initialization
this.$element
.addClass( 'oo-ui-window' )
- .append( this.$frame );
+ .append( this.$frame, this.$overlay );
this.$frame.addClass( 'oo-ui-window-frame' );
+ this.$overlay.addClass( 'oo-ui-window-overlay' );
// NOTE: Additional intitialization will occur when #setManager is called
};
this.$head = this.$( '<div>' );
this.$body = this.$( '<div>' );
this.$foot = this.$( '<div>' );
- this.$overlay = this.$( '<div>' );
- this.$focusTrap = this.$( '<div>' ).prop( 'tabIndex', 0 );
+ this.$innerOverlay = this.$( '<div>' );
// Events
- this.$element.on( 'mousedown', OO.ui.bind( this.onMouseDown, this ) );
+ this.$element.on( 'mousedown', this.onMouseDown.bind( this ) );
// Initialization
this.$head.addClass( 'oo-ui-window-head' );
this.$body.addClass( 'oo-ui-window-body' );
this.$foot.addClass( 'oo-ui-window-foot' );
- this.$overlay.addClass( 'oo-ui-window-overlay' );
- this.$focusTrap.addClass( 'oo-ui-window-focustrap' );
- this.$content.append( this.$head, this.$body, this.$foot, this.$overlay, this.$focusTrap );
+ this.$innerOverlay.addClass( 'oo-ui-window-inner-overlay' );
+ this.$content.append( this.$head, this.$body, this.$foot, this.$innerOverlay );
return this;
};
-/**
- * Called when someone tries to focus the hidden element at the end of the dialog.
- * Sends focus back to the start of the dialog.
- */
-OO.ui.Window.prototype.onFocusTrapFocused = function () {
- this.$content.find( ':focusable:first' ).focus();
-};
-
/**
* Open window.
*
this.$element.show();
this.visible = true;
- this.focusTrapHandler = OO.ui.bind( this.onFocusTrapFocused, this );
- this.$focusTrap.on( 'focus', this.focusTrapHandler );
this.getSetupProcess( data ).execute().done( function () {
// Force redraw by asking the browser to measure the elements' widths
win.$element.addClass( 'oo-ui-window-setup' ).width();
var win = this,
deferred = $.Deferred();
- this.getTeardownProcess( data ).execute().done( OO.ui.bind( function () {
+ 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.$content.removeClass( 'oo-ui-window-content-setup' ).width();
win.$element.hide();
- this.$focusTrap.off( 'focus', this.focusTrapHandler );
win.visible = false;
deferred.resolve();
- }, this ) );
+ } );
return deferred.promise();
};
// Events
if ( this.constructor.static.escapable ) {
- this.$document.on( 'keydown', OO.ui.bind( this.onDocumentKeyDown, this ) );
+ this.$document.on( 'keydown', this.onDocumentKeyDown.bind( this ) );
}
// Initialization
OO.ui.Dialog.prototype.executeAction = function ( action ) {
this.pushPending();
return this.getActionProcess( action ).execute()
- .always( OO.ui.bind( this.popPending, this ) );
+ .always( this.popPending.bind( this ) );
};
/**
this.$ariaHidden = null;
this.requestedSize = null;
this.onWindowResizeTimeout = null;
- this.onWindowResizeHandler = OO.ui.bind( this.onWindowResize, this );
- this.afterWindowResizeHandler = OO.ui.bind( this.afterWindowResize, this );
- this.onWindowMouseWheelHandler = OO.ui.bind( this.onWindowMouseWheel, this );
- this.onDocumentKeyDownHandler = OO.ui.bind( this.onDocumentKeyDown, this );
+ this.onWindowResizeHandler = this.onWindowResize.bind( this );
+ this.afterWindowResizeHandler = this.afterWindowResize.bind( this );
+ this.onWindowMouseWheelHandler = this.onWindowMouseWheel.bind( this );
+ this.onDocumentKeyDownHandler = this.onDocumentKeyDown.bind( this );
// Initialization
this.$element
*
* @param {jQuery.Event} e Mouse wheel event
*/
-OO.ui.WindowManager.prototype.onWindowMouseWheel = function () {
- return false;
+OO.ui.WindowManager.prototype.onWindowMouseWheel = function ( e ) {
+ // Kill all events in the parent window if the child window is isolated,
+ // or if the event didn't come from the child window
+ return !( this.shouldIsolate() || !$.contains( this.getCurrentWindow().$frame[0], e.target ) );
};
/**
case OO.ui.Keys.UP:
case OO.ui.Keys.RIGHT:
case OO.ui.Keys.DOWN:
- // Prevent any key events that might cause scrolling
- return false;
+ // Kill all events in the parent window if the child window is isolated,
+ // or if the event didn't come from the child window
+ return !( this.shouldIsolate() || !$.contains( this.getCurrentWindow().$frame[0], e.target ) );
}
};
var manager = this,
preparing = [],
closing = $.Deferred(),
- opened = this.opened;
+ opened;
// Argument handling
if ( typeof win === 'string' ) {
manager.closing = closing;
manager.preparingToClose = null;
manager.emit( 'closing', win, closing, data );
+ opened = manager.opened;
manager.opened = null;
opened.resolve( closing.promise(), data );
setTimeout( function () {
if ( !win ) {
throw new Error( 'Cannot remove window' );
}
- promises.push( this.closeWindow( name ).then( OO.ui.bind( cleanup, null, name, win ) ) );
+ promises.push( this.closeWindow( name ).then( cleanup.bind( null, name, win ) ) );
}
return $.when.apply( $, promises );
this.tabIndex = null;
this.accessKey = null;
this.active = false;
- this.onMouseUpHandler = OO.ui.bind( this.onMouseUp, this );
- this.onMouseDownHandler = OO.ui.bind( this.onMouseDown, this );
+ this.onMouseUpHandler = this.onMouseUp.bind( this );
+ this.onMouseDownHandler = this.onMouseDown.bind( this );
// Initialization
this.$element.addClass( 'oo-ui-buttonElement' );
*
* Adding an existing item (by value) will move it.
*
- * @param {OO.ui.Element[]} items Item
+ * @param {OO.ui.Element[]} items Items
* @param {number} [index] Index to insert items at
* @chainable
*/
this.$clippableWindow = null;
this.idealWidth = null;
this.idealHeight = null;
- this.onClippableContainerScrollHandler = OO.ui.bind( this.clip, this );
- this.onClippableWindowResizeHandler = OO.ui.bind( this.clip, this );
+ this.onClippableContainerScrollHandler = this.clip.bind( this );
+ this.onClippableWindowResizeHandler = this.clip.bind( this );
// Initialization
this.setClippableElement( config.$clippable || this.$element );
// Events
this.$element
.add( this.$bar ).add( this.$group ).add( this.$actions )
- .on( 'mousedown touchstart', OO.ui.bind( this.onPointerDown, this ) );
+ .on( 'mousedown touchstart', this.onPointerDown.bind( this ) );
// Initialization
this.$group.addClass( 'oo-ui-toolbar-tools' );
group.type = 'list';
}
if ( group.label === undefined ) {
- group.label = 'ooui-toolbar-more';
+ group.label = OO.ui.msg( 'ooui-toolbar-more' );
}
}
// Check type has been registered
this.exclude = config.exclude || [];
this.promote = config.promote || [];
this.demote = config.demote || [];
- this.onCapturedMouseUpHandler = OO.ui.bind( this.onCapturedMouseUp, this );
+ this.onCapturedMouseUpHandler = this.onCapturedMouseUp.bind( this );
// Events
this.$element.on( {
- 'mousedown touchstart': OO.ui.bind( this.onPointerDown, this ),
- 'mouseup touchend': OO.ui.bind( this.onPointerUp, this ),
- mouseover: OO.ui.bind( this.onMouseOver, this ),
- mouseout: OO.ui.bind( this.onMouseOut, this )
+ 'mousedown touchstart': this.onPointerDown.bind( this ),
+ 'mouseup touchend': this.onPointerUp.bind( this ),
+ mouseover: this.onMouseOver.bind( this ),
+ mouseout: this.onMouseOut.bind( this )
} );
this.toolbar.getToolFactory().connect( this, { register: 'onToolFactoryRegister' } );
this.aggregate( { disable: 'itemDisable' } );
*/
OO.ui.ProcessDialog.prototype.executeAction = function ( action ) {
OO.ui.ProcessDialog.super.prototype.executeAction.call( this, action )
- .fail( OO.ui.bind( this.showErrors, this ) );
+ .fail( this.showErrors.bind( this ) );
};
/**
}
if ( this.autoFocus ) {
// Event 'focus' does not bubble, but 'focusin' does
- this.stackLayout.onDOMEvent( 'focusin', OO.ui.bind( this.onStackLayoutFocus, this ) );
+ this.stackLayout.onDOMEvent( 'focusin', this.onStackLayoutFocus.bind( this ) );
}
// Initialization
* scanning for a specific label in a form with many fields
* - right: Label is before the field and aligned toward it, best for forms the user is very
* familiar with and will tab through field checking quickly to verify which field they are in
- * - top: Label is before the field and above it, best for when the use will need to fill out all
+ * - top: Label is before the field and above it, best for when the user will need to fill out all
* fields from top to bottom in a form with few fields
* - inline: Label is after the field and aligned toward it, best for small boolean fields like
* checkboxes or radio buttons
*
* @constructor
- * @param {OO.ui.Widget} field Field widget
+ * @param {OO.ui.Widget} fieldWidget Field widget
* @param {Object} [config] Configuration options
* @cfg {string} [align='left'] Alignment mode, either 'left', 'right', 'top' or 'inline'
* @cfg {string} [help] Explanatory text shown as a '?' icon.
*/
-OO.ui.FieldLayout = function OoUiFieldLayout( field, config ) {
+OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
// Config initialization
config = $.extend( { align: 'left' }, config );
// Properties
this.$field = this.$( '<div>' );
- this.field = field;
+ this.fieldWidget = fieldWidget;
this.align = null;
if ( config.help ) {
this.popupButtonWidget = new OO.ui.PopupButtonWidget( {
}
// Events
- if ( this.field instanceof OO.ui.InputWidget ) {
- this.$label.on( 'click', OO.ui.bind( this.onLabelClick, this ) );
+ if ( this.fieldWidget instanceof OO.ui.InputWidget ) {
+ this.$label.on( 'click', this.onLabelClick.bind( this ) );
}
- this.field.connect( this, { disable: 'onFieldDisable' } );
+ this.fieldWidget.connect( this, { disable: 'onFieldDisable' } );
// Initialization
this.$element.addClass( 'oo-ui-fieldLayout' );
this.$field
.addClass( 'oo-ui-fieldLayout-field' )
- .toggleClass( 'oo-ui-fieldLayout-disable', this.field.isDisabled() )
- .append( this.field.$element );
+ .toggleClass( 'oo-ui-fieldLayout-disable', this.fieldWidget.isDisabled() )
+ .append( this.fieldWidget.$element );
this.setAlignment( config.align );
};
* @param {jQuery.Event} e Mouse click event
*/
OO.ui.FieldLayout.prototype.onLabelClick = function () {
- this.field.simulateLabelClick();
+ this.fieldWidget.simulateLabelClick();
return false;
};
* @return {OO.ui.Widget} Field widget
*/
OO.ui.FieldLayout.prototype.getField = function () {
- return this.field;
+ return this.fieldWidget;
};
/**
} else {
this.$element.append( this.$help, this.$label, this.$field );
}
- // Set classes
+ // Set classes. The following classes can be used here:
+ // * oo-ui-fieldLayout-align-left
+ // * oo-ui-fieldLayout-align-right
+ // * oo-ui-fieldLayout-align-top
+ // * oo-ui-fieldLayout-align-inline
if ( this.align ) {
this.$element.removeClass( 'oo-ui-fieldLayout-align-' + this.align );
}
+ this.$element.addClass( 'oo-ui-fieldLayout-align-' + value );
this.align = value;
- // The following classes can be used here:
- // oo-ui-fieldLayout-align-left
- // oo-ui-fieldLayout-align-right
- // oo-ui-fieldLayout-align-top
- // oo-ui-fieldLayout-align-inline
- this.$element.addClass( 'oo-ui-fieldLayout-align-' + this.align );
}
return this;
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [icon] Symbolic icon name
* @cfg {OO.ui.FieldLayout[]} [items] Items to add
*/
OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.LabelElement );
OO.mixinClass( OO.ui.FieldsetLayout, OO.ui.GroupElement );
-/* Static Properties */
-
-OO.ui.FieldsetLayout.static.tagName = 'div';
-
/**
* Layout with an HTML form.
*
OO.ui.FormLayout.super.call( this, config );
// Events
- this.$element.on( 'submit', OO.ui.bind( this.onFormSubmit, this ) );
+ this.$element.on( 'submit', this.onFormSubmit.bind( this ) );
// Initialization
this.$element.addClass( 'oo-ui-formLayout' );
* @param {OO.ui.PanelLayout[]} panels Panels in the grid
* @param {Object} [config] Configuration options
* @cfg {number[]} [widths] Widths of columns as ratios
- * @cfg {number[]} [heights] Heights of columns as ratios
+ * @cfg {number[]} [heights] Heights of rows as ratios
*/
OO.ui.GridLayout = function OoUiGridLayout( panels, config ) {
var i, len, widths;
this.layout( config.widths || [ 1 ], config.heights || [ 1 ] );
} else {
// Arrange in columns by default
- widths = [];
- for ( i = 0, len = this.panels.length; i < len; i++ ) {
- widths[i] = 1;
- }
+ widths = this.panels.map( function () { return 1; } );
this.layout( widths, [ 1 ] );
}
};
* @event update
*/
-/* Static Properties */
-
-OO.ui.GridLayout.static.tagName = 'div';
-
/* Methods */
/**
* @fires update
*/
OO.ui.GridLayout.prototype.update = function () {
- var x, y, panel,
+ var x, y, panel, width, height, dimensions,
i = 0,
- left = 0,
top = 0,
- dimensions,
- width = 0,
- height = 0,
+ left = 0,
cols = this.widths.length,
rows = this.heights.length;
for ( y = 0; y < rows; y++ ) {
height = this.heights[y];
for ( x = 0; x < cols; x++ ) {
- panel = this.panels[i];
width = this.widths[x];
+ panel = this.panels[i];
dimensions = {
width: Math.round( width * 100 ) + '%',
height: Math.round( height * 100 ) + '%',
- top: Math.round( top * 100 ) + '%',
- // HACK: Work around IE bug by setting visibility: hidden; if width or height is zero
- visibility: width === 0 || height === 0 ? 'hidden' : ''
+ top: Math.round( top * 100 ) + '%'
};
// If RTL, reverse:
if ( OO.ui.Element.getDir( this.$.context ) === 'rtl' ) {
} else {
dimensions.left = Math.round( left * 100 ) + '%';
}
+ // HACK: Work around IE bug by setting visibility: hidden; if width or height is zero
+ if ( width === 0 || height === 0 ) {
+ dimensions.visibility = 'hidden';
+ }
panel.$element.css( dimensions );
i++;
left += width;
* @return {OO.ui.PanelLayout} The panel at the given postion
*/
OO.ui.GridLayout.prototype.getPanel = function ( x, y ) {
- return this.panels[( x * this.widths.length ) + y];
+ return this.panels[ ( x * this.widths.length ) + y ];
};
/**
*/
OO.ui.PanelLayout = function OoUiPanelLayout( config ) {
// Config initialization
- config = config || {};
+ config = $.extend( {
+ scrollable: false,
+ padded: false,
+ expanded: true
+ }, config );
// Parent constructor
OO.ui.PanelLayout.super.call( this, config );
if ( config.scrollable ) {
this.$element.addClass( 'oo-ui-panelLayout-scrollable' );
}
-
if ( config.padded ) {
this.$element.addClass( 'oo-ui-panelLayout-padded' );
}
-
- if ( config.expanded === undefined || config.expanded ) {
+ if ( config.expanded ) {
this.$element.addClass( 'oo-ui-panelLayout-expanded' );
}
};
// Properties
this.active = false;
this.dragging = false;
- this.onBlurHandler = OO.ui.bind( this.onBlur, this );
+ this.onBlurHandler = this.onBlur.bind( this );
this.$handle = this.$( '<span>' );
// Events
this.$handle.on( {
- 'mousedown touchstart': OO.ui.bind( this.onHandlePointerDown, this ),
- 'mouseup touchend': OO.ui.bind( this.onHandlePointerUp, this )
+ 'mousedown touchstart': this.onHandlePointerDown.bind( this ),
+ 'mouseup touchend': this.onHandlePointerUp.bind( this )
} );
// Initialization
* @constructor
* @param {OO.ui.TextInputWidget} input Input widget
* @param {Object} [config] Configuration options
- * @cfg {jQuery} [$overlay=this.$( 'body' )] Overlay layer
+ * @cfg {jQuery} [$overlay] Overlay layer; defaults to the current window's overlay.
*/
OO.ui.LookupInputWidget = function OoUiLookupInputWidget( input, config ) {
// Config intialization
// Properties
this.lookupInput = input;
- this.$overlay = config.$overlay || this.$( 'body,.oo-ui-window-overlay' ).last();
+ 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.lookupMenu = new OO.ui.TextInputMenuWidget( this, {
$: OO.ui.Element.getJQuery( this.$overlay ),
input: this.lookupInput,
this.$overlay.append( this.lookupMenu.$element );
this.lookupInput.$input.on( {
- focus: OO.ui.bind( this.onLookupInputFocus, this ),
- blur: OO.ui.bind( this.onLookupInputBlur, this ),
- mousedown: OO.ui.bind( this.onLookupInputMouseDown, this )
+ focus: this.onLookupInputFocus.bind( this ),
+ blur: this.onLookupInputBlur.bind( this ),
+ mousedown: this.onLookupInputMouseDown.bind( this )
} );
this.lookupInput.connect( this, { change: 'onLookupInputChange' } );
OO.ui.IconElement.call( this, config );
OO.ui.IndicatorElement.call( this, config );
OO.ui.LabelElement.call( this, config );
- OO.ui.TitledElement.call( this, config, $.extend( {}, config, { $titled: this.$button } ) );
+ OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) );
OO.ui.FlaggedElement.call( this, config );
// Properties
// Events
this.$button.on( {
- click: OO.ui.bind( this.onClick, this ),
- keypress: OO.ui.bind( this.onKeyPress, this )
+ click: this.onClick.bind( this ),
+ keypress: this.onKeyPress.bind( this )
} );
// Initialization
* Inline 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.MenuOptionWidget.
+ * Use with OO.ui.MenuItemWidget.
*
* @class
* @extends OO.ui.Widget
this.$handle = this.$( '<span>' );
// Events
- this.$element.on( { click: OO.ui.bind( this.onClick, this ) } );
+ this.$element.on( { click: this.onClick.bind( this ) } );
this.menu.connect( this, { select: 'onMenuSelect' } );
// Initialization
this.inputFilter = config.inputFilter;
// Events
- this.$input.on( 'keydown mouseup cut paste change input select', OO.ui.bind( this.onEdit, this ) );
+ this.$input.on( 'keydown mouseup cut paste change input select', this.onEdit.bind( this ) );
// Initialization
this.$input
OO.inheritClass( OO.ui.CheckboxInputWidget, OO.ui.InputWidget );
-/* Events */
-
/* Methods */
/**
};
/**
- * Set value
+ * Set checked state of the checkbox
+ *
+ * @param {boolean} value New value
*/
OO.ui.CheckboxInputWidget.prototype.setValue = function ( value ) {
value = !!value;
// Events
this.$input.on( {
- keypress: OO.ui.bind( this.onKeyPress, this ),
- blur: OO.ui.bind( this.setValidityFlag, this )
+ keypress: this.onKeyPress.bind( this ),
+ blur: this.setValidityFlag.bind( this )
} );
- this.$element.on( 'DOMNodeInsertedIntoDocument', OO.ui.bind( this.onElementAttach, this ) );
- this.$icon.on( 'mousedown', OO.ui.bind( this.onIconMouseDown, this ) );
- this.$indicator.on( 'mousedown', OO.ui.bind( this.onIndicatorMouseDown, this ) );
+ this.$element.on( 'DOMNodeInsertedIntoDocument', this.onElementAttach.bind( this ) );
+ this.$icon.on( 'mousedown', this.onIconMouseDown.bind( this ) );
+ this.$indicator.on( 'mousedown', this.onIndicatorMouseDown.bind( this ) );
// Initialization
this.$element
* Sets the 'invalid' flag appropriately.
*/
OO.ui.TextInputWidget.prototype.setValidityFlag = function () {
- this.isValid().done( OO.ui.bind( function ( valid ) {
- this.setFlags( { invalid: !valid } );
- }, this ) );
+ var widget = this;
+ this.isValid().done( function ( valid ) {
+ widget.setFlags( { invalid: !valid } );
+ } );
};
/**
* @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.
*/
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.input = new OO.ui.TextInputWidget( $.extend(
{ $: this.$, indicator: 'down', disabled: this.isDisabled() },
config.input
) );
- this.menu = new OO.ui.MenuWidget( $.extend(
+ this.menu = new OO.ui.TextInputMenuWidget( this.input, $.extend(
{ $: this.$, widget: this, input: this.input, disabled: this.isDisabled() },
config.menu
) );
} );
// Initialization
- this.$element.addClass( 'oo-ui-comboBoxWidget' ).append(
- this.input.$element,
- this.menu.$element
- );
+ this.$element.addClass( 'oo-ui-comboBoxWidget' ).append( this.input.$element );
+ this.$overlay.append( this.menu.$element );
this.onMenuItemsChange();
};
// Mixin constructors
OO.ui.LabelElement.call( this, $.extend( {}, config, { $label: this.$element } ) );
+ OO.ui.TitledElement.call( this, config );
// Properties
this.input = config.input;
// Events
if ( this.input instanceof OO.ui.InputWidget ) {
- this.$element.on( 'click', OO.ui.bind( this.onClick, this ) );
+ this.$element.on( 'click', this.onClick.bind( this ) );
}
// Initialization
OO.inheritClass( OO.ui.LabelWidget, OO.ui.Widget );
OO.mixinClass( OO.ui.LabelWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.LabelWidget, OO.ui.TitledElement );
/* Static Properties */
this.height = config.height !== undefined ? config.height : null;
this.align = config.align || 'center';
this.closeButton = new OO.ui.ButtonWidget( { $: this.$, framed: false, icon: 'close' } );
- this.onMouseDownHandler = OO.ui.bind( this.onMouseDown, this );
+ this.onMouseDownHandler = this.onMouseDown.bind( this );
// Events
this.closeButton.connect( this, { click: 'onCloseButtonClick' } );
highlight: 'onResultsHighlight',
select: 'onResultsSelect'
} );
- this.query.$input.on( 'keydown', OO.ui.bind( this.onQueryKeydown, this ) );
+ this.query.$input.on( 'keydown', this.onQueryKeydown.bind( this ) );
// Initialization
this.$query
/**
* Generic selection of options.
*
- * Items can contain any rendering, and are uniquely identified by a has of thier data. Any widget
+ * Items can contain any rendering, and are uniquely identified by a hash of their data. Any widget
* that provides options, from which the user must choose one, should be built on this class.
*
* Use together with OO.ui.OptionWidget.
this.pressed = false;
this.selecting = null;
this.hashes = {};
- this.onMouseUpHandler = OO.ui.bind( this.onMouseUp, this );
- this.onMouseMoveHandler = OO.ui.bind( this.onMouseMove, this );
+ this.onMouseUpHandler = this.onMouseUp.bind( this );
+ this.onMouseMoveHandler = this.onMouseMove.bind( this );
// Events
this.$element.on( {
- mousedown: OO.ui.bind( this.onMouseDown, this ),
- mouseover: OO.ui.bind( this.onMouseOver, this ),
- mouseleave: OO.ui.bind( this.onMouseLeave, this )
+ mousedown: this.onMouseDown.bind( this ),
+ mouseover: this.onMouseOver.bind( this ),
+ mouseleave: this.onMouseLeave.bind( this )
} );
// Initialization
this.$widget = config.widget ? config.widget.$element : null;
this.$previousFocus = null;
this.isolated = !config.input;
- this.onKeyDownHandler = OO.ui.bind( this.onKeyDown, this );
- this.onDocumentMouseDownHandler = OO.ui.bind( this.onDocumentMouseDown, this );
+ this.onKeyDownHandler = this.onKeyDown.bind( this );
+ this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this );
// Initialization
this.$element
visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length;
var i, len,
- change = visible !== this.isVisible();
+ change = visible !== this.isVisible(),
+ elementDoc = this.getElementDocument(),
+ widgetDoc = this.$widget ? this.$widget[0].ownerDocument : null;
// Parent method
OO.ui.MenuWidget.super.prototype.toggle.call( this, visible );
// Auto-hide
if ( this.autoHide ) {
- this.getElementDocument().addEventListener(
+ elementDoc.addEventListener(
'mousedown', this.onDocumentMouseDownHandler, true
);
+ // Support $widget being in a different document
+ if ( widgetDoc && widgetDoc !== elementDoc ) {
+ widgetDoc.addEventListener(
+ 'mousedown', this.onDocumentMouseDownHandler, true
+ );
+ }
}
} else {
this.unbindKeyDownListener();
this.$previousFocus[0].focus();
this.$previousFocus = null;
}
- this.getElementDocument().removeEventListener(
+ elementDoc.removeEventListener(
'mousedown', this.onDocumentMouseDownHandler, true
);
+ // Support $widget being in a different document
+ if ( widgetDoc && widgetDoc !== elementDoc ) {
+ widgetDoc.removeEventListener(
+ 'mousedown', this.onDocumentMouseDownHandler, true
+ );
+ }
this.toggleClipping( false );
}
}
* Menu for a text input widget.
*
* This menu is specially designed to be positioned beneath the text input widget. Even if the input
- * is in a different frame, the menu's position is automatically calulated and maintained when the
+ * is in a different frame, the menu's position is automatically calculated and maintained when the
* menu is toggled or the window is resized.
*
* @class
// Properties
this.input = input;
this.$container = config.$container || this.input.$element;
- this.onWindowResizeHandler = OO.ui.bind( this.onWindowResize, this );
+ this.onWindowResizeHandler = this.onWindowResize.bind( this );
// Initialization
this.$element.addClass( 'oo-ui-textInputMenuWidget' );
* @inheritdoc
*/
OO.ui.TextInputMenuWidget.prototype.toggle = function ( visible ) {
- visible = !!visible;
+ visible = visible === undefined ? !this.isVisible() : !!visible;
var change = visible !== this.isVisible();
* @chainable
*/
OO.ui.TextInputMenuWidget.prototype.position = function () {
- var frameOffset,
- $container = this.$container,
- dimensions = $container.offset();
+ var $container = this.$container,
+ pos = OO.ui.Element.getRelativePosition( $container, this.$element.offsetParent() );
// Position under input
- dimensions.top += $container.height();
+ pos.top += $container.height();
+ this.$element.css( pos );
- // Compensate for frame position if in a differnt frame
- if ( this.input.$.$iframe && this.input.$.context !== this.$element[0].ownerDocument ) {
- frameOffset = OO.ui.Element.getRelativePosition(
- this.input.$.$iframe, this.$element.offsetParent()
- );
- dimensions.left += frameOffset.left;
- dimensions.top += frameOffset.top;
- } else {
- // Fix for RTL (for some reason, no need to fix if the frameoffset is set)
- if ( this.$element.css( 'direction' ) === 'rtl' ) {
- dimensions.right = this.$element.parent().position().left -
- $container.width() - dimensions.left;
- // Erase the value for 'left':
- delete dimensions.left;
- }
- }
- this.$element.css( dimensions );
+ // Set width
this.setIdealSize( $container.width() );
// We updated the position, so re-evaluate the clipping state
this.clip();
this.$grip = this.$( '<span>' );
// Events
- this.$element.on( 'click', OO.ui.bind( this.onClick, this ) );
+ this.$element.on( 'click', this.onClick.bind( this ) );
// Initialization
this.$glow.addClass( 'oo-ui-toggleSwitchWidget-glow' );