Merge "mediawiki.searchSuggest: Show full article title as a tooltip for each suggestion"
[lhc/web/wiklou.git] / resources / lib / oojs-ui / oojs-ui.js
index b5f8824..0dcadd4 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (7d2507b267)
+ * OOjs UI v0.1.0-pre (7b283a9dcc)
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: Mon May 05 2014 14:13:13 GMT-0700 (PDT)
+ * Date: Tue May 27 2014 15:41:42 GMT-0700 (PDT)
  */
 ( function ( OO ) {
 
@@ -112,7 +112,16 @@ var messages = {
        // Tool tip for a button that removes items from a list
        'ooui-outline-control-remove': 'Remove item',
        // Label for the toolbar group that contains a list of all other available tools
-       'ooui-toolbar-more': 'More'
+       'ooui-toolbar-more': 'More',
+
+       // Label for the generic dialog used to confirm things
+       'ooui-dialog-confirm-title': 'Confirm',
+       // The default prompt of a confirmation dialog
+       'ooui-dialog-confirm-default-prompt': 'Are you sure?',
+       // The default OK button text on a confirmation dialog
+       'ooui-dialog-confirm-default-ok': 'OK',
+       // The default cancel button text on a confirmation dialog
+       'ooui-dialog-confirm-default-cancel': 'Cancel'
 };
 
 /**
@@ -896,7 +905,7 @@ OO.ui.Frame.prototype.load = function () {
        this.loading = true;
 
        // Figure out directionality:
-       this.dir = this.$element.closest( '[dir]' ).prop( 'dir' ) || 'ltr';
+       this.dir = OO.ui.Element.getDir( this.$element ) || 'ltr';
 
        // Initialize contents
        doc.open();
@@ -977,6 +986,7 @@ OO.ui.Frame.prototype.setSize = function ( width, height ) {
  * @fires initialize
  */
 OO.ui.Window = function OoUiWindow( config ) {
+       var element = this;
        // Parent constructor
        OO.ui.Window.super.call( this, config );
 
@@ -1007,7 +1017,14 @@ OO.ui.Window = function OoUiWindow( config ) {
                .append( this.frame.$element );
 
        // Events
-       this.frame.connect( this, { 'load': 'initialize' } );
+       this.frame.on( 'load', function () {
+               element.initialize();
+               // Undo the visibility: hidden; hack and apply display: none;
+               // We can do this safely now that the iframe has initialized
+               // (don't do this from within #initialize because it has to happen
+               // after the all subclasses have been handled as well).
+               element.$element.hide().css( 'visibility', '' );
+       } );
 };
 
 /* Setup */
@@ -1252,12 +1269,6 @@ OO.ui.Window.prototype.initialize = function () {
                this.$overlay
        );
 
-       // Undo the visibility: hidden; hack from the constructor and apply display: none;
-       // We can do this safely now that the iframe has initialized
-       this.$element.hide().css( 'visibility', '' );
-
-       this.emit( 'initialize' );
-
        return this;
 };
 
@@ -1900,6 +1911,103 @@ OO.ui.Widget.prototype.setDisabled = function ( disabled ) {
        this.wasDisabled = isDisabled;
        return this;
 };
+/**
+ * Dialog for showing a confirmation/warning message.
+ *
+ * @class
+ * @extends OO.ui.Dialog
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.ConfirmationDialog = function OoUiConfirmationDialog( config ) {
+       // Configuration initialization
+       config = $.extend( { 'size': 'small' }, config );
+
+       // Parent constructor
+       OO.ui.Dialog.call( this, config );
+};
+
+/* Inheritance */
+
+OO.inheritClass( OO.ui.ConfirmationDialog, OO.ui.Dialog );
+
+/* Static Properties */
+
+OO.ui.ConfirmationDialog.static.name = 'confirm';
+
+OO.ui.ConfirmationDialog.static.icon = 'help';
+
+OO.ui.ConfirmationDialog.static.title = OO.ui.deferMsg( 'ooui-dialog-confirm-title' );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ConfirmationDialog.prototype.initialize = function () {
+       // Parent method
+       OO.ui.Dialog.prototype.initialize.call( this );
+
+       // Set up the layout
+       var contentLayout = new OO.ui.PanelLayout( {
+               '$': this.$,
+               'padded': true
+       } );
+
+       this.$promptContainer = this.$( '<div>' ).addClass( 'oo-ui-dialog-confirm-promptContainer' );
+
+       this.cancelButton = new OO.ui.ButtonWidget();
+       this.cancelButton.connect( this, { 'click': [ 'emit', 'cancel' ] } );
+
+       this.okButton = new OO.ui.ButtonWidget();
+       this.okButton.connect( this, { 'click': [ 'emit', 'ok' ] } );
+
+       // Make the buttons
+       contentLayout.$element.append( this.$promptContainer );
+       this.$body.append( contentLayout.$element );
+
+       this.$foot.append(
+               this.okButton.$element,
+               this.cancelButton.$element
+       );
+
+       this.connect( this, {
+               'ok': 'close',
+               'cancel': 'close',
+               'close': [ 'emit', 'cancel' ]
+       } );
+};
+
+/*
+ * Open a confirmation dialog.
+ *
+ * @param {Object} [data] Window opening data including text of the dialog and text for the buttons
+ * @param {jQuery|string} [data.prompt] Text to display or list of nodes to use as content of the dialog.
+ * @param {jQuery|string|Function|null} [data.okLabel] Label of the OK button
+ * @param {jQuery|string|Function|null} [data.cancelLabel] Label of the cancel button
+ * @param {string|string[]} [data.okFlags="constructive"] Flags for the OK button
+ * @param {string|string[]} [data.cancelFlags="destructive"] Flags for the cancel button
+ */
+OO.ui.ConfirmationDialog.prototype.setup = function ( data ) {
+       // Parent method
+       OO.ui.Dialog.prototype.setup.call( this, data );
+
+       var prompt = data.prompt || OO.ui.deferMsg( 'ooui-dialog-confirm-default-prompt' ),
+               okLabel = data.okLabel || OO.ui.deferMsg( 'ooui-dialog-confirm-default-ok' ),
+               cancelLabel = data.cancelLabel || OO.ui.deferMsg( 'ooui-dialog-confirm-default-cancel' ),
+               okFlags = data.okFlags || 'constructive',
+               cancelFlags = data.cancelFlags || 'destructive';
+
+       if ( typeof prompt === 'string' ) {
+               this.$promptContainer.text( prompt );
+       } else {
+               this.$promptContainer.empty().append( prompt );
+       }
+
+       this.okButton.setLabel( okLabel ).clearFlags().setFlags( okFlags );
+       this.cancelButton.setLabel( cancelLabel ).clearFlags().setFlags( cancelFlags );
+};
 /**
  * Element with a button.
  *
@@ -2191,15 +2299,19 @@ OO.ui.FlaggableElement.prototype.clearFlags = function () {
 /**
  * Add one or more flags.
  *
- * @param {string[]|Object.<string, boolean>} flags List of flags to add, or list of set/remove
- *  values, keyed by flag name
+ * @param {string|string[]|Object.<string, boolean>} flags One or more flags to add, or an object
+ *  keyed by flag name containing boolean set/remove instructions.
  * @chainable
  */
 OO.ui.FlaggableElement.prototype.setFlags = function ( flags ) {
        var i, len, flag,
                classPrefix = 'oo-ui-flaggableElement-';
 
-       if ( $.isArray( flags ) ) {
+       if ( typeof flags === 'string' ) {
+               // Set
+               this.flags[flags] = true;
+               this.$element.addClass( classPrefix + flags );
+       } else if ( $.isArray( flags ) ) {
                for ( i = 0, len = flags.length; i < len; i++ ) {
                        flag = flags[i];
                        // Set
@@ -3960,14 +4072,14 @@ OO.ui.GridLayout = function OoUiGridLayout( panels, config ) {
                this.$element.append( panels[i].$element );
        }
        if ( config.widths || config.heights ) {
-               this.layout( config.widths || [1], config.heights || [1] );
+               this.layout( config.widths || [ 1 ], config.heights || [ 1 ] );
        } else {
                // Arrange in columns by default
                widths = [];
                for ( i = 0, len = this.panels.length; i < len; i++ ) {
                        widths[i] = 1;
                }
-               this.layout( widths, [1] );
+               this.layout( widths, [ 1 ] );
        }
 };
 
@@ -4123,7 +4235,8 @@ OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
                this.outlineWidget = new OO.ui.OutlineWidget( { '$': this.$ } );
                this.outlinePanel = new OO.ui.PanelLayout( { '$': this.$, 'scrollable': true } );
                this.gridLayout = new OO.ui.GridLayout(
-                       [this.outlinePanel, this.stackLayout], { '$': this.$, 'widths': [1, 2] }
+                       [ this.outlinePanel, this.stackLayout ],
+                       { '$': this.$, 'widths': [ 1, 2 ] }
                );
                this.outlineVisible = true;
                if ( this.editable ) {
@@ -4534,9 +4647,6 @@ OO.ui.PanelLayout = function OoUiPanelLayout( config ) {
        if ( config.padded ) {
                this.$element.addClass( 'oo-ui-panelLayout-padded' );
        }
-
-       // Add directionality class:
-       this.$element.addClass( 'oo-ui-' + OO.ui.Element.getDir( this.$.context ) );
 };
 
 /* Setup */
@@ -4777,7 +4887,6 @@ OO.ui.StackLayout.prototype.setItem = function ( item ) {
 /**
  * Horizontal bar layout of tools as icon buttons.
  *
- * @abstract
  * @class
  * @extends OO.ui.ToolGroup
  *
@@ -4819,6 +4928,7 @@ OO.ui.BarToolGroup.static.name = 'bar';
  * @constructor
  * @param {OO.ui.Toolbar} toolbar
  * @param {Object} [config] Configuration options
+ * @cfg {string} [header] Text to display at the top of the pop-up
  */
 OO.ui.PopupToolGroup = function OoUiPopupToolGroup( toolbar, config ) {
        // Configuration initialization
@@ -4850,6 +4960,16 @@ OO.ui.PopupToolGroup = function OoUiPopupToolGroup( toolbar, config ) {
        this.$handle
                .addClass( 'oo-ui-popupToolGroup-handle' )
                .append( this.$icon, this.$label, this.$indicator );
+       // If the pop-up should have a header, add it to the top of the toolGroup.
+       // Note: If this feature is useful for other widgets, we could abstract it into an
+       // OO.ui.HeaderedElement mixin constructor.
+       if ( config.header !== undefined ) {
+               this.$group
+                       .prepend( this.$( '<span>' )
+                               .addClass( 'oo-ui-popupToolGroup-header' )
+                               .text( config.header )
+                       );
+       }
        this.$element
                .addClass( 'oo-ui-popupToolGroup' )
                .prepend( this.$handle );
@@ -4948,7 +5068,6 @@ OO.ui.PopupToolGroup.prototype.setActive = function ( value ) {
 /**
  * Drop down list layout of tools as labeled icon buttons.
  *
- * @abstract
  * @class
  * @extends OO.ui.PopupToolGroup
  *
@@ -4976,7 +5095,6 @@ OO.ui.ListToolGroup.static.name = 'list';
 /**
  * Drop down menu layout of tools as selectable menu items.
  *
- * @abstract
  * @class
  * @extends OO.ui.PopupToolGroup
  *
@@ -5283,7 +5401,6 @@ OO.mixinClass( OO.ui.ButtonGroupWidget, OO.ui.GroupElement );
 /**
  * Button widget.
  *
- * @abstract
  * @class
  * @extends OO.ui.Widget
  * @mixins OO.ui.ButtonedElement
@@ -5373,9 +5490,9 @@ OO.ui.ButtonWidget.prototype.onClick = function () {
  * @fires click
  */
 OO.ui.ButtonWidget.prototype.onKeyPress = function ( e ) {
-       if ( !this.isDisabled() && e.which === OO.ui.Keys.SPACE ) {
+       if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
+               this.onClick();
                if ( this.isHyperlink ) {
-                       this.onClick();
                        return true;
                }
        }
@@ -5932,7 +6049,6 @@ OO.ui.LookupInputWidget.prototype.getLookupMenuItemsFromData = function () {
  *
  * Use with OO.ui.SelectWidget.
  *
- * @abstract
  * @class
  * @extends OO.ui.Widget
  * @mixins OO.ui.IconedElement
@@ -6150,7 +6266,6 @@ OO.ui.OptionWidget.prototype.getData = function () {
  *
  * Use together with OO.ui.OptionWidget.
  *
- * @abstract
  * @class
  * @extends OO.ui.Widget
  * @mixins OO.ui.GroupElement
@@ -6679,6 +6794,7 @@ OO.inheritClass( OO.ui.MenuItemWidget, OO.ui.OptionWidget );
  * @constructor
  * @param {Object} [config] Configuration options
  * @cfg {OO.ui.InputWidget} [input] Input to bind keyboard handlers to
+ * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu
  */
 OO.ui.MenuWidget = function OoUiMenuWidget( config ) {
        // Config intialization
@@ -6691,6 +6807,7 @@ OO.ui.MenuWidget = function OoUiMenuWidget( config ) {
        OO.ui.ClippableElement.call( this, this.$group, config );
 
        // Properties
+       this.autoHide = config.autoHide === undefined || !!config.autoHide;
        this.newItems = null;
        this.$input = config.input ? config.input.$input : null;
        this.$previousFocus = null;
@@ -6698,6 +6815,7 @@ OO.ui.MenuWidget = function OoUiMenuWidget( config ) {
        this.visible = false;
        this.flashing = false;
        this.onKeyDownHandler = OO.ui.bind( this.onKeyDown, this );
+       this.onDocumentMouseDownHandler = OO.ui.bind( this.onDocumentMouseDown, this );
 
        // Initialization
        this.$element.hide().addClass( 'oo-ui-menuWidget' );
@@ -6710,6 +6828,17 @@ OO.mixinClass( OO.ui.MenuWidget, OO.ui.ClippableElement );
 
 /* Methods */
 
+/**
+ * Handles document mouse down events.
+ *
+ * @param {jQuery.Event} e Key down event
+ */
+OO.ui.MenuWidget.prototype.onDocumentMouseDown = function ( e ) {
+       if ( !$.contains( this.$element[0], e.target ) ) {
+               this.hide();
+       }
+};
+
 /**
  * Handles key down events.
  *
@@ -6875,6 +7004,13 @@ OO.ui.MenuWidget.prototype.show = function () {
                }
 
                this.setClipping( true );
+
+               // Auto-hide
+               if ( this.autoHide ) {
+                       this.getElementDocument().addEventListener(
+                               'mousedown', this.onDocumentMouseDownHandler, true
+                       );
+               }
        }
 
        return this;
@@ -6895,6 +7031,10 @@ OO.ui.MenuWidget.prototype.hide = function () {
                this.$previousFocus = null;
        }
 
+       this.getElementDocument().removeEventListener(
+               'mousedown', this.onDocumentMouseDownHandler, true
+       );
+
        this.setClipping( false );
 
        return this;
@@ -7110,9 +7250,9 @@ OO.ui.OutlineControlsWidget = function OoUiOutlineControlsWidget( outline, confi
                'add': 'onOutlineChange',
                'remove': 'onOutlineChange'
        } );
-       this.upButton.connect( this, { 'click': ['emit', 'move', -1] } );
-       this.downButton.connect( this, { 'click': ['emit', 'move', 1] } );
-       this.removeButton.connect( this, { 'click': ['emit', 'remove'] } );
+       this.upButton.connect( this, { 'click': [ 'emit', 'move', -1 ] } );
+       this.downButton.connect( this, { 'click': [ 'emit', 'move', 1 ] } );
+       this.removeButton.connect( this, { 'click': [ 'emit', 'remove' ] } );
 
        // Initialization
        this.$element.addClass( 'oo-ui-outlineControlsWidget' );
@@ -8248,7 +8388,6 @@ OO.ui.ToggleButtonWidget.prototype.setValue = function ( value ) {
 /**
  * Switch that slides on and off.
  *
- * @abstract
  * @class
  * @extends OO.ui.Widget
  * @mixins OO.ui.ToggleWidget