Merge "registration: Allow extensions to specify which MW core versions they require"
[lhc/web/wiklou.git] / resources / lib / oojs-ui / oojs-ui.js
index e5f54fc..81677ed 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.6
+ * OOjs UI v0.12.8
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-08-26T00:14:36Z
+ * Date: 2015-09-08T20:55:55Z
  */
 ( function ( OO ) {
 
@@ -279,12 +279,14 @@ OO.ui.infuse = function ( idOrNode ) {
                'ooui-dialog-process-retry': 'Try again',
                // Label for process dialog retry action button, visible when describing only warnings
                'ooui-dialog-process-continue': 'Continue',
-               // Default placeholder for file selection widgets
+               // Label for the file selection widget's select file button
+               'ooui-selectfile-button-select': 'Select a file',
+               // Label for the file selection widget if file selection is not supported
                'ooui-selectfile-not-supported': 'File selection is not supported',
-               // Default placeholder for file selection widgets
+               // Label for the file selection widget when no file is currently selected
                'ooui-selectfile-placeholder': 'No file is selected',
-               // Default placeholder for file selection widgets when using drag drop UI
-               'ooui-selectfile-dragdrop-placeholder': 'Drop file here (or click to browse)',
+               // Label for the file selection widget's drop target
+               'ooui-selectfile-dragdrop-placeholder': 'Drop file here',
                // Semicolon separator
                'ooui-semicolon-separator': '; '
        };
@@ -5976,7 +5978,7 @@ OO.ui.mixin.LookupElement.prototype.populateLookupMenu = function () {
        var widget = this,
                value = this.getValue();
 
-       if ( this.lookupsDisabled ) {
+       if ( this.lookupsDisabled || this.isReadOnly() ) {
                return;
        }
 
@@ -6142,9 +6144,8 @@ OO.ui.mixin.LookupElement.prototype.setReadOnly = function ( readOnly ) {
        // Note: Calling #setReadOnly this way assumes this is mixed into an OO.ui.TextInputWidget
        OO.ui.TextInputWidget.prototype.setReadOnly.call( this, readOnly );
 
-       this.setLookupsDisabled( readOnly );
        // During construction, #setReadOnly is called before the OO.ui.mixin.LookupElement constructor
-       if ( readOnly && this.lookupMenu ) {
+       if ( this.isReadOnly() && this.lookupMenu ) {
                this.closeLookupMenu();
        }
 
@@ -7558,10 +7559,12 @@ OO.ui.Toolbar.prototype.onWindowResize = function () {
  * 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();
+       if ( !this.initialized ) {
+               this.initialized = true;
+               this.narrowThreshold = this.$group.width() + this.$actions.width();
+               $( this.getElementWindow() ).on( 'resize', this.onWindowResizeHandler );
+               this.onWindowResize();
+       }
 };
 
 /**
@@ -13697,7 +13700,6 @@ OO.ui.DropdownWidget.prototype.onKeyPress = function ( e ) {
  * @mixins OO.ui.mixin.IndicatorElement
  * @mixins OO.ui.mixin.PendingElement
  * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TabIndexedElement
  *
  * @constructor
  * @param {Object} [config] Configuration options
@@ -13705,39 +13707,40 @@ OO.ui.DropdownWidget.prototype.onKeyPress = function ( e ) {
  * @cfg {string} [placeholder] Text to display when no file is selected.
  * @cfg {string} [notsupported] Text to display when file support is missing in the browser.
  * @cfg {boolean} [droppable=true] Whether to accept files by drag and drop.
- * @cfg {boolean} [dragDropUI=false] Whether to render the drag and drop UI.
+ * @cfg {boolean} [showDropTarget=false] Whether to show a drop target. Requires droppable to be true.
+ * @cfg {boolean} [dragDropUI=false] Deprecated alias for showDropTarget
  */
 OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
-       var dragHandler,
-               placeholderMsg = ( config && config.dragDropUI ) ?
-                       'ooui-selectfile-dragdrop-placeholder' :
-                       'ooui-selectfile-placeholder';
+       var dragHandler;
+
+       // TODO: Remove in next release
+       if ( config && config.dragDropUI ) {
+               config.showDropTarget = true;
+       }
 
        // Configuration initialization
        config = $.extend( {
                accept: null,
-               placeholder: OO.ui.msg( placeholderMsg ),
+               placeholder: OO.ui.msg( 'ooui-selectfile-placeholder' ),
                notsupported: OO.ui.msg( 'ooui-selectfile-not-supported' ),
                droppable: true,
-               dragDropUI: false
+               showDropTarget: false
        }, config );
 
        // Parent constructor
        OO.ui.SelectFileWidget.parent.call( this, config );
 
-       // Properties (must be set before TabIndexedElement constructor call)
-       this.$handle = $( '<span>' );
-
        // Mixin constructors
        OO.ui.mixin.IconElement.call( this, config );
        OO.ui.mixin.IndicatorElement.call( this, config );
-       OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$handle } ) );
+       OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$info } ) );
        OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { autoFitLabel: true } ) );
-       OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$handle } ) );
 
        // Properties
-       this.active = false;
-       this.dragDropUI = config.dragDropUI;
+       this.$info = $( '<span>' );
+
+       // Properties
+       this.showDropTarget = config.showDropTarget;
        this.isSupported = this.constructor.static.isSupported();
        this.currentFile = null;
        if ( Array.isArray( config.accept ) ) {
@@ -13749,6 +13752,12 @@ OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
        this.notsupported = config.notsupported;
        this.onFileSelectedHandler = this.onFileSelected.bind( this );
 
+       this.selectButton = new OO.ui.ButtonWidget( {
+               classes: [ 'oo-ui-selectFileWidget-selectButton' ],
+               label: 'Select a file',
+               disabled: this.disabled || !this.isSupported
+       } );
+
        this.clearButton = new OO.ui.ButtonWidget( {
                classes: [ 'oo-ui-selectFileWidget-clearButton' ],
                framed: false,
@@ -13757,7 +13766,7 @@ OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
        } );
 
        // Events
-       this.$handle.on( {
+       this.selectButton.$button.on( {
                keypress: this.onKeyPress.bind( this )
        } );
        this.clearButton.connect( this, {
@@ -13765,7 +13774,7 @@ OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
        } );
        if ( config.droppable ) {
                dragHandler = this.onDragEnterOrOver.bind( this );
-               this.$handle.on( {
+               this.$element.on( {
                        dragenter: dragHandler,
                        dragover: dragHandler,
                        dragleave: this.onDragLeave.bind( this ),
@@ -13777,22 +13786,20 @@ OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
        this.addInput();
        this.updateUI();
        this.$label.addClass( 'oo-ui-selectFileWidget-label' );
-       this.$handle
-               .addClass( 'oo-ui-selectFileWidget-handle' )
+       this.$info
+               .addClass( 'oo-ui-selectFileWidget-info' )
                .append( this.$icon, this.$label, this.clearButton.$element, this.$indicator );
        this.$element
                .addClass( 'oo-ui-selectFileWidget' )
-               .append( this.$handle );
-       if ( config.droppable ) {
-               if ( config.dragDropUI ) {
-                       this.$element.addClass( 'oo-ui-selectFileWidget-dragdrop-ui' );
-                       this.$element.on( {
-                               mouseover: this.onMouseOver.bind( this ),
-                               mouseleave: this.onMouseLeave.bind( this )
+               .append( this.$info, this.selectButton.$element );
+       if ( config.droppable && config.showDropTarget ) {
+               this.$dropTarget = $( '<div>' )
+                       .addClass( 'oo-ui-selectFileWidget-dropTarget' )
+                       .text( OO.ui.msg( 'ooui-selectfile-dragdrop-placeholder' ) )
+                       .on( {
+                               click: this.onDropTargetClick.bind( this )
                        } );
-               } else {
-                       this.$element.addClass( 'oo-ui-selectFileWidget-droppable' );
-               }
+               this.$element.prepend( this.$dropTarget );
        }
 };
 
@@ -13803,7 +13810,6 @@ OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IconElement );
 OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IndicatorElement );
 OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.PendingElement );
 OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.TabIndexedElement );
 
 /* Static Properties */
 
@@ -13868,14 +13874,17 @@ OO.ui.SelectFileWidget.prototype.updateUI = function () {
                this.$element.addClass( 'oo-ui-selectFileWidget-notsupported' );
                this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
                this.setLabel( this.notsupported );
-       } else if ( this.currentFile ) {
-               this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
-               this.setLabel( this.currentFile.name +
-                       ( this.currentFile.type !== '' ? OO.ui.msg( 'ooui-semicolon-separator' ) + this.currentFile.type : '' )
-               );
        } else {
-               this.$element.addClass( 'oo-ui-selectFileWidget-empty' );
-               this.setLabel( this.placeholder );
+               this.$element.addClass( 'oo-ui-selectFileWidget-supported' );
+               if ( this.currentFile ) {
+                       this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
+                       this.setLabel( this.currentFile.name +
+                               ( this.currentFile.type !== '' ? OO.ui.msg( 'ooui-semicolon-separator' ) + this.currentFile.type : '' )
+                       );
+               } else {
+                       this.$element.addClass( 'oo-ui-selectFileWidget-empty' );
+                       this.setLabel( this.placeholder );
+               }
        }
 
        if ( this.$input ) {
@@ -13884,7 +13893,7 @@ OO.ui.SelectFileWidget.prototype.updateUI = function () {
 };
 
 /**
- * Add the input to the handle
+ * Add the input to the widget
  *
  * @private
  */
@@ -13907,31 +13916,30 @@ OO.ui.SelectFileWidget.prototype.addInput = function () {
        if ( this.accept ) {
                this.$input.attr( 'accept', this.accept.join( ', ' ) );
        }
-       this.$handle.append( this.$input );
+       this.selectButton.$button.append( this.$input );
 };
 
 /**
  * Determine if we should accept this file
  *
  * @private
- * @param {File} file
+ * @param {string} File MIME type
  * @return {boolean}
  */
-OO.ui.SelectFileWidget.prototype.isFileAcceptable = function ( file ) {
-       var i, mime, mimeTest;
+OO.ui.SelectFileWidget.prototype.isAllowedType = function ( mimeType ) {
+       var i, mimeTest;
 
-       if ( !this.accept || file.type === '' ) {
+       if ( !this.accept || !mimeType ) {
                return true;
        }
 
-       mime = file.type;
        for ( i = 0; i < this.accept.length; i++ ) {
                mimeTest = this.accept[ i ];
-               if ( mimeTest === mime ) {
+               if ( mimeTest === mimeType ) {
                        return true;
                } else if ( mimeTest.substr( -2 ) === '/*' ) {
                        mimeTest = mimeTest.substr( 0, mimeTest.length - 1 );
-                       if ( mime.substr( 0, mimeTest.length ) === mimeTest ) {
+                       if ( mimeType.substr( 0, mimeTest.length ) === mimeTest ) {
                                return true;
                        }
                }
@@ -13947,13 +13955,10 @@ OO.ui.SelectFileWidget.prototype.isFileAcceptable = function ( file ) {
  * @param {jQuery.Event} e
  */
 OO.ui.SelectFileWidget.prototype.onFileSelected = function ( e ) {
-       var file = null;
+       var file = OO.getProp( e.target, 'files', 0 ) || null;
 
-       if ( e.target.files && e.target.files[ 0 ] ) {
-               file = e.target.files[ 0 ];
-               if ( !this.isFileAcceptable( file ) ) {
-                       file = null;
-               }
+       if ( file && !this.isAllowedType( file.type ) ) {
+               file = null;
        }
 
        this.setValue( file );
@@ -13985,6 +13990,19 @@ OO.ui.SelectFileWidget.prototype.onKeyPress = function ( e ) {
        }
 };
 
+/**
+ * Handle drop target click events.
+ *
+ * @private
+ * @param {jQuery.Event} e Key press event
+ */
+OO.ui.SelectFileWidget.prototype.onDropTargetClick = function () {
+       if ( this.isSupported && !this.isDisabled() && this.$input ) {
+               this.$input.click();
+               return false;
+       }
+};
+
 /**
  * Handle drag enter and over events
  *
@@ -13992,7 +14010,8 @@ OO.ui.SelectFileWidget.prototype.onKeyPress = function ( e ) {
  * @param {jQuery.Event} e Drag event
  */
 OO.ui.SelectFileWidget.prototype.onDragEnterOrOver = function ( e ) {
-       var file = null,
+       var itemOrFile,
+               droppableFile = false,
                dt = e.originalEvent.dataTransfer;
 
        e.preventDefault();
@@ -14000,28 +14019,27 @@ OO.ui.SelectFileWidget.prototype.onDragEnterOrOver = function ( e ) {
 
        if ( this.isDisabled() || !this.isSupported ) {
                this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
-               this.setActive( false );
                dt.dropEffect = 'none';
                return false;
        }
 
-       if ( dt && dt.files && dt.files[ 0 ] ) {
-               file = dt.files[ 0 ];
-               if ( !this.isFileAcceptable( file ) ) {
-                       file = null;
+       // DataTransferItem and File both have a type property, but in Chrome files
+       // have no information at this point.
+       itemOrFile = OO.getProp( dt, 'items', 0 ) || OO.getProp( dt, 'files', 0 );
+       if ( itemOrFile ) {
+               if ( this.isAllowedType( itemOrFile.type ) ) {
+                       droppableFile = true;
                }
-       } else if ( dt && dt.types && dt.types.indexOf( 'Files' ) !== -1 ) {
-               // We know we have files so set 'file' to something truthy, we just
-               // can't know any details about them.
-               // * https://bugzilla.mozilla.org/show_bug.cgi?id=640534
-               file = 'Files exist, but details are unknown';
+       // dt.types is Array-like, but not an Array
+       } else if ( Array.prototype.indexOf.call( OO.getProp( dt, 'types' ) || [], 'Files' ) !== -1 ) {
+               // File information is not available at this point for security so just assume
+               // it is acceptable for now.
+               // https://bugzilla.mozilla.org/show_bug.cgi?id=640534
+               droppableFile = true;
        }
-       if ( file ) {
-               this.$element.addClass( 'oo-ui-selectFileWidget-canDrop' );
-               this.setActive( true );
-       } else {
-               this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
-               this.setActive( false );
+
+       this.$element.toggleClass( 'oo-ui-selectFileWidget-canDrop', droppableFile );
+       if ( !droppableFile ) {
                dt.dropEffect = 'none';
        }
 
@@ -14036,7 +14054,6 @@ OO.ui.SelectFileWidget.prototype.onDragEnterOrOver = function ( e ) {
  */
 OO.ui.SelectFileWidget.prototype.onDragLeave = function () {
        this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
-       this.setActive( false );
 };
 
 /**
@@ -14052,17 +14069,14 @@ OO.ui.SelectFileWidget.prototype.onDrop = function ( e ) {
        e.preventDefault();
        e.stopPropagation();
        this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
-       this.setActive( false );
 
        if ( this.isDisabled() || !this.isSupported ) {
                return false;
        }
 
-       if ( dt && dt.files && dt.files[ 0 ] ) {
-               file = dt.files[ 0 ];
-               if ( !this.isFileAcceptable( file ) ) {
-                       file = null;
-               }
+       file = OO.getProp( dt, 'files', 0 );
+       if ( file && !this.isAllowedType( file.type ) ) {
+               file = null;
        }
        if ( file ) {
                this.setValue( file );
@@ -14071,47 +14085,16 @@ OO.ui.SelectFileWidget.prototype.onDrop = function ( e ) {
        return false;
 };
 
-/**
- * Handle mouse over events.
- *
- * @private
- * @param {jQuery.Event} e Mouse over event
- */
-OO.ui.SelectFileWidget.prototype.onMouseOver = function () {
-       this.setActive( true );
-};
-
-/**
- * Handle mouse leave events.
- *
- * @private
- * @param {jQuery.Event} e Mouse over event
- */
-OO.ui.SelectFileWidget.prototype.onMouseLeave = function () {
-       this.setActive( false );
-};
-
 /**
  * @inheritdoc
  */
-OO.ui.SelectFileWidget.prototype.setDisabled = function ( state ) {
-       OO.ui.SelectFileWidget.parent.prototype.setDisabled.call( this, state );
-       if ( this.clearButton ) {
-               this.clearButton.setDisabled( state );
+OO.ui.SelectFileWidget.prototype.setDisabled = function ( disabled ) {
+       OO.ui.SelectFileWidget.parent.prototype.setDisabled.call( this, disabled );
+       if ( this.selectButton ) {
+               this.selectButton.setDisabled( disabled );
        }
-       return this;
-};
-
-/**
- * Set 'active' (hover) state, only matters for widgets with `dragDropUI: true`.
- *
- * @param {boolean} value Whether widget is active
- * @chainable
- */
-OO.ui.SelectFileWidget.prototype.setActive = function ( value ) {
-       if ( this.dragDropUI ) {
-               this.active = value;
-               this.updateThemeClasses();
+       if ( this.clearButton ) {
+               this.clearButton.setDisabled( disabled );
        }
        return this;
 };