/*!
- * OOjs UI v0.9.0
+ * OOjs UI v0.9.1
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2015 OOjs Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2015-03-04T23:55:34Z
+ * Date: 2015-03-12T19:08:47Z
*/
( function ( OO ) {
};
/**
- * ActionSets manage the behavior of the {@link OO.ui.ActionWidget Action widgets} that comprise them.
+ * ActionSets manage the behavior of the {@link OO.ui.ActionWidget action widgets} that comprise them.
* Actions can be made available for specific contexts (modes) and circumstances
- * (abilities). Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ * (abilities). Action sets are primarily used with {@link OO.ui.Dialog Dialogs}.
+ *
+ * ActionSets contain two types of actions:
+ *
+ * - Special: Special actions are the first visible actions with special flags, such as 'safe' and 'primary', the default special flags. Additional special flags can be configured in subclasses with the static #specialFlags property.
+ * - Other: Other actions include all non-special visible actions.
+ *
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
*
* @example
* // Example: An action set used in a process dialog
/**
* @event click
+ *
+ * A 'click' event is emitted when an action is clicked.
+ *
* @param {OO.ui.ActionWidget} action Action that was clicked
*/
/**
* @event resize
+ *
+ * A 'resize' event is emitted when an action widget is resized.
+ *
* @param {OO.ui.ActionWidget} action Action that was resized
*/
/**
* @event add
+ *
+ * An 'add' event is emitted when actions are {@link #method-add added} to the action set.
+ *
* @param {OO.ui.ActionWidget[]} added Actions added
*/
/**
* @event remove
+ *
+ * A 'remove' event is emitted when actions are {@link #method-remove removed}
+ * or {@link #clear cleared}.
+ *
* @param {OO.ui.ActionWidget[]} added Actions removed
*/
/**
* @event change
+ *
+ * A 'change' event is emitted when actions are {@link #method-add added}, {@link #clear cleared},
+ * or {@link #method-remove removed} from the action set or when the {@link #setMode mode} is changed.
+ *
*/
/* Methods */
};
/**
- * Check if a action is one of the special actions.
+ * Check if an action is one of the special actions.
*
* @param {OO.ui.ActionWidget} action Action to check
* @return {boolean} Action is special
};
/**
- * Get actions.
+ * Get action widgets based on the specified filter: ‘actions’, ‘flags’, ‘modes’, ‘visible’,
+ * or ‘disabled’.
*
* @param {Object} [filters] Filters to use, omit to get all actions
- * @param {string|string[]} [filters.actions] Actions that actions must have
- * @param {string|string[]} [filters.flags] Flags that actions must have
- * @param {string|string[]} [filters.modes] Modes that actions must have
- * @param {boolean} [filters.visible] Actions must be visible
- * @param {boolean} [filters.disabled] Actions must be disabled
- * @return {OO.ui.ActionWidget[]} Actions matching all criteria
+ * @param {string|string[]} [filters.actions] Actions that action widgets must have
+ * @param {string|string[]} [filters.flags] Flags that action widgets must have (e.g., 'safe')
+ * @param {string|string[]} [filters.modes] Modes that action widgets must have
+ * @param {boolean} [filters.visible] Action widgets must be visible
+ * @param {boolean} [filters.disabled] Action widgets must be disabled
+ * @return {OO.ui.ActionWidget[]} Action widgets matching all criteria
*/
OO.ui.ActionSet.prototype.get = function ( filters ) {
var i, len, list, category, actions, index, match, matches;
};
/**
- * Get special actions.
+ * Get 'special' actions.
*
- * Special actions are the first visible actions with special flags, such as 'safe' and 'primary'.
- * Special flags can be configured by changing #static-specialFlags in a subclass.
+ * Special actions are the first visible action widgets with special flags, such as 'safe' and 'primary'.
+ * Special flags can be configured in subclasses by changing the static #specialFlags property.
*
- * @return {OO.ui.ActionWidget|null} Safe action
+ * @return {OO.ui.ActionWidget[]|null} 'Special' action widgets.
*/
OO.ui.ActionSet.prototype.getSpecial = function () {
this.organize();
};
/**
- * Get other actions.
+ * Get 'other' actions.
*
- * Other actions include all non-special visible actions.
+ * Other actions include all non-special visible action widgets.
*
- * @return {OO.ui.ActionWidget[]} Other actions
+ * @return {OO.ui.ActionWidget[]} 'Other' action widgets
*/
OO.ui.ActionSet.prototype.getOthers = function () {
this.organize();
};
/**
- * Toggle actions based on their modes.
- *
- * Unlike calling toggle on actions with matching flags, this will enforce mutually exclusive
- * visibility; matching actions will be shown, non-matching actions will be hidden.
+ * Set the mode (e.g., ‘edit’ or ‘view’). Only {@link OO.ui.ActionWidget#modes actions} configured
+ * to be available in the specified mode will be made visible. All other actions will be hidden.
*
- * @param {string} mode Mode actions must have
+ * @param {string} mode The mode. Only actions configured to be available in the specified
+ * mode will be made visible.
* @chainable
* @fires toggle
* @fires change
};
/**
- * Change which actions are able to be performed.
+ * Set the abilities of the specified actions.
*
- * Actions with matching actions will be disabled/enabled. Other actions will not be changed.
+ * Action widgets that are configured with the specified actions will be enabled
+ * or disabled based on the boolean values specified in the `actions`
+ * parameter.
*
- * @param {Object.<string,boolean>} actions List of abilities, keyed by action name, values
- * indicate actions are able to be performed
+ * @param {Object.<string,boolean>} actions A list keyed by action name with boolean
+ * values that indicate whether or not the action should be enabled.
* @chainable
*/
OO.ui.ActionSet.prototype.setAbilities = function ( actions ) {
* Executes a function once per action.
*
* When making changes to multiple actions, use this method instead of iterating over the actions
- * manually to defer emitting a change event until after all actions have been changed.
+ * manually to defer emitting a #change event until after all actions have been changed.
*
- * @param {Object|null} actions Filters to use for which actions to iterate over; see #get
+ * @param {Object|null} actions Filters to use to determine which actions to iterate over; see #get
* @param {Function} callback Callback to run for each action; callback is invoked with three
* arguments: the action, the action's index, the list of actions being iterated over
* @chainable
};
/**
- * Add actions.
+ * Add action widgets to the action set.
*
- * @param {OO.ui.ActionWidget[]} actions Actions to add
+ * @param {OO.ui.ActionWidget[]} actions Action widgets to add
* @chainable
* @fires add
* @fires change
};
/**
- * Remove actions.
+ * Remove action widgets from the set.
*
- * @param {OO.ui.ActionWidget[]} actions Actions to remove
+ * To remove all actions, you may wish to use the #clear method instead.
+ *
+ * @param {OO.ui.ActionWidget[]} actions Action widgets to remove
* @chainable
* @fires remove
* @fires change
};
/**
- * Remove all actions.
+ * Remove all action widets from the set.
+ *
+ * To remove only specified actions, use the {@link #method-remove remove} method instead.
*
* @chainable
* @fires remove
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [size] Symbolic name of dialog size, `small`, `medium`, `large`, `larger` or
- * `full`; omit to use #static-size
+ * @cfg {string} [size] Symbolic name of the dialog size: `small`, `medium`, `large`, `larger` or
+ * `full`. If omitted, the value of the {@link #static-size static size} property will be used.
*/
OO.ui.Window = function OoUiWindow( config ) {
// Configuration initialization
// Properties
this.manager = null;
this.size = config.size || this.constructor.static.size;
- this.$frame = $( '<div>' );
+ this.frame = new OO.ui.PanelLayout( {
+ expanded: false,
+ framed: true
+ } );
+ this.$frame = this.frame.$element;
this.$overlay = $( '<div>' );
this.$content = $( '<div>' );
/* Static Properties */
/**
- * Symbolic name of size.
+ * Symbolic name of the window size: `small`, `medium`, `large`, `larger` or `full`.
*
- * Size is used if no size is configured during construction.
+ * The static size is used if no #size is configured during construction.
*
* @static
* @inheritable
/**
* Handle mouse down events.
*
+ * @private
* @param {jQuery.Event} e Mouse down event
*/
OO.ui.Window.prototype.onMouseDown = function ( e ) {
};
/**
- * Check if window has been initialized.
+ * Check if the window has been initialized.
*
* Initialization occurs when a window is added to a manager.
*
};
/**
- * Check if window is visible.
+ * Check if the window is visible.
*
* @return {boolean} Window is visible
*/
};
/**
- * Check if window is opening.
+ * Check if the window is opening.
*
- * This is a wrapper around OO.ui.WindowManager#isOpening.
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isOpening isOpening}
+ * method.
*
* @return {boolean} Window is opening
*/
};
/**
- * Check if window is closing.
+ * Check if the window is closing.
*
- * This is a wrapper around OO.ui.WindowManager#isClosing.
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isClosing isClosing} method.
*
* @return {boolean} Window is closing
*/
};
/**
- * Check if window is opened.
+ * Check if the window is opened.
*
- * This is a wrapper around OO.ui.WindowManager#isOpened.
+ * This method is a wrapper around the window manager's {@link OO.ui.WindowManager#isOpened isOpened} method.
*
* @return {boolean} Window is opened
*/
/**
* Get the window manager.
*
+ * All windows must be attached to a window manager, which is used to open
+ * and close the window and control its presentation.
+ *
* @return {OO.ui.WindowManager} Manager of window
*/
OO.ui.Window.prototype.getManager = function () {
};
/**
- * Get the window size.
+ * Get the symbolic name of the window size (e.g., `small` or `medium`).
*
- * @return {string} Symbolic size name, e.g. `small`, `medium`, `large`, `larger`, `full`
+ * @return {string} Symbolic name of the size: `small`, `medium`, `large`, `larger`, `full`
*/
OO.ui.Window.prototype.getSize = function () {
return this.size;
};
/**
- * Get the height of the dialog contents.
+ * Get the height of the full window contents (i.e., the window head, body and foot together).
+ *
+ * What consistitutes the head, body, and foot varies depending on the window type.
+ * A {@link OO.ui.MessageDialog message dialog} displays a title and message in its body,
+ * and any actions in the foot. A {@link OO.ui.ProcessDialog process dialog} displays a title
+ * and special actions in the head, and dialog content in the body.
+ *
+ * To get just the height of the dialog body, use the #getBodyHeight method.
*
- * @return {number} Content height
+ * @return {number} The height of the window contents (the dialog head, body and foot) in pixels
*/
OO.ui.Window.prototype.getContentHeight = function () {
var bodyHeight,
};
/**
- * Get the height of the dialog contents.
+ * Get the height of the window body.
*
- * When this function is called, the dialog will temporarily have been resized
+ * To get the height of the full window contents (the window body, head, and foot together),
+ * use #getContentHeight.
+ *
+ * When this function is called, the window will temporarily have been resized
* to height=1px, so .scrollHeight measurements can be taken accurately.
*
- * @return {number} Height of content
+ * @return {number} Height of the window body in pixels
*/
OO.ui.Window.prototype.getBodyHeight = function () {
return this.$body[ 0 ].scrollHeight;
};
/**
- * Get the directionality of the frame
+ * Get the directionality of the frame (right-to-left or left-to-right).
*
- * @return {string} Directionality, 'ltr' or 'rtl'
+ * @return {string} Directionality: `'ltr'` or `'rtl'`
*/
OO.ui.Window.prototype.getDir = function () {
return this.dir;
};
/**
- * Get a process for setting up a window for use.
+ * Get the 'setup' process.
+ *
+ * The setup process is used to set up a window for use in a particular context,
+ * based on the `data` argument. This method is called during the opening phase of the window’s
+ * lifecycle.
*
- * Each time the window is opened this process will set it up for use in a particular context, based
- * on the `data` argument.
+ * Override this method to add additional steps to the ‘setup’ process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
*
- * When you override this method, you can add additional setup steps to the process the parent
- * method provides using the 'first' and 'next' methods.
+ * To add window content that persists between openings, you may wish to use the #initialize method
+ * instead.
*
* @abstract
* @param {Object} [data] Window opening data
};
/**
- * Get a process for readying a window for use.
+ * Get the ‘ready’ process.
*
- * Each time the window is open and setup, this process will ready it up for use in a particular
- * context, based on the `data` argument.
+ * The ready process is used to ready a window for use in a particular
+ * context, based on the `data` argument. This method is called during the opening phase of
+ * the window’s lifecycle, after the window has been {@link #getSetupProcess setup}.
*
- * When you override this method, you can add additional setup steps to the process the parent
- * method provides using the 'first' and 'next' methods.
+ * Override this method to add additional steps to the ‘ready’ process the parent method
+ * provides using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next}
+ * methods of OO.ui.Process.
*
* @abstract
* @param {Object} [data] Window opening data
- * @return {OO.ui.Process} Setup process
+ * @return {OO.ui.Process} Ready process
*/
OO.ui.Window.prototype.getReadyProcess = function () {
return new OO.ui.Process();
};
/**
- * Get a process for holding a window from use.
+ * Get the 'hold' process.
*
- * Each time the window is closed, this process will hold it from use in a particular context, based
- * on the `data` argument.
+ * The hold proccess is used to keep a window from being used in a particular context,
+ * based on the `data` argument. This method is called during the closing phase of the window’s
+ * lifecycle.
*
- * When you override this method, you can add additional setup steps to the process the parent
- * method provides using the 'first' and 'next' methods.
+ * Override this method to add additional steps to the 'hold' process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
*
* @abstract
* @param {Object} [data] Window closing data
};
/**
- * Get a process for tearing down a window after use.
+ * Get the ‘teardown’ process.
*
- * Each time the window is closed this process will tear it down and do something with the user's
- * interactions within the window, based on the `data` argument.
+ * The teardown process is used to teardown a window after use. During teardown,
+ * user interactions within the window are conveyed and the window is closed, based on the `data`
+ * argument. This method is called during the closing phase of the window’s lifecycle.
*
- * When you override this method, you can add additional teardown steps to the process the parent
- * method provides using the 'first' and 'next' methods.
+ * Override this method to add additional steps to the ‘teardown’ process the parent method provides
+ * using the {@link OO.ui.Process#first first} and {@link OO.ui.Process#next next} methods
+ * of OO.ui.Process.
*
* @abstract
* @param {Object} [data] Window closing data
* This will cause the window to initialize. Calling it more than once will cause an error.
*
* @param {OO.ui.WindowManager} manager Manager for this window
- * @throws {Error} If called more than once
+ * @throws {Error} An error is thrown if the method is called more than once
* @chainable
*/
OO.ui.Window.prototype.setManager = function ( manager ) {
};
/**
- * Set the window size.
+ * Set the window size by symbolic name (e.g., 'small' or 'medium')
*
- * @param {string} size Symbolic size name, e.g. 'small', 'medium', 'large', 'full'
+ * @param {string} size Symbolic name of size: `small`, `medium`, `large`, `larger` or
+ * `full`
* @chainable
*/
OO.ui.Window.prototype.setSize = function ( size ) {
/**
* Update the window size.
*
- * @throws {Error} If not attached to a manager
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
* @chainable
*/
OO.ui.Window.prototype.updateSize = function () {
/**
* Initialize window contents.
*
- * The first time the window is opened, #initialize is called so that changes to the window that
- * will persist between openings can be made. See #getSetupProcess for a way to make changes each
- * time the window opens.
+ * Before the window is opened for the first time, #initialize is called so that content that
+ * persists between openings can be added to the window.
*
- * @throws {Error} If not attached to a manager
+ * To set up a window with new content each time the window opens, use #getSetupProcess.
+ *
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
* @chainable
*/
OO.ui.Window.prototype.initialize = function () {
};
/**
- * Open window.
+ * Open the window.
+ *
+ * This method is a wrapper around a call to the window manager’s {@link OO.ui.WindowManager#openWindow openWindow}
+ * method, which returns a promise resolved when the window is done opening.
*
- * This is a wrapper around calling {@link OO.ui.WindowManager#openWindow} on the window manager.
- * To do something each time the window opens, use #getSetupProcess or #getReadyProcess.
+ * To customize the window each time it opens, use #getSetupProcess or #getReadyProcess.
*
* @param {Object} [data] Window opening data
- * @return {jQuery.Promise} Promise resolved when window is opened; when the promise is resolved the
- * first argument will be a promise which will be resolved when the window begins closing
- * @throws {Error} If not attached to a manager
+ * @return {jQuery.Promise} Promise resolved with a value when the window is opened, or rejected
+ * if the window fails to open. When the promise is resolved successfully, the first argument of the
+ * value is a new promise, which is resolved when the window begins closing.
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
*/
OO.ui.Window.prototype.open = function ( data ) {
if ( !this.manager ) {
};
/**
- * Close window.
+ * Close the window.
*
- * This is a wrapper around calling OO.ui.WindowManager#closeWindow on the window manager.
- * To do something each time the window closes, use #getHoldProcess or #getTeardownProcess.
+ * This method is a wrapper around a call to the window
+ * manager’s {@link OO.ui.WindowManager#closeWindow closeWindow} method,
+ * which returns a closing promise resolved when the window is done closing.
+ *
+ * The window's #getHoldProcess and #getTeardownProcess methods are called during the closing
+ * phase of the window’s lifecycle and can be used to specify closing behavior each time
+ * the window closes.
*
* @param {Object} [data] Window closing data
* @return {jQuery.Promise} Promise resolved when window is closed
- * @throws {Error} If not attached to a manager
+ * @throws {Error} An error is thrown if the window is not attached to a window manager
*/
OO.ui.Window.prototype.close = function ( data ) {
if ( !this.manager ) {
/**
* Symbolic name of dialog.
*
+ * The dialog class must have a symbolic name in order to be registered with OO.Factory.
+ * Please see the [OOjs UI documentation on MediaWiki] [3] for more information.
+ *
+ * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
* @abstract
* @static
* @inheritable
OO.ui.Dialog.static.name = '';
/**
- * Dialog title.
+ * The dialog title.
+ *
+ * The title can be specified as a plaintext string, a {@link OO.ui.LabelElement Label} node, or a function
+ * that will produce a Label node or string. The title can also be specified with data passed to the
+ * constructor (see #getSetupProcess). In this case, the static value will be overriden.
*
* @abstract
* @static
* @inheritable
- * @property {jQuery|string|Function} Label nodes, text or a function that returns nodes or text
+ * @property {jQuery|string|Function}
*/
OO.ui.Dialog.static.title = '';
/**
- * List of OO.ui.ActionWidget configuration options.
+ * An array of configured {@link OO.ui.ActionWidget action widgets}.
+ *
+ * Actions can also be specified with data passed to the constructor (see #getSetupProcess). In this case, the static
+ * value will be overriden.
+ *
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
*
* @static
- * inheritable
+ * @inheritable
* @property {Object[]}
*/
OO.ui.Dialog.static.actions = [];
/**
- * Close dialog when the escape key is pressed.
+ * Close the dialog when the 'Esc' key is pressed.
*
* @static
* @abstract
/**
* Handle frame document key down events.
*
+ * @private
* @param {jQuery.Event} e Key down event
*/
OO.ui.Dialog.prototype.onDocumentKeyDown = function ( e ) {
/**
* Handle action resized events.
*
+ * @private
* @param {OO.ui.ActionWidget} action Action that was resized
*/
OO.ui.Dialog.prototype.onActionResize = function () {
/**
* Handle action click events.
*
+ * @private
* @param {OO.ui.ActionWidget} action Action that was clicked
*/
OO.ui.Dialog.prototype.onActionClick = function ( action ) {
/**
* Handle actions change event.
+ *
+ * @private
*/
OO.ui.Dialog.prototype.onActionsChange = function () {
this.detachActions();
};
/**
- * Get set of actions.
+ * Get the set of actions used by the dialog.
*
* @return {OO.ui.ActionSet}
*/
/**
* Get a process for taking action.
*
- * When you override this method, you can add additional accept steps to the process the parent
- * method provides using the 'first' and 'next' methods.
+ * When you override this method, you can create a new OO.ui.Process and return it, or add additional
+ * accept steps to the process the parent method provides using the {@link OO.ui.Process#first 'first'}
+ * and {@link OO.ui.Process#next 'next'} methods of OO.ui.Process.
*
* @abstract
* @param {string} [action] Symbolic name of action
/**
* Attach action actions.
+ *
+ * @protected
*/
OO.ui.Dialog.prototype.attachActions = function () {
// Remember the list of potentially attached actions
/**
* Detach action actions.
*
+ * @protected
* @chainable
*/
OO.ui.Dialog.prototype.detachActions = function () {
*
* @event closing
* @param {OO.ui.Window} win Window that's being closed
- * @param {jQuery.Promise} opening Promise resolved when window is closed; when the promise
+ * @param {jQuery.Promise} closing Promise resolved when window is closed; when the promise
* is resolved the first argument will be a the closing data; progress notifications will be fired
* on the promise for `hold` and `teardown` when those processes are completed respectively.
* @param {Object} data Window closing data
config = config || {};
// Properties
- this.$button = config.$button || $( '<a>' );
+ this.$button = null;
this.framed = null;
this.accessKey = null;
this.active = false;
this.$element.addClass( 'oo-ui-buttonElement' );
this.toggleFramed( config.framed === undefined || config.framed );
this.setAccessKey( config.accessKey );
- this.setButtonElement( this.$button );
+ this.setButtonElement( config.$button || $( '<a>' ) );
};
/* Setup */
OO.ui.ButtonElement.prototype.onKeyPress = function ( e ) {
if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
this.emit( 'click' );
+ return false;
}
- return false;
};
/**
this.$bar = $( '<div>' );
this.$actions = $( '<div>' );
this.initialized = false;
+ this.onWindowResizeHandler = this.onWindowResize.bind( this );
// Events
this.$element
.add( this.$bar ).add( this.$group ).add( this.$actions )
- .on( 'mousedown touchstart', this.onPointerDown.bind( this ) );
+ .on( 'mousedown', this.onPointerDown.bind( this ) );
// Initialization
this.$group.addClass( 'oo-ui-toolbar-tools' );
}
};
+/**
+ * Handle window resize event.
+ *
+ * @private
+ * @param {jQuery.Event} e Window resize event
+ */
+OO.ui.Toolbar.prototype.onWindowResize = function () {
+ this.$element.toggleClass(
+ 'oo-ui-toolbar-narrow',
+ this.$bar.width() <= this.narrowThreshold
+ );
+};
+
/**
* Sets up handles and preloads required information for the toolbar to work.
* This must be called after it is attached to a visible document and before doing anything else.
*/
OO.ui.Toolbar.prototype.initialize = function () {
this.initialized = true;
+ this.narrowThreshold = this.$group.width() + this.$actions.width();
+ $( this.getElementWindow() ).on( 'resize', this.onWindowResizeHandler );
+ this.onWindowResize();
};
/**
* Call this whenever you are done using a toolbar.
*/
OO.ui.Toolbar.prototype.destroy = function () {
+ $( this.getElementWindow() ).off( 'resize', this.onWindowResizeHandler );
this.reset();
this.$element.remove();
};
// Events
this.$element.on( {
- 'mousedown touchstart': this.onPointerDown.bind( this ),
- 'mouseup touchend': this.onPointerUp.bind( this ),
+ mousedown: this.onPointerDown.bind( this ),
+ mouseup: this.onPointerUp.bind( this ),
mouseover: this.onMouseOver.bind( this ),
mouseout: this.onMouseOut.bind( this )
} );
* @param {jQuery.Event} e Mouse down event
*/
OO.ui.ToolGroup.prototype.onPointerDown = function ( e ) {
- // e.which is 0 for touch events, 1 for left mouse button
- if ( !this.isDisabled() && e.which <= 1 ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
this.pressed = this.getTargetTool( e );
if ( this.pressed ) {
this.pressed.setActive( true );
OO.ui.ToolGroup.prototype.onPointerUp = function ( e ) {
var tool = this.getTargetTool( e );
- // e.which is 0 for touch events, 1 for left mouse button
- if ( !this.isDisabled() && e.which <= 1 && this.pressed && this.pressed === tool ) {
+ if ( !this.isDisabled() && e.which === 1 && this.pressed && this.pressed === tool ) {
this.pressed.onSelect();
}
};
/**
- * Dialog for showing a message.
+ * MessageDialogs display a confirmation or alert message. By default, the rendered dialog box
+ * consists of a header that contains the dialog title, a body with the message, and a footer that
+ * contains any {@link OO.ui.ActionWidget action widgets}. The MessageDialog class is the only type
+ * of {@link OO.ui.Dialog dialog} that is usually instantiated directly.
+ *
+ * There are two basic types of message dialogs, confirmation and alert:
+ *
+ * - **confirmation**: the dialog title describes what a progressive action will do and the message provides
+ * more details about the consequences.
+ * - **alert**: the dialog title describes which event occurred and the message provides more information
+ * about why the event occurred.
+ *
+ * The MessageDialog class specifies two actions: ‘accept’, the primary
+ * action (e.g., ‘ok’) and ‘reject,’ the safe action (e.g., ‘cancel’). Both will close the window,
+ * passing along the selected action.
+ *
+ * For more information and examples, please see the [OOjs UI documentation on MediaWiki][1].
*
- * User interface:
- * - Registers two actions by default (safe and primary).
- * - Renders action widgets in the footer.
+ * @example
+ * // Example: Creating and opening a message dialog window.
+ * var messageDialog = new OO.ui.MessageDialog();
+ *
+ * // Create and append a window manager.
+ * var windowManager = new OO.ui.WindowManager();
+ * $( 'body' ).append( windowManager.$element );
+ * windowManager.addWindows( [ messageDialog ] );
+ * // Open the window.
+ * windowManager.openWindow( messageDialog, {
+ * title: 'Basic message dialog',
+ * message: 'This is the message'
+ * } );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Message_Dialogs
*
* @class
* @extends OO.ui.Dialog
/**
* Dialog title.
*
- * A confirmation dialog's title should describe what the progressive action will do. An alert
- * dialog's title should describe what event occurred.
+ * The title of a confirmation dialog describes what a progressive action will do. The
+ * title of an alert dialog describes which event occurred.
*
* @static
- * inheritable
+ * @inheritable
* @property {jQuery|string|Function|null}
*/
OO.ui.MessageDialog.static.title = null;
/**
- * A confirmation dialog's message should describe the consequences of the progressive action. An
- * alert dialog's message should describe why the event occurred.
+ * The message displayed in the dialog body.
+ *
+ * A confirmation message describes the consequences of a progressive action. An alert
+ * message describes why an event occurred.
*
* @static
- * inheritable
+ * @inheritable
* @property {jQuery|string|Function|null}
*/
OO.ui.MessageDialog.static.message = null;
/**
* Handle window resized events.
+ *
+ * @private
*/
OO.ui.MessageDialog.prototype.onResize = function () {
var dialog = this;
/**
* Toggle action layout between vertical and horizontal.
*
+ *
+ * @private
* @param {boolean} [value] Layout actions vertically, omit to toggle
* @chainable
*/
* Fit action actions into columns or rows.
*
* Columns will be used if all labels can fit without overflow, otherwise rows will be used.
+ *
+ * @private
*/
OO.ui.MessageDialog.prototype.fitActions = function () {
var i, len, action,
};
/**
- * Navigation dialog window.
+ * ProcessDialog windows encapsulate a {@link OO.ui.Process process} and all of the code necessary
+ * to complete it. If the process terminates with an error, a customizable {@link OO.ui.Error error
+ * interface} alerts users to the trouble, permitting the user to dismiss the error and try again when
+ * relevant. The ProcessDialog class is always extended and customized with the actions and content
+ * required for each process.
+ *
+ * The process dialog box consists of a header that visually represents the ‘working’ state of long
+ * processes with an animation. The header contains the dialog title as well as
+ * two {@link OO.ui.ActionWidget action widgets}: a ‘safe’ action on the left (e.g., ‘Cancel’) and
+ * a ‘primary’ action on the right (e.g., ‘Done’).
+ *
+ * Like other windows, the process dialog is managed by a {@link OO.ui.WindowManager window manager}.
+ * Please see the [OOjs UI documentation on MediaWiki][1] for more information and examples.
*
- * Logic:
- * - Show and hide errors.
- * - Retry an action.
+ * @example
+ * // Example: Creating and opening a process dialog window.
+ * function ProcessDialog( config ) {
+ * ProcessDialog.super.call( this, config );
+ * }
+ * OO.inheritClass( ProcessDialog, OO.ui.ProcessDialog );
*
- * User interface:
- * - Renders header with dialog title and one action widget on either side
- * (a 'safe' button on the left, and a 'primary' button on the right, both of
- * which close the dialog).
- * - Displays any action widgets in the footer (none by default).
- * - Ability to dismiss errors.
+ * ProcessDialog.static.title = 'Process dialog';
+ * ProcessDialog.static.actions = [
+ * { action: 'save', label: 'Done', flags: 'primary' },
+ * { label: 'Cancel', flags: 'safe' }
+ * ];
*
- * Subclass responsibilities:
- * - Register a 'safe' action.
- * - Register a 'primary' action.
- * - Add content to the dialog.
+ * ProcessDialog.prototype.initialize = function () {
+ * ProcessDialog.super.prototype.initialize.apply( this, arguments );
+ * this.content = new OO.ui.PanelLayout( { $: this.$, padded: true, expanded: false } );
+ * this.content.$element.append( '<p>This is a process dialog window. The header contains the title and two buttons: \'Cancel\' (a safe action) on the left and \'Done\' (a primary action) on the right. </p>' );
+ * this.$body.append( this.content.$element );
+ * };
+ * ProcessDialog.prototype.getActionProcess = function ( action ) {
+ * var dialog = this;
+ * if ( action ) {
+ * return new OO.ui.Process( function () {
+ * dialog.close( { action: action } );
+ * } );
+ * }
+ * return ProcessDialog.super.prototype.getActionProcess.call( this, action );
+ * };
+ *
+ * var windowManager = new OO.ui.WindowManager();
+ * $( 'body' ).append( windowManager.$element );
+ *
+ * var processDialog = new ProcessDialog();
+ * windowManager.addWindows( [ processDialog ] );
+ * windowManager.openWindow( processDialog );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs
*
* @abstract
* @class
* Handle dismiss button click events.
*
* Hides errors.
+ *
+ * @private
*/
OO.ui.ProcessDialog.prototype.onDismissErrorButtonClick = function () {
this.hideErrors();
* Handle retry button click events.
*
* Hides errors and then tries again.
+ *
+ * @private
*/
OO.ui.ProcessDialog.prototype.onRetryButtonClick = function () {
this.hideErrors();
/**
* Fit label between actions.
*
+ * @private
* @chainable
*/
OO.ui.ProcessDialog.prototype.fitLabel = function () {
/**
* Handle errors that occurred during accept or reject processes.
*
+ * @private
* @param {OO.ui.Error[]} errors Errors to be handled
*/
OO.ui.ProcessDialog.prototype.showErrors = function ( errors ) {
/**
* Hide errors.
+ *
+ * @private
*/
OO.ui.ProcessDialog.prototype.hideErrors = function () {
this.$errors.addClass( 'oo-ui-element-hidden' );
* @cfg {boolean} [scrollable=false] Allow vertical scrolling
* @cfg {boolean} [padded=false] Pad the content from the edges
* @cfg {boolean} [expanded=true] Expand size to fill the entire parent element
+ * @cfg {boolean} [framed=false] Wrap in a frame to visually separate from outside content
*/
OO.ui.PanelLayout = function OoUiPanelLayout( config ) {
// Configuration initialization
config = $.extend( {
scrollable: false,
padded: false,
- expanded: true
+ expanded: true,
+ framed: false
}, config );
// Parent constructor
if ( config.expanded ) {
this.$element.addClass( 'oo-ui-panelLayout-expanded' );
}
+ if ( config.framed ) {
+ this.$element.addClass( 'oo-ui-panelLayout-framed' );
+ }
};
/* Setup */
// Events
this.$handle.on( {
- 'mousedown touchstart': this.onHandlePointerDown.bind( this ),
- 'mouseup touchend': this.onHandlePointerUp.bind( this )
+ mousedown: this.onHandlePointerDown.bind( this ),
+ mouseup: this.onHandlePointerUp.bind( this )
} );
// Initialization
* @inheritdoc
*/
OO.ui.PopupToolGroup.prototype.onPointerUp = function ( e ) {
- // e.which is 0 for touch events, 1 for left mouse button
// Only close toolgroup when a tool was actually selected
- // FIXME: this duplicates logic from the parent class
- if ( !this.isDisabled() && e.which <= 1 && this.pressed && this.pressed === this.getTargetTool( e ) ) {
+ if ( !this.isDisabled() && e.which === 1 && this.pressed && this.pressed === this.getTargetTool( e ) ) {
this.setActive( false );
}
return OO.ui.PopupToolGroup.super.prototype.onPointerUp.call( this, e );
* @param {jQuery.Event} e Mouse down event
*/
OO.ui.PopupToolGroup.prototype.onHandlePointerDown = function ( e ) {
- // e.which is 0 for touch events, 1 for left mouse button
- if ( !this.isDisabled() && e.which <= 1 ) {
+ if ( !this.isDisabled() && e.which === 1 ) {
this.setActive( !this.active );
}
return false;
};
/**
- * Set of controls for an OO.ui.OutlineSelectWidget.
- *
+ * OutlineControlsWidget is a set of controls for an {@link OO.ui.OutlineSelectWidget outline select widget}.
* Controls include moving items up and down, removing items, and adding different kinds of items.
+ * ####Currently, this class is only used by {@link OO.ui.BookletLayout BookletLayouts}.####
*
* @class
* @extends OO.ui.Widget
/* Methods */
/**
+ *
+ * @private
* Handle outline change events.
*/
OO.ui.OutlineControlsWidget.prototype.onOutlineChange = function () {
/**
* An ActionWidget is a {@link OO.ui.ButtonWidget button widget} that executes an action.
* Action widgets are used with OO.ui.ActionSet, which manages the behavior and availability
- * of the actions. Please see the [OOjs UI documentation on MediaWiki] [1] for more information
+ * of the actions.
+ *
+ * Both actions and action sets are primarily used with {@link OO.ui.Dialog Dialogs}.
+ * Please see the [OOjs UI documentation on MediaWiki] [1] for more information
* and examples.
*
* [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [action] Symbolic action name
- * @cfg {string[]} [modes] Symbolic mode names
- * @cfg {boolean} [framed=false] Render button with a frame
+ * @cfg {string} [action] Symbolic name of the action (e.g., ‘continue’ or ‘cancel’).
+ * @cfg {string[]} [modes] Symbolic names of the modes (e.g., ‘edit’ or ‘read’) in which the action
+ * should be made available. See the action set's {@link OO.ui.ActionSet#setMode setMode} method
+ * for more information about setting modes.
+ * @cfg {boolean} [framed=false] Render the action button with a frame
*/
OO.ui.ActionWidget = function OoUiActionWidget( config ) {
// Configuration initialization
/* Events */
/**
+ * A resize event is emitted when the size of the widget changes.
+ *
* @event resize
*/
/* Methods */
/**
- * Check if action is available in a certain mode.
+ * Check if the action is configured to be available in the specified `mode`.
*
* @param {string} mode Name of mode
- * @return {boolean} Has mode
+ * @return {boolean} The action is configured with the mode
*/
OO.ui.ActionWidget.prototype.hasMode = function ( mode ) {
return this.modes.indexOf( mode ) !== -1;
};
/**
- * Get symbolic action name.
+ * Get the symbolic name of the action (e.g., ‘continue’ or ‘cancel’).
*
* @return {string}
*/
};
/**
- * Get symbolic action name.
+ * Get the symbolic name of the mode or modes for which the action is configured to be available.
*
- * @return {string}
+ * The current mode is set with the action set's {@link OO.ui.ActionSet#setMode setMode} method.
+ * Only actions that are configured to be avaiable in the current mode will be visible. All other actions
+ * are hidden.
+ *
+ * @return {string[]}
*/
OO.ui.ActionWidget.prototype.getModes = function () {
return this.modes.slice();
/**
* Emit a resize event if the size has changed.
*
+ * @private
* @chainable
*/
OO.ui.ActionWidget.prototype.propagateResize = function () {
};
/**
- * Toggle visibility of button.
+ * Toggle the visibility of the action button.
*
* @param {boolean} [show] Show button, omit to toggle visibility
* @chainable
OO.ui.DropdownWidget.prototype.onKeyPress = function ( e ) {
if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
this.menu.toggle();
+ return false;
}
- return false;
};
/**
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [name=''] HTML input name
- * @cfg {string} [value=''] Input value
- * @cfg {Function} [inputFilter] Filter function to apply to the input. Takes a string argument and returns a string.
+ * @cfg {string} [name=''] The value of the input’s HTML `name` attribute.
+ * @cfg {string} [value=''] The value of the input.
+ * @cfg {Function} [inputFilter] The name of an input filter function. Input filters modify the value of an input
+ * before it is accepted.
*/
OO.ui.InputWidget = function OoUiInputWidget( config ) {
// Configuration initialization
/**
* @event change
+ *
+ * A change event is emitted when the value of the input changes.
+ *
* @param {string} value
*/
/**
* Handle potentially value-changing events.
*
+ * @private
* @param {jQuery.Event} e Key down, mouse up, cut, paste, change, input, or select event
*/
OO.ui.InputWidget.prototype.onEdit = function () {
};
/**
- * Sets the direction of the current input, either RTL or LTR
+ * Set the direction of the input, either RTL (right-to-left) or LTR (left-to-right).
*
* @param {boolean} isRTL
+ * Direction is right-to-left
*/
OO.ui.InputWidget.prototype.setRTL = function ( isRTL ) {
this.$input.prop( 'dir', isRTL ? 'rtl' : 'ltr' );
};
/**
- * Simulate the behavior of clicking on a label bound to this input.
+ * Simulate the behavior of clicking on a label bound to this input. This method is only called by
+ * {@link OO.ui.LabelWidget LabelWidget} and {@link OO.ui.FieldLayout FieldLayout}. It should not be
+ * called directly.
*/
OO.ui.InputWidget.prototype.simulateLabelClick = function () {
if ( !this.isDisabled() ) {
*
* @constructor
* @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. 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).
+ * @cfg {string} [type='button'] The value of the HTML `'type'` attribute: 'button', 'submit' or 'reset'.
+ * @cfg {boolean} [useInputTag=false] Use an `<input/>` tag instead of a `<button/>` tag, the default.
+ * Widgets configured to be an `<input/>` do not support {@link #icon icons} and {@link #indicator indicators},
+ * non-plaintext {@link #label labels}, or {@link #value values}. In general, useInputTag should only
+ * be set to `true` when there’s need to support IE6 in a form with multiple buttons.
*/
OO.ui.ButtonInputWidget = function OoUiButtonInputWidget( config ) {
// Configuration initialization
/**
* Set label value.
*
- * Overridden to support setting the 'value' of `<input/>` elements.
+ * If #useInputTag is `true`, the label is set as the `value` of the `<input/>` tag.
*
- * @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
- * text; or null for no label
+ * @param {jQuery|string|Function|null} label Label nodes, text, a function that returns nodes or
+ * text, or `null` for no label
* @chainable
*/
OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
/**
* Set the value of the input.
*
- * Overridden to disable for `<input/>` elements, which have value identical to the label.
+ * This method is disabled for button inputs configured as {@link #useInputTag <input/> tags}, as
+ * they do not support {@link #value values}.
*
* @param {string} value New value
* @chainable
keypress: this.onKeyPress.bind( this ),
blur: this.setValidityFlag.bind( this )
} );
- this.$element.on( 'DOMNodeInsertedIntoDocument', this.onElementAttach.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 ) );
if ( config.required ) {
this.$input.attr( 'required', 'true' );
}
+ if ( this.label || config.autosize ) {
+ this.installParentChangeDetector();
+ }
};
/* Setup */
return this;
};
+/**
+ * Support function for making #onElementAttach work across browsers.
+ *
+ * This whole function could be replaced with one line of code using the DOMNodeInsertedIntoDocument
+ * event, but it's not supported by Firefox and allegedly deprecated, so we only use it as fallback.
+ *
+ * Due to MutationObserver performance woes, #onElementAttach is only somewhat reliably called the
+ * first time that the element gets attached to the documented.
+ */
+OO.ui.TextInputWidget.prototype.installParentChangeDetector = function () {
+ var mutationObserver, onRemove, topmostNode, fakeParentNode,
+ MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
+ widget = this;
+
+ if ( MutationObserver ) {
+ // The new way. If only it wasn't so ugly.
+
+ if ( this.$element.closest( 'html' ).length ) {
+ // Widget is attached already, do nothing. This breaks the functionality of this function when
+ // the widget is detached and reattached. Alas, doing this correctly with MutationObserver
+ // would require observation of the whole document, which would hurt performance of other,
+ // more important code.
+ return;
+ }
+
+ // Find topmost node in the tree
+ topmostNode = this.$element[0];
+ while ( topmostNode.parentNode ) {
+ topmostNode = topmostNode.parentNode;
+ }
+
+ // We have no way to detect the $element being attached somewhere without observing the entire
+ // DOM with subtree modifications, which would hurt performance. So we cheat: we hook to the
+ // parent node of $element, and instead detect when $element is removed from it (and thus
+ // probably attached somewhere else). If there is no parent, we create a "fake" one. If it
+ // doesn't get attached, we end up back here and create the parent.
+
+ mutationObserver = new MutationObserver( function ( mutations ) {
+ var i, j, removedNodes;
+ for ( i = 0; i < mutations.length; i++ ) {
+ removedNodes = mutations[ i ].removedNodes;
+ for ( j = 0; j < removedNodes.length; j++ ) {
+ if ( removedNodes[ j ] === topmostNode ) {
+ setTimeout( onRemove, 0 );
+ return;
+ }
+ }
+ }
+ } );
+
+ onRemove = function () {
+ // If the node was attached somewhere else, report it
+ if ( widget.$element.closest( 'html' ).length ) {
+ widget.onElementAttach();
+ }
+ mutationObserver.disconnect();
+ widget.installParentChangeDetector();
+ };
+
+ // Create a fake parent and observe it
+ fakeParentNode = $( '<div>' ).append( this.$element )[0];
+ mutationObserver.observe( fakeParentNode, { childList: true } );
+ } else {
+ // Using the DOMNodeInsertedIntoDocument event is much nicer and less magical, and works for
+ // detachment and reattachment, but it's not supported by Firefox and allegedly deprecated.
+ this.$element.on( 'DOMNodeInsertedIntoDocument', this.onElementAttach.bind( this ) );
+ }
+};
+
/**
* Automatically adjust the size of the text input.
*
if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
this.menu.toggle();
this.input.$input[ 0 ].focus();
+ return false;
}
- return false;
};
/**
};
/**
- * Menu for a text input widget.
- *
- * This menu is specially designed to be positioned beneath a text input widget. The menu's position
- * is automatically calculated and maintained when the menu is toggled or the window is resized.
+ * TextInputMenuSelectWidget is a menu that is specially designed to be positioned beneath
+ * a {@link OO.ui.TextInputWidget text input} field. The menu's position is automatically
+ * calculated and maintained when the menu is toggled or the window is resized.
+ * See OO.ui.ComboBoxWidget for an example of a widget that uses this class.
*
* @class
* @extends OO.ui.MenuSelectWidget
/**
* Handle window resize event.
*
+ * @private
* @param {jQuery.Event} e Window resize event
*/
OO.ui.TextInputMenuSelectWidget.prototype.onWindowResize = function () {
/**
* Position the menu.
*
+ * @private
* @chainable
*/
OO.ui.TextInputMenuSelectWidget.prototype.position = function () {
};
/**
- * Structured list of items.
+ * OutlineSelectWidget is a structured list that contains {@link OO.ui.OutlineOptionWidget outline options}
+ * A set of controls can be provided with an {@link OO.ui.OutlineControlsWidget outline controls} widget.
*
- * Use with OO.ui.OutlineOptionWidget.
+ * ####Currently, this class is only used by {@link OO.ui.BookletLayout BookletLayouts}.####
*
* @class
* @extends OO.ui.SelectWidget
OO.ui.ToggleSwitchWidget.prototype.onKeyPress = function ( e ) {
if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
this.setValue( !this.value );
+ return false;
}
- return false;
};
}( OO ) );