/*!
- * OOjs UI v0.13.3
+ * OOjs UI v0.15.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
- * Copyright 2011–2015 OOjs UI Team and other contributors.
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2015-11-18T01:09:23Z
+ * Date: 2016-01-12T23:06:31Z
*/
( function ( OO ) {
SPACE: 32
};
+/**
+ * Constants for MouseEvent.which
+ *
+ * @property {Object}
+ */
+OO.ui.MouseButtons = {
+ LEFT: 1,
+ MIDDLE: 2,
+ RIGHT: 3
+};
+
/**
* @property {Number}
*/
};
/**
- * Proxy for `node.addEventListener( eventName, handler, true )`, if the browser supports it.
- * Otherwise falls back to non-capturing event listeners.
+ * Proxy for `node.addEventListener( eventName, handler, true )`.
*
* @param {HTMLElement} node
* @param {string} eventName
* @param {Function} handler
+ * @deprecated
*/
OO.ui.addCaptureEventListener = function ( node, eventName, handler ) {
- if ( node.addEventListener ) {
- node.addEventListener( eventName, handler, true );
- } else {
- node.attachEvent( 'on' + eventName, handler );
- }
+ node.addEventListener( eventName, handler, true );
};
/**
- * Proxy for `node.removeEventListener( eventName, handler, true )`, if the browser supports it.
- * Otherwise falls back to non-capturing event listeners.
+ * Proxy for `node.removeEventListener( eventName, handler, true )`.
*
* @param {HTMLElement} node
* @param {string} eventName
* @param {Function} handler
+ * @deprecated
*/
OO.ui.removeCaptureEventListener = function ( node, eventName, handler ) {
- if ( node.addEventListener ) {
- node.removeEventListener( eventName, handler, true );
- } else {
- node.detachEvent( 'on' + eventName, handler );
- }
+ node.removeEventListener( eventName, handler, true );
};
/**
}
return message;
};
+} )();
- /**
- * Package a message and arguments for deferred resolution.
- *
- * Use this when you are statically specifying a message and the message may not yet be present.
- *
- * @param {string} key Message key
- * @param {Mixed...} [params] Message parameters
- * @return {Function} Function that returns the resolved message when executed
- */
- OO.ui.deferMsg = function () {
- var args = arguments;
- return function () {
- return OO.ui.msg.apply( OO.ui, args );
- };
+/**
+ * Package a message and arguments for deferred resolution.
+ *
+ * Use this when you are statically specifying a message and the message may not yet be present.
+ *
+ * @param {string} key Message key
+ * @param {Mixed...} [params] Message parameters
+ * @return {Function} Function that returns the resolved message when executed
+ */
+OO.ui.deferMsg = function () {
+ var args = arguments;
+ return function () {
+ return OO.ui.msg.apply( OO.ui, args );
};
+};
- /**
- * Resolve a message.
- *
- * If the message is a function it will be executed, otherwise it will pass through directly.
- *
- * @param {Function|string} msg Deferred message, or message text
- * @return {string} Resolved message
- */
- OO.ui.resolveMsg = function ( msg ) {
- if ( $.isFunction( msg ) ) {
- return msg();
- }
- return msg;
- };
+/**
+ * Resolve a message.
+ *
+ * If the message is a function it will be executed, otherwise it will pass through directly.
+ *
+ * @param {Function|string} msg Deferred message, or message text
+ * @return {string} Resolved message
+ */
+OO.ui.resolveMsg = function ( msg ) {
+ if ( $.isFunction( msg ) ) {
+ return msg();
+ }
+ return msg;
+};
- /**
- * @param {string} url
- * @return {boolean}
- */
- OO.ui.isSafeUrl = function ( url ) {
- var protocol,
- // Keep in sync with php/Tag.php
- whitelist = [
- 'bitcoin:', 'ftp:', 'ftps:', 'geo:', 'git:', 'gopher:', 'http:', 'https:', 'irc:', 'ircs:',
- 'magnet:', 'mailto:', 'mms:', 'news:', 'nntp:', 'redis:', 'sftp:', 'sip:', 'sips:', 'sms:', 'ssh:',
- 'svn:', 'tel:', 'telnet:', 'urn:', 'worldwind:', 'xmpp:'
- ];
-
- if ( url.indexOf( ':' ) === -1 ) {
- // No protocol, safe
- return true;
- }
+/**
+ * @param {string} url
+ * @return {boolean}
+ */
+OO.ui.isSafeUrl = function ( url ) {
+ var protocol,
+ // Keep in sync with php/Tag.php
+ whitelist = [
+ 'bitcoin:', 'ftp:', 'ftps:', 'geo:', 'git:', 'gopher:', 'http:', 'https:', 'irc:', 'ircs:',
+ 'magnet:', 'mailto:', 'mms:', 'news:', 'nntp:', 'redis:', 'sftp:', 'sip:', 'sips:', 'sms:', 'ssh:',
+ 'svn:', 'tel:', 'telnet:', 'urn:', 'worldwind:', 'xmpp:'
+ ];
+
+ if ( url.indexOf( ':' ) === -1 ) {
+ // No protocol, safe
+ return true;
+ }
- protocol = url.split( ':', 1 )[ 0 ] + ':';
- if ( !protocol.match( /^([A-za-z0-9\+\.\-])+:/ ) ) {
- // Not a valid protocol, safe
- return true;
- }
+ protocol = url.split( ':', 1 )[ 0 ] + ':';
+ if ( !protocol.match( /^([A-za-z0-9\+\.\-])+:/ ) ) {
+ // Not a valid protocol, safe
+ return true;
+ }
- // Safe if in the whitelist
- return whitelist.indexOf( protocol ) !== -1;
- };
+ // Safe if in the whitelist
+ return whitelist.indexOf( protocol ) !== -1;
+};
-} )();
+/**
+ * Lazy-initialize and return a global OO.ui.WindowManager instance, used by OO.ui.alert and
+ * OO.ui.confirm.
+ *
+ * @private
+ * @return {OO.ui.WindowManager}
+ */
+OO.ui.getWindowManager = function () {
+ if ( !OO.ui.windowManager ) {
+ OO.ui.windowManager = new OO.ui.WindowManager();
+ $( 'body' ).append( OO.ui.windowManager.$element );
+ OO.ui.windowManager.addWindows( {
+ messageDialog: new OO.ui.MessageDialog()
+ } );
+ }
+ return OO.ui.windowManager;
+};
+
+/**
+ * Display a quick modal alert dialog, using a OO.ui.MessageDialog. While the dialog is open, the
+ * rest of the page will be dimmed out and the user won't be able to interact with it. The dialog
+ * has only one action button, labelled "OK", clicking it will simply close the dialog.
+ *
+ * A window manager is created automatically when this function is called for the first time.
+ *
+ * @example
+ * OO.ui.alert( 'Something happened!' ).done( function () {
+ * console.log( 'User closed the dialog.' );
+ * } );
+ *
+ * @param {jQuery|string} text Message text to display
+ * @param {Object} [options] Additional options, see OO.ui.MessageDialog#getSetupProcess
+ * @return {jQuery.Promise} Promise resolved when the user closes the dialog
+ */
+OO.ui.alert = function ( text, options ) {
+ return OO.ui.getWindowManager().openWindow( 'messageDialog', $.extend( {
+ message: text,
+ verbose: true,
+ actions: [ OO.ui.MessageDialog.static.actions[ 0 ] ]
+ }, options ) ).then( function ( opened ) {
+ return opened.then( function ( closing ) {
+ return closing.then( function () {
+ return $.Deferred().resolve();
+ } );
+ } );
+ } );
+};
+
+/**
+ * Display a quick modal confirmation dialog, using a OO.ui.MessageDialog. While the dialog is open,
+ * the rest of the page will be dimmed out and the user won't be able to interact with it. The
+ * dialog has two action buttons, one to confirm an operation (labelled "OK") and one to cancel it
+ * (labelled "Cancel").
+ *
+ * A window manager is created automatically when this function is called for the first time.
+ *
+ * @example
+ * OO.ui.confirm( 'Are you sure?' ).done( function ( confirmed ) {
+ * if ( confirmed ) {
+ * console.log( 'User clicked "OK"!' );
+ * } else {
+ * console.log( 'User clicked "Cancel" or closed the dialog.' );
+ * }
+ * } );
+ *
+ * @param {jQuery|string} text Message text to display
+ * @param {Object} [options] Additional options, see OO.ui.MessageDialog#getSetupProcess
+ * @return {jQuery.Promise} Promise resolved when the user closes the dialog. If the user chose to
+ * confirm, the promise will resolve to boolean `true`; otherwise, it will resolve to boolean
+ * `false`.
+ */
+OO.ui.confirm = function ( text, options ) {
+ return OO.ui.getWindowManager().openWindow( 'messageDialog', $.extend( {
+ message: text,
+ verbose: true
+ }, options ) ).then( function ( opened ) {
+ return opened.then( function ( closing ) {
+ return closing.then( function ( data ) {
+ return $.Deferred().resolve( !!( data && data.action === 'accept' ) );
+ } );
+ } );
+ } );
+};
/*!
* Mixin namespace.
*/
OO.ui.Element.static.getWindow = function ( obj ) {
var doc = this.getDocument( obj );
- // Support: IE 8
- // Standard Document.defaultView is IE9+
- return doc.parentWindow || doc.defaultView;
+ return doc.defaultView;
};
/**
*/
OO.ui.Element.static.getBorders = function ( el ) {
var doc = el.ownerDocument,
- // Support: IE 8
- // Standard Document.defaultView is IE9+
- win = doc.parentWindow || doc.defaultView,
- style = win && win.getComputedStyle ?
- win.getComputedStyle( el, null ) :
- // Support: IE 8
- // Standard getComputedStyle() is IE9+
- el.currentStyle,
+ win = doc.defaultView,
+ style = win.getComputedStyle( el, null ),
$el = $( el ),
top = parseFloat( style ? style.borderTopWidth : $el.css( 'borderTopWidth' ) ) || 0,
left = parseFloat( style ? style.borderLeftWidth : $el.css( 'borderLeftWidth' ) ) || 0,
OO.ui.Element.static.getDimensions = function ( el ) {
var $el, $win,
doc = el.ownerDocument || el.document,
- // Support: IE 8
- // Standard Document.defaultView is IE9+
- win = doc.parentWindow || doc.defaultView;
+ win = doc.defaultView;
if ( win === el || el === doc.documentElement ) {
$win = $( win );
*
* The title can be specified as a plaintext string, a {@link OO.ui.mixin.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.
+ * constructor (see #getSetupProcess). In this case, the static value will be overridden.
*
* @abstract
* @static
* 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.
+ * value will be overridden.
*
* [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
*
*/
OO.ui.Dialog.prototype.onDialogKeyDown = function ( e ) {
if ( e.which === OO.ui.Keys.ESCAPE ) {
- this.close();
+ this.executeAction( '' );
e.preventDefault();
e.stopPropagation();
}
/**
* Get tools from the factory
*
- * @param {Array} include Included tools
- * @param {Array} exclude Excluded tools
- * @param {Array} promote Promoted tools
- * @param {Array} demote Demoted tools
+ * @param {Array|string} [include] Included tools, see #extract for format
+ * @param {Array|string} [exclude] Excluded tools, see #extract for format
+ * @param {Array|string} [promote] Promoted tools, see #extract for format
+ * @param {Array|string} [demote] Demoted tools, see #extract for format
* @return {string[]} List of tools
*/
OO.ui.ToolFactory.prototype.getTools = function ( include, exclude, promote, demote ) {
/**
* Get a flat list of names from a list of names or groups.
*
- * Tools can be specified in the following ways:
+ * Normally, `collection` is an array of tool specifications. Tools can be specified in the
+ * following ways:
+ *
+ * - To include an individual tool, use the symbolic name: `{ name: 'tool-name' }` or `'tool-name'`.
+ * - To include all tools in a group, use the group name: `{ group: 'group-name' }`. (To assign the
+ * tool to a group, use OO.ui.Tool.static.group.)
+ *
+ * Alternatively, to include all tools that are not yet assigned to any other toolgroup, use the
+ * catch-all selector `'*'`.
*
- * - A specific tool: `{ name: 'tool-name' }` or `'tool-name'`
- * - All tools in a group: `{ group: 'group-name' }`
- * - All tools: `'*'`
+ * If `used` is passed, tool names that appear as properties in this object will be considered
+ * already assigned, and will not be returned even if specified otherwise. The tool names extracted
+ * by this function call will be added as new properties in the object.
*
* @private
- * @param {Array|string} collection List of tools
- * @param {Object} [used] Object with names that should be skipped as properties; extracted
- * names will be added as properties
- * @return {string[]} List of extracted names
+ * @param {Array|string} collection List of tools, see above
+ * @param {Object} [used] Object containing information about used tools, see above
+ * @return {string[]} List of extracted tool names
*/
OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
var i, len, item, name, tool,
* @param {jQuery.Event} e Mouse down event
*/
OO.ui.mixin.ButtonElement.prototype.onMouseDown = function ( e ) {
- if ( this.isDisabled() || e.which !== 1 ) {
+ if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
return;
}
this.$element.addClass( 'oo-ui-buttonElement-pressed' );
// Run the mouseup handler no matter where the mouse is when the button is let go, so we can
// reliably remove the pressed class
- OO.ui.addCaptureEventListener( this.getElementDocument(), 'mouseup', this.onMouseUpHandler );
+ this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
// Prevent change of focus unless specifically configured otherwise
if ( this.constructor.static.cancelButtonMouseDownEvents ) {
return false;
* @param {jQuery.Event} e Mouse up event
*/
OO.ui.mixin.ButtonElement.prototype.onMouseUp = function ( e ) {
- if ( this.isDisabled() || e.which !== 1 ) {
+ if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
return;
}
this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
// Stop listening for mouseup, since we only needed this once
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mouseup', this.onMouseUpHandler );
+ this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
};
/**
* @fires click
*/
OO.ui.mixin.ButtonElement.prototype.onClick = function ( e ) {
- if ( !this.isDisabled() && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
if ( this.emit( 'click' ) ) {
return false;
}
this.$element.addClass( 'oo-ui-buttonElement-pressed' );
// Run the keyup handler no matter where the key is when the button is let go, so we can
// reliably remove the pressed class
- OO.ui.addCaptureEventListener( this.getElementDocument(), 'keyup', this.onKeyUpHandler );
+ this.getElementDocument().addEventListener( 'keyup', this.onKeyUpHandler, true );
};
/**
}
this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
// Stop listening for keyup, since we only needed this once
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'keyup', this.onKeyUpHandler );
+ this.getElementDocument().removeEventListener( 'keyup', this.onKeyUpHandler, true );
};
/**
* out when the tool is selected. Tools must also be registered with a {@link OO.ui.ToolFactory tool factory},
* which creates the tools on demand.
*
+ * Every Tool subclass must implement two methods:
+ *
+ * - {@link #onUpdateState}
+ * - {@link #onSelect}
+ *
* Tools are added to toolgroups ({@link OO.ui.ListToolGroup ListToolGroup},
* {@link OO.ui.BarToolGroup BarToolGroup}, or {@link OO.ui.MenuToolGroup MenuToolGroup}), which determine how
* the tool is displayed in the toolbar. See {@link OO.ui.Toolbar toolbars} for an example.
/**
* Check if this tool is compatible with given data.
*
- * This is a stub that can be overriden to provide support for filtering tools based on an
+ * This is a stub that can be overridden to provide support for filtering tools based on an
* arbitrary piece of information (e.g., where the cursor is in a document). The implementation
* must also call this method so that the compatibility check can be performed.
*
/* Methods */
/**
- * Handle the toolbar state being updated.
+ * Handle the toolbar state being updated. This method is called when the
+ * {@link OO.ui.Toolbar#event-updateState 'updateState' event} is emitted on the
+ * {@link OO.ui.Toolbar Toolbar} that uses this tool, and should set the state of this tool
+ * depending on application state (usually by calling #setDisabled to enable or disable the tool,
+ * or #setActive to mark is as currently in-use or not).
*
* This is an abstract method that must be overridden in a concrete subclass.
*
OO.ui.Tool.prototype.onUpdateState = null;
/**
- * Handle the tool being selected.
+ * Handle the tool being selected. This method is called when the user triggers this tool,
+ * usually by clicking on its label/icon.
*
* This is an abstract method that must be overridden in a concrete subclass.
*
* The arrangement and order of the toolgroups is customized when the toolbar is set up. Tools can be presented in
* any order, but each can only appear once in the toolbar.
*
+ * The toolbar can be synchronized with the state of the external "application", like a text
+ * editor's editing area, marking tools as active/inactive (e.g. a 'bold' tool would be shown as
+ * active when the text cursor was inside bolded text) or enabled/disabled (e.g. a table caption
+ * tool would be disabled while the user is not editing a table). A state change is signalled by
+ * emitting the {@link #event-updateState 'updateState' event}, which calls Tools'
+ * {@link OO.ui.Tool#onUpdateState onUpdateState method}.
+ *
* The following is an example of a basic toolbar.
*
* @example
* // Define the tools that we're going to place in our toolbar
*
* // Create a class inheriting from OO.ui.Tool
- * function ImageTool() {
- * ImageTool.parent.apply( this, arguments );
+ * function SearchTool() {
+ * SearchTool.parent.apply( this, arguments );
* }
- * OO.inheritClass( ImageTool, OO.ui.Tool );
+ * OO.inheritClass( SearchTool, OO.ui.Tool );
* // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
* // of 'icon' and 'title' (displayed icon and text).
- * ImageTool.static.name = 'image';
- * ImageTool.static.icon = 'image';
- * ImageTool.static.title = 'Insert image';
+ * SearchTool.static.name = 'search';
+ * SearchTool.static.icon = 'search';
+ * SearchTool.static.title = 'Search...';
* // Defines the action that will happen when this tool is selected (clicked).
- * ImageTool.prototype.onSelect = function () {
- * $area.text( 'Image tool clicked!' );
+ * SearchTool.prototype.onSelect = function () {
+ * $area.text( 'Search tool clicked!' );
* // Never display this tool as "active" (selected).
* this.setActive( false );
* };
+ * SearchTool.prototype.onUpdateState = function () {};
* // Make this tool available in our toolFactory and thus our toolbar
- * toolFactory.register( ImageTool );
+ * toolFactory.register( SearchTool );
*
* // Register two more tools, nothing interesting here
* function SettingsTool() {
* $area.text( 'Settings tool clicked!' );
* this.setActive( false );
* };
+ * SettingsTool.prototype.onUpdateState = function () {};
* toolFactory.register( SettingsTool );
*
* // Register two more tools, nothing interesting here
* $area.text( 'More stuff tool clicked!' );
* this.setActive( false );
* };
+ * StuffTool.prototype.onUpdateState = function () {};
* toolFactory.register( StuffTool );
*
* // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
* {
* // 'bar' tool groups display tools' icons only, side-by-side.
* type: 'bar',
- * include: [ 'image', 'help' ]
+ * include: [ 'search', 'help' ]
* },
* {
* // 'list' tool groups display both the titles and icons, in a dropdown list.
* // Here is where the toolbar is actually built. This must be done after inserting it into the
* // document.
* toolbar.initialize();
+ * toolbar.emit( 'updateState' );
*
* The following example extends the previous one to illustrate 'menu' toolgroups and the usage of
- * 'updateState' event.
+ * {@link #event-updateState 'updateState' event}.
*
* @example
* // Create the toolbar
* // Define the tools that we're going to place in our toolbar
*
* // Create a class inheriting from OO.ui.Tool
- * function ImageTool() {
- * ImageTool.parent.apply( this, arguments );
+ * function SearchTool() {
+ * SearchTool.parent.apply( this, arguments );
* }
- * OO.inheritClass( ImageTool, OO.ui.Tool );
+ * OO.inheritClass( SearchTool, OO.ui.Tool );
* // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
* // of 'icon' and 'title' (displayed icon and text).
- * ImageTool.static.name = 'image';
- * ImageTool.static.icon = 'image';
- * ImageTool.static.title = 'Insert image';
+ * SearchTool.static.name = 'search';
+ * SearchTool.static.icon = 'search';
+ * SearchTool.static.title = 'Search...';
* // Defines the action that will happen when this tool is selected (clicked).
- * ImageTool.prototype.onSelect = function () {
- * $area.text( 'Image tool clicked!' );
+ * SearchTool.prototype.onSelect = function () {
+ * $area.text( 'Search tool clicked!' );
* // Never display this tool as "active" (selected).
* this.setActive( false );
* };
- * // The toolbar can be synchronized with the state of some external stuff, like a text
- * // editor's editing area, highlighting the tools (e.g. a 'bold' tool would be shown as active
- * // when the text cursor was inside bolded text). Here we simply disable this feature.
- * ImageTool.prototype.onUpdateState = function () {
- * };
+ * SearchTool.prototype.onUpdateState = function () {};
* // Make this tool available in our toolFactory and thus our toolbar
- * toolFactory.register( ImageTool );
+ * toolFactory.register( SearchTool );
*
* // Register two more tools, nothing interesting here
* function SettingsTool() {
* // To update the menu label
* this.toolbar.emit( 'updateState' );
* };
- * SettingsTool.prototype.onUpdateState = function () {
- * };
+ * SettingsTool.prototype.onUpdateState = function () {};
* toolFactory.register( SettingsTool );
*
* // Register two more tools, nothing interesting here
* // To update the menu label
* this.toolbar.emit( 'updateState' );
* };
- * StuffTool.prototype.onUpdateState = function () {
- * };
+ * StuffTool.prototype.onUpdateState = function () {};
* toolFactory.register( StuffTool );
*
* // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
* {
* // 'bar' tool groups display tools' icons only, side-by-side.
* type: 'bar',
- * include: [ 'image', 'help' ]
+ * include: [ 'search', 'help' ]
* },
* {
* // 'menu' tool groups display both the titles and icons, in a dropdown menu.
OO.mixinClass( OO.ui.Toolbar, OO.EventEmitter );
OO.mixinClass( OO.ui.Toolbar, OO.ui.mixin.GroupElement );
+/* Events */
+
+/**
+ * @event updateState
+ *
+ * An 'updateState' event must be emitted on the Toolbar (by calling `toolbar.emit( 'updateState' )`)
+ * every time the state of the application using the toolbar changes, and an update to the state of
+ * tools is required.
+ *
+ * @param {Mixed...} data Application-defined parameters
+ */
+
/* Methods */
/**
* to which a tool belongs determines how the tool is arranged and displayed in the toolbar. Toolgroups
* themselves are created on demand with a {@link OO.ui.ToolGroupFactory toolgroup factory}.
*
- * Toolgroups can contain individual tools, groups of tools, or all available tools:
- *
- * To include an individual tool (or array of individual tools), specify tools by symbolic name:
- *
- * include: [ 'tool-name' ] or [ { name: 'tool-name' }]
- *
- * To include a group of tools, specify the group name. (The tool's static ‘group’ config is used to assign the tool to a group.)
- *
- * include: [ { group: 'group-name' } ]
- *
- * To include all tools that are not yet assigned to a toolgroup, use the catch-all selector, an asterisk (*):
- *
- * include: '*'
+ * Toolgroups can contain individual tools, groups of tools, or all available tools, as specified
+ * using the `include` config option. See OO.ui.ToolFactory#extract on documentation of the format.
+ * The options `exclude`, `promote`, and `demote` support the same formats.
*
* See {@link OO.ui.Toolbar toolbars} for a full example. For more information about toolbars in general,
* please see the [OOjs UI documentation on MediaWiki][1].
* @constructor
* @param {OO.ui.Toolbar} toolbar
* @param {Object} [config] Configuration options
- * @cfg {Array|string} [include=[]] List of tools to include in the toolgroup.
- * @cfg {Array|string} [exclude=[]] List of tools to exclude from the toolgroup.
- * @cfg {Array|string} [promote=[]] List of tools to promote to the beginning of the toolgroup.
- * @cfg {Array|string} [demote=[]] List of tools to demote to the end of the toolgroup.
+ * @cfg {Array|string} [include] List of tools to include in the toolgroup, see above.
+ * @cfg {Array|string} [exclude] List of tools to exclude from the toolgroup, see above.
+ * @cfg {Array|string} [promote] List of tools to promote to the beginning of the toolgroup, see above.
+ * @cfg {Array|string} [demote] List of tools to demote to the end of the toolgroup, see above.
* This setting is particularly useful when tools have been added to the toolgroup
* en masse (e.g., via the catch-all selector).
*/
OO.ui.ToolGroup.prototype.onMouseKeyDown = function ( e ) {
if (
!this.isDisabled() &&
- ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
) {
this.pressed = this.getTargetTool( e );
if ( this.pressed ) {
this.pressed.setActive( true );
- OO.ui.addCaptureEventListener( this.getElementDocument(), 'mouseup', this.onCapturedMouseKeyUpHandler );
- OO.ui.addCaptureEventListener( this.getElementDocument(), 'keyup', this.onCapturedMouseKeyUpHandler );
+ this.getElementDocument().addEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
+ this.getElementDocument().addEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
}
return false;
}
* @param {Event} e Mouse up or key up event
*/
OO.ui.ToolGroup.prototype.onCapturedMouseKeyUp = function ( e ) {
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mouseup', this.onCapturedMouseKeyUpHandler );
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'keyup', this.onCapturedMouseKeyUpHandler );
+ this.getElementDocument().removeEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
+ this.getElementDocument().removeEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
// onMouseKeyUp may be called a second time, depending on where the mouse is when the button is
// released, but since `this.pressed` will no longer be true, the second call will be ignored.
this.onMouseKeyUp( e );
if (
!this.isDisabled() && this.pressed && this.pressed === tool &&
- ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
) {
this.pressed.onSelect();
this.pressed = null;
*/
OO.ui.MessageDialog.static.message = null;
+// Note that OO.ui.alert() and OO.ui.confirm() rely on these.
OO.ui.MessageDialog.static.actions = [
{ action: 'accept', label: OO.ui.deferMsg( 'ooui-dialog-message-accept' ), flags: 'primary' },
{ action: 'reject', label: OO.ui.deferMsg( 'ooui-dialog-message-reject' ), flags: 'safe' }
* // Define the tools that we're going to place in our toolbar
*
* // Create a class inheriting from OO.ui.Tool
- * function ImageTool() {
- * ImageTool.parent.apply( this, arguments );
+ * function SearchTool() {
+ * SearchTool.parent.apply( this, arguments );
* }
- * OO.inheritClass( ImageTool, OO.ui.Tool );
+ * OO.inheritClass( SearchTool, OO.ui.Tool );
* // Each tool must have a 'name' (used as an internal identifier, see later) and at least one
* // of 'icon' and 'title' (displayed icon and text).
- * ImageTool.static.name = 'image';
- * ImageTool.static.icon = 'image';
- * ImageTool.static.title = 'Insert image';
+ * SearchTool.static.name = 'search';
+ * SearchTool.static.icon = 'search';
+ * SearchTool.static.title = 'Search...';
* // Defines the action that will happen when this tool is selected (clicked).
- * ImageTool.prototype.onSelect = function () {
- * $area.text( 'Image tool clicked!' );
+ * SearchTool.prototype.onSelect = function () {
+ * $area.text( 'Search tool clicked!' );
* // Never display this tool as "active" (selected).
* this.setActive( false );
* };
+ * SearchTool.prototype.onUpdateState = function () {};
* // Make this tool available in our toolFactory and thus our toolbar
- * toolFactory.register( ImageTool );
+ * toolFactory.register( SearchTool );
*
* // This is a PopupTool. Rather than having a custom 'onSelect' action, it will display a
* // little popup window (a PopupWidget).
* {
* // 'bar' tool groups display tools by icon only
* type: 'bar',
- * include: [ 'image', 'help' ]
+ * include: [ 'search', 'help' ]
* }
* ] );
*
// Only close toolgroup when a tool was actually selected
if (
!this.isDisabled() && this.pressed && this.pressed === this.getTargetTool( e ) &&
- ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
) {
this.setActive( false );
}
OO.ui.PopupToolGroup.prototype.onHandleMouseKeyUp = function ( e ) {
if (
!this.isDisabled() &&
- ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
) {
return false;
}
OO.ui.PopupToolGroup.prototype.onHandleMouseKeyDown = function ( e ) {
if (
!this.isDisabled() &&
- ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
) {
this.setActive( !this.active );
return false;
if ( this.active !== value ) {
this.active = value;
if ( value ) {
- OO.ui.addCaptureEventListener( this.getElementDocument(), 'mouseup', this.onBlurHandler );
- OO.ui.addCaptureEventListener( this.getElementDocument(), 'keyup', this.onBlurHandler );
+ this.getElementDocument().addEventListener( 'mouseup', this.onBlurHandler, true );
+ this.getElementDocument().addEventListener( 'keyup', this.onBlurHandler, true );
this.$clippable.css( 'left', '' );
// Try anchoring the popup to the left first
} );
}
} else {
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mouseup', this.onBlurHandler );
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'keyup', this.onBlurHandler );
+ this.getElementDocument().removeEventListener( 'mouseup', this.onBlurHandler, true );
+ this.getElementDocument().removeEventListener( 'keyup', this.onBlurHandler, true );
this.$element.removeClass(
'oo-ui-popupToolGroup-active oo-ui-popupToolGroup-left oo-ui-popupToolGroup-right'
);
* SettingsTool.prototype.onSelect = function () {
* this.setActive( false );
* };
+ * SettingsTool.prototype.onUpdateState = function () {};
* toolFactory.register( SettingsTool );
* // Register two more tools, nothing interesting here
* function StuffTool() {
* }
* OO.inheritClass( StuffTool, OO.ui.Tool );
* StuffTool.static.name = 'stuff';
- * StuffTool.static.icon = 'ellipsis';
+ * StuffTool.static.icon = 'search';
* StuffTool.static.title = 'Change the world';
* StuffTool.prototype.onSelect = function () {
* this.setActive( false );
* };
+ * StuffTool.prototype.onUpdateState = function () {};
* toolFactory.register( StuffTool );
* toolbar.setup( [
* {
* type: 'list',
* label: 'ListToolGroup',
* indicator: 'down',
- * icon: 'image',
+ * icon: 'ellipsis',
* title: 'This is the title, displayed when user moves the mouse over the list toolgroup',
* header: 'This is the header',
* include: [ 'settings', 'stuff' ],
// Do not close the popup when the user wants to show more/fewer tools
if (
$( e.target ).closest( '.oo-ui-tool-name-more-fewer' ).length &&
- ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+ ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
) {
// HACK: Prevent the popup list from being hidden. Skip the PopupToolGroup implementation (which
// hides the popup list when a tool is selected) and call ToolGroup's implementation directly.
* the menu label is empty. The menu can be configured with an indicator, icon, title, and/or header.
*
* MenuToolGroups are created by a {@link OO.ui.ToolGroupFactory tool group factory} when the toolbar
- * is set up. Note that all tools must define an {@link OO.ui.Tool#onUpdateState onUpdateState} method if
- * a MenuToolGroup is used.
+ * is set up.
*
* @example
* // Example of a MenuToolGroup
* // To update the menu label
* this.toolbar.emit( 'updateState' );
* };
- * SettingsTool.prototype.onUpdateState = function () {
- * };
+ * SettingsTool.prototype.onUpdateState = function () {};
* toolFactory.register( SettingsTool );
*
* function StuffTool() {
* // To update the menu label
* this.toolbar.emit( 'updateState' );
* };
- * StuffTool.prototype.onUpdateState = function () {
- * };
+ * StuffTool.prototype.onUpdateState = function () {};
* toolFactory.register( StuffTool );
*
* // Finally define which tools and in what order appear in the toolbar. Each tool may only be
OO.ui.mixin.IconElement.call( this, config );
// Properties
+ this.$content = $( '<div>' );
this.allowArbitrary = !!config.allowArbitrary;
this.$overlay = config.$overlay || this.$element;
this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend(
this.$input.on( {
focus: this.onInputFocus.bind( this ),
blur: this.onInputBlur.bind( this ),
- 'propertychange change click mouseup keydown keyup input cut paste select': this.onInputChange.bind( this ),
+ 'propertychange change click mouseup keydown keyup input cut paste select focus':
+ OO.ui.debounce( this.updateInputSize.bind( this ) ),
keydown: this.onKeyDown.bind( this ),
keypress: this.onKeyPress.bind( this )
} );
remove: 'onMenuItemsChange'
} );
this.$handle.on( {
- click: this.onClick.bind( this )
+ mousedown: this.onMouseDown.bind( this )
} );
// Initialization
role: 'combobox',
'aria-autocomplete': 'list'
} );
- this.$input.width( '1em' );
+ this.updateInputSize();
}
if ( config.data ) {
this.setItemsFromData( config.data );
}
+ this.$content.addClass( 'oo-ui-capsuleMultiSelectWidget-content' )
+ .append( this.$group );
this.$group.addClass( 'oo-ui-capsuleMultiSelectWidget-group' );
this.$handle.addClass( 'oo-ui-capsuleMultiSelectWidget-handle' )
- .append( this.$indicator, this.$icon, this.$group );
+ .append( this.$indicator, this.$icon, this.$content );
this.$element.addClass( 'oo-ui-capsuleMultiSelectWidget' )
.append( this.$handle );
if ( this.popup ) {
- this.$handle.append( $tabFocus );
+ this.$content.append( $tabFocus );
this.$overlay.append( this.popup.$element );
} else {
- this.$handle.append( this.$input );
+ this.$content.append( this.$input );
this.$overlay.append( this.menu.$element );
}
this.onMenuItemsChange();
}
if ( !same ) {
this.emit( 'change', this.getItemsData() );
+ this.menu.position();
}
return this;
}
if ( !same ) {
this.emit( 'change', this.getItemsData() );
+ this.menu.position();
}
return this;
if ( this.items.length ) {
OO.ui.mixin.GroupElement.prototype.clearItems.call( this );
this.emit( 'change', this.getItemsData() );
+ this.menu.position();
}
return this;
};
};
/**
- * Handle mouse click events.
+ * Handle mouse down events.
*
* @private
- * @param {jQuery.Event} e Mouse click event
+ * @param {jQuery.Event} e Mouse down event
*/
-OO.ui.CapsuleMultiSelectWidget.prototype.onClick = function ( e ) {
- if ( e.which === 1 ) {
+OO.ui.CapsuleMultiSelectWidget.prototype.onMouseDown = function ( e ) {
+ if ( e.which === OO.ui.MouseButtons.LEFT ) {
this.focus();
return false;
+ } else {
+ this.updateInputSize();
}
};
}
// Make sure the input gets resized.
- setTimeout( this.onInputChange.bind( this ), 0 );
+ setTimeout( this.updateInputSize.bind( this ), 0 );
}
}
};
};
/**
- * Handle input change events.
+ * Update the dimensions of the text input field to encompass all available area.
*
* @private
* @param {jQuery.Event} e Event of some sort
*/
-OO.ui.CapsuleMultiSelectWidget.prototype.onInputChange = function () {
+OO.ui.CapsuleMultiSelectWidget.prototype.updateInputSize = function () {
+ var $lastItem, direction, contentWidth, currentWidth, bestWidth;
if ( !this.isDisabled() ) {
- this.$input.width( this.$input.val().length + 'em' );
+ this.$input.css( 'width', '1em' );
+ $lastItem = this.$group.children().last();
+ direction = OO.ui.Element.static.getDir( this.$handle );
+ contentWidth = this.$input[ 0 ].scrollWidth;
+ currentWidth = this.$input.width();
+
+ if ( contentWidth < currentWidth ) {
+ // All is fine, don't perform expensive calculations
+ return;
+ }
+
+ if ( !$lastItem.length ) {
+ bestWidth = this.$content.innerWidth();
+ } else {
+ bestWidth = direction === 'ltr' ?
+ this.$content.innerWidth() - $lastItem.position().left - $lastItem.outerWidth() :
+ $lastItem.position().left;
+ }
+ // Some safety margin for sanity, because I *really* don't feel like finding out where the few
+ // pixels this is off by are coming from.
+ bestWidth -= 10;
+ if ( contentWidth > bestWidth ) {
+ // This will result in the input getting shifted to the next line
+ bestWidth = this.$content.innerWidth() - 10;
+ }
+ this.$input.width( Math.floor( bestWidth ) );
+
+ this.menu.position();
}
};
OO.ui.CapsuleMultiSelectWidget.prototype.clearInput = function () {
if ( this.$input ) {
this.$input.val( '' );
- this.$input.width( '1em' );
+ this.updateInputSize();
}
if ( this.popup ) {
this.popup.toggle( false );
.first()
.focus();
} else {
+ this.updateInputSize();
this.menu.toggle( true );
this.$input.focus();
}
* @param {jQuery.Event} e Mouse click event
*/
OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
- if ( !this.isDisabled() && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
this.menu.toggle();
}
return false;
this.selectButton = new OO.ui.ButtonWidget( {
classes: [ 'oo-ui-selectFileWidget-selectButton' ],
- label: 'Select a file',
+ label: OO.ui.msg( 'ooui-selectfile-button-select' ),
disabled: this.disabled || !this.isSupported
} );
this.setLabel( this.placeholder );
}
}
-
- if ( this.$input ) {
- this.$input.attr( 'title', this.getLabel() );
- }
};
/**
this.$input = $( '<input type="file">' );
this.$input.on( 'change', this.onFileSelectedHandler );
this.$input.attr( {
- tabindex: -1,
- title: this.getLabel()
+ tabindex: -1
} );
if ( this.accept ) {
this.$input.attr( 'accept', this.accept.join( ', ' ) );
* @fires icon
*/
OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
- if ( e.which === 1 ) {
+ if ( e.which === OO.ui.MouseButtons.LEFT ) {
this.$input[ 0 ].focus();
return false;
}
* @fires indicator
*/
OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
- if ( e.which === 1 ) {
+ if ( e.which === OO.ui.MouseButtons.LEFT ) {
if ( this.type === 'search' ) {
// Clear the text field
this.setValue( '' );
* @chainable
*/
OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
- var textRange, isBackwards, start, end,
+ var isBackwards, start, end,
input = this.$input[ 0 ];
to = to || from;
this.focus();
- if ( input.setSelectionRange ) {
- input.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
- } else if ( input.createTextRange ) {
- // IE 8 and below
- textRange = input.createTextRange();
- textRange.collapse( true );
- textRange.moveStart( 'character', start );
- textRange.moveEnd( 'character', end - start );
- textRange.select();
- }
+ input.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
return this;
};
return this.selectRange( this.getInputLength() );
};
+/**
+ * Insert new content into the input.
+ *
+ * @param {string} content Content to be inserted
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.insertContent = function ( content ) {
+ var start, end,
+ range = this.getRange(),
+ value = this.getValue();
+
+ start = Math.min( range.from, range.to );
+ end = Math.max( range.from, range.to );
+
+ this.setValue( value.slice( 0, start ) + content + value.slice( end ) );
+ this.selectRange( start + content.length );
+ return this;
+};
+
+/**
+ * Insert new content either side of a selection.
+ *
+ * @param {string} pre Content to be inserted before the selection
+ * @param {string} post Content to be inserted after the selection
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.encapsulateContent = function ( pre, post ) {
+ var start, end,
+ range = this.getRange(),
+ offset = pre.length;
+
+ start = Math.min( range.from, range.to );
+ end = Math.max( range.from, range.to );
+
+ this.selectRange( start ).insertContent( pre );
+ this.selectRange( offset + end ).insertContent( post );
+
+ this.selectRange( offset + start, offset + end );
+ return this;
+};
+
/**
* Set the validation pattern.
*
return this;
};
-/**
- * Deprecated alias of #setLabelPosition
- *
- * @deprecated Use setLabelPosition instead.
- */
-OO.ui.TextInputWidget.prototype.setPosition =
- OO.ui.TextInputWidget.prototype.setLabelPosition;
-
/**
* Update the position of the inline label.
*
* @param {jQuery.Event} e Mouse click event
*/
OO.ui.ComboBoxInputWidget.prototype.onIndicatorClick = function ( e ) {
- if ( !this.isDisabled() && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
this.menu.toggle();
this.$input[ 0 ].focus();
}
*
* Removability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
*
- * @param {boolean} movable Item is removable
+ * @param {boolean} removable Item is removable
* @chainable
*/
OO.ui.OutlineOptionWidget.prototype.setRemovable = function ( removable ) {
*/
OO.ui.PopupWidget.prototype.bindMouseDownListener = function () {
// Capture clicks outside popup
- OO.ui.addCaptureEventListener( this.getElementWindow(), 'mousedown', this.onMouseDownHandler );
+ this.getElementWindow().addEventListener( 'mousedown', this.onMouseDownHandler, true );
};
/**
* @private
*/
OO.ui.PopupWidget.prototype.unbindMouseDownListener = function () {
- OO.ui.removeCaptureEventListener( this.getElementWindow(), 'mousedown', this.onMouseDownHandler );
+ this.getElementWindow().removeEventListener( 'mousedown', this.onMouseDownHandler, true );
};
/**
* @private
*/
OO.ui.PopupWidget.prototype.bindKeyDownListener = function () {
- OO.ui.addCaptureEventListener( this.getElementWindow(), 'keydown', this.onDocumentKeyDownHandler );
+ this.getElementWindow().addEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
};
/**
* @private
*/
OO.ui.PopupWidget.prototype.unbindKeyDownListener = function () {
- OO.ui.removeCaptureEventListener( this.getElementWindow(), 'keydown', this.onDocumentKeyDownHandler );
+ this.getElementWindow().removeEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
};
/**
OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
var item;
- if ( !this.isDisabled() && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
this.togglePressed( true );
item = this.getTargetItem( e );
if ( item && item.isSelectable() ) {
this.pressItem( item );
this.selecting = item;
- OO.ui.addCaptureEventListener(
- this.getElementDocument(),
- 'mouseup',
- this.onMouseUpHandler
- );
- OO.ui.addCaptureEventListener(
- this.getElementDocument(),
- 'mousemove',
- this.onMouseMoveHandler
- );
+ this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
+ this.getElementDocument().addEventListener( 'mousemove', this.onMouseMoveHandler, true );
}
}
return false;
this.selecting = item;
}
}
- if ( !this.isDisabled() && e.which === 1 && this.selecting ) {
+ if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT && this.selecting ) {
this.pressItem( null );
this.chooseItem( this.selecting );
this.selecting = null;
}
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mouseup',
- this.onMouseUpHandler );
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mousemove',
- this.onMouseMoveHandler );
+ this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
+ this.getElementDocument().removeEventListener( 'mousemove', this.onMouseMoveHandler, true );
return false;
};
* @protected
*/
OO.ui.SelectWidget.prototype.bindKeyDownListener = function () {
- OO.ui.addCaptureEventListener( this.getElementWindow(), 'keydown', this.onKeyDownHandler );
+ this.getElementWindow().addEventListener( 'keydown', this.onKeyDownHandler, true );
};
/**
* @protected
*/
OO.ui.SelectWidget.prototype.unbindKeyDownListener = function () {
- OO.ui.removeCaptureEventListener( this.getElementWindow(), 'keydown', this.onKeyDownHandler );
+ this.getElementWindow().removeEventListener( 'keydown', this.onKeyDownHandler, true );
};
/**
* @protected
*/
OO.ui.SelectWidget.prototype.bindKeyPressListener = function () {
- OO.ui.addCaptureEventListener( this.getElementWindow(), 'keypress', this.onKeyPressHandler );
+ this.getElementWindow().addEventListener( 'keypress', this.onKeyPressHandler, true );
};
/**
* @protected
*/
OO.ui.SelectWidget.prototype.unbindKeyPressListener = function () {
- OO.ui.removeCaptureEventListener( this.getElementWindow(), 'keypress', this.onKeyPressHandler );
+ this.getElementWindow().removeEventListener( 'keypress', this.onKeyPressHandler, true );
this.clearKeyPressBuffer();
};
// Auto-hide
if ( this.autoHide ) {
- OO.ui.addCaptureEventListener( this.getElementDocument(), 'mousedown', this.onDocumentMouseDownHandler );
+ this.getElementDocument().addEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
}
} else {
this.unbindKeyDownListener();
this.unbindKeyPressListener();
- OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mousedown', this.onDocumentMouseDownHandler );
+ this.getElementDocument().removeEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
this.toggleClipping( false );
}
}
* @param {jQuery.Event} e Mouse click event
*/
OO.ui.ToggleSwitchWidget.prototype.onClick = function ( e ) {
- if ( !this.isDisabled() && e.which === 1 ) {
+ if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
this.setValue( !this.value );
}
return false;