/*!
- * OOUI v0.30.4
+ * OOUI v0.31.3
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-03-07T09:14:18Z
+ * Date: 2019-04-04T19:10:48Z
*/
( function ( OO ) {
OO.ui.StackLayout.parent.call( this, config );
// Mixin constructors
- OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+ OO.ui.mixin.GroupElement.call( this, $.extend( { $group: this.$element }, config ) );
// Properties
this.currentItem = null;
* contentPanel: contentPanel
* } );
* menuLayout.$menu.append(
- * menuPanel.$element.append( '<b>Menu panel</b>', select.$element );
+ * menuPanel.$element.append( '<b>Menu panel</b>', select.$element )
* );
* menuLayout.$content.append(
* contentPanel.$element.append(
* '<b>Content panel</b>',
* '<p>Note that the menu is positioned relative to the content panel: ' +
* 'top, bottom, after, before.</p>'
- * );
+ * )
* );
* $( document.body ).append( menuLayout.$element );
*
* Set menu position.
*
* @param {string} position Position of menu, either `top`, `after`, `bottom` or `before`
- * @throws {Error} If position value is not supported
* @chainable
* @return {OO.ui.MenuLayout} The layout, for chaining
*/
OO.ui.MenuLayout.prototype.setMenuPosition = function ( position ) {
+ if ( [ 'top', 'bottom', 'before', 'after' ].indexOf( position ) === -1 ) {
+ position = 'before';
+ }
+
this.$element.removeClass( 'oo-ui-menuLayout-' + this.menuPosition );
this.menuPosition = position;
if ( this.menuPosition === 'top' || this.menuPosition === 'before' ) {
*
* @constructor
* @param {Object} [config] Configuration options
+ * @cfg {OO.ui.StackLayout} [contentPanel] Content stack (see MenuLayout)
* @cfg {boolean} [continuous=false] Show all tab panels, one after another
* @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new tab panel is
* displayed. Disabled on mobile.
// Properties
this.currentTabPanelName = null;
- this.tabPanels = {};
+ // Allow infused widgets to pass existing tabPanels
+ this.tabPanels = config.tabPanels || {};
this.ignoreFocus = false;
- this.stackLayout = new OO.ui.StackLayout( {
+ this.stackLayout = this.contentPanel || new OO.ui.StackLayout( {
continuous: !!config.continuous,
expanded: this.expanded
} );
this.setContentPanel( this.stackLayout );
this.autoFocus = config.autoFocus === undefined || !!config.autoFocus;
- this.tabSelectWidget = new OO.ui.TabSelectWidget();
- this.tabPanel = new OO.ui.PanelLayout( {
+ // Allow infused widgets to pass an existing tabSelectWidget
+ this.tabSelectWidget = config.tabSelectWidget || new OO.ui.TabSelectWidget();
+ this.tabPanel = this.menuPanel || new OO.ui.PanelLayout( {
expanded: this.expanded
} );
this.setMenuPanel( this.tabPanel );
this.tabPanel.$element
.addClass( 'oo-ui-indexLayout-tabPanel' )
.append( this.tabSelectWidget.$element );
+
+ this.selectFirstSelectableTabPanel();
};
/* Setup */
OO.ui.ToggleButtonWidget.parent.call( this, config );
// Mixin constructors
- OO.ui.mixin.ButtonElement.call( this, $.extend( {}, config, {
+ OO.ui.mixin.ButtonElement.call( this, $.extend( {
active: this.active
- } ) );
+ }, config ) );
OO.ui.mixin.IconElement.call( this, config );
OO.ui.mixin.IndicatorElement.call( this, config );
OO.ui.mixin.LabelElement.call( this, config );
OO.ui.mixin.FlaggedElement.call( this, config );
- OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, {
+ OO.ui.mixin.TabIndexedElement.call( this, $.extend( {
$tabIndexed: this.$button
- } ) );
+ }, config ) );
// Events
this.connect( this, {
this.addTagFromInput();
}
// 1. Get the label of the tag into the input
- this.input.setValue( item.getData() );
+ this.input.setValue( item.getLabel() );
// 2. Remove the tag
this.removeItems( [ item ] );
// 3. Focus the input
* @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
*/
OO.ui.MenuTagMultiselectWidget = function OoUiMenuTagMultiselectWidget( config ) {
+ var $autoCloseIgnore = $( [] );
config = config || {};
// Parent constructor
OO.ui.MenuTagMultiselectWidget.parent.call( this, config );
+ $autoCloseIgnore = $autoCloseIgnore.add( this.$group );
+ if ( this.hasInput ) {
+ $autoCloseIgnore = $autoCloseIgnore.add( this.input.$element );
+ }
+
this.$overlay = ( config.$overlay === true ?
OO.ui.getDefaultOverlay() : config.$overlay ) || this.$element;
this.clearInputOnChoose = config.clearInputOnChoose === undefined ||
!!config.clearInputOnChoose;
this.menu = this.createMenuWidget( $.extend( {
widget: this,
+ hideOnChoose: false,
input: this.hasInput ? this.input : null,
$input: this.hasInput ? this.input.$input : null,
filterFromInput: !!this.hasInput,
- highlightOnFilter: true,
- $autoCloseIgnore: this.hasInput ?
- this.input.$element : $( [] ),
+ highlightOnFilter: !this.allowArbitrary,
+ multiselect: true,
+ $autoCloseIgnore: $autoCloseIgnore,
$floatableContainer: this.hasInput && this.inputPosition === 'outline' ?
this.input.$element : this.$element,
$overlay: this.$overlay,
this.menu.toggle( true );
};
-/**
- * @inheritdoc
- */
-OO.ui.MenuTagMultiselectWidget.prototype.onInputBlur = function () {
- // Parent method
- OO.ui.MenuTagMultiselectWidget.parent.prototype.onInputBlur.call( this );
-
- this.menu.toggle( false );
-};
-
/**
* Respond to input change event
*/
};
/**
- * Respond to menu choose event
+ * Respond to menu choose event, which is intentional by the user.
*
- * @param {OO.ui.OptionWidget} menuItem Chosen menu item
+ * @param {OO.ui.OptionWidget} menuItem Selected menu items
+ * @param {boolean} selected Item is selected
*/
-OO.ui.MenuTagMultiselectWidget.prototype.onMenuChoose = function ( menuItem ) {
+OO.ui.MenuTagMultiselectWidget.prototype.onMenuChoose = function ( menuItem, selected ) {
if ( this.hasInput && this.clearInputOnChoose ) {
this.input.setValue( '' );
}
- // Add tag
- this.addTag( menuItem.getData(), menuItem.getLabel() );
+
+ if ( selected && !this.findItemFromData( menuItem.getData() ) ) {
+ // The menu item is selected, add it to the tags
+ this.addTag( menuItem.getData(), menuItem.getLabel() );
+ } else {
+ // The menu item was unselected, remove the tag
+ this.removeTagByData( menuItem.getData() );
+ }
};
/**
*/
OO.ui.MenuTagMultiselectWidget.prototype.onMenuToggle = function ( isVisible ) {
if ( !isVisible ) {
- this.menu.selectItem( null );
this.menu.highlightItem( null );
+ this.menu.scrollToTop();
}
setTimeout( function () {
// Remove MenuSelectWidget's generic focus owner ARIA attribute
this.input.setValue( '' );
}
- // Select the menu item
- this.menu.selectItem( menuItem );
-
this.focus();
+
+ // Highlight the menu item
+ this.menu.highlightItem( menuItem );
+ this.menu.scrollItemIntoView( menuItem );
+
} else {
// Use the default
OO.ui.MenuTagMultiselectWidget.parent.prototype.onTagSelect.call( this, tagItem );
}
};
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuTagMultiselectWidget.prototype.removeItems = function ( items ) {
+ var widget = this;
+
+ // Parent
+ OO.ui.MenuTagMultiselectWidget.parent.prototype.removeItems.call( this, items );
+
+ items.forEach( function ( tagItem ) {
+ var menuItem = widget.menu.findItemFromData( tagItem.getData() );
+ if ( menuItem ) {
+ // Synchronize the menu selection - unselect the removed tag
+ widget.menu.unselectItem( menuItem );
+ }
+ } );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.MenuTagMultiselectWidget.prototype.setValue = function ( valueObject ) {
+ valueObject = Array.isArray( valueObject ) ? valueObject : [ valueObject ];
+
+ // We override this method from the parent, to make sure we are adding proper
+ // menu items, and are accounting for cases where we have this widget with
+ // a menu but also 'allowArbitrary'
+ if ( !this.menu ) {
+ return;
+ }
+
+ this.clearItems();
+ valueObject.forEach( function ( obj ) {
+ var data, label, menuItem;
+
+ if ( typeof obj === 'string' ) {
+ data = label = obj;
+ } else {
+ data = obj.data;
+ label = obj.label;
+ }
+
+ // Check if the item is in the menu
+ menuItem = this.menu.getItemFromLabel( label ) || this.menu.findItemFromData( data );
+ if ( menuItem ) {
+ // Menu item found, add the menu item
+ this.addTag( menuItem.getData(), menuItem.getLabel() );
+ // Make sure that item is also selected
+ this.menu.selectItem( menuItem );
+ } else if ( this.allowArbitrary ) {
+ // If the item isn't in the menu, only add it if we
+ // allow for arbitrary values
+ this.addTag( data, label );
+ }
+ }.bind( this ) );
+};
+
/**
* @inheritdoc
*/
* @chainable
*/
OO.ui.MenuTagMultiselectWidget.prototype.initializeMenuSelection = function () {
- if ( !this.menu.findSelectedItem() ) {
- this.menu.highlightItem(
- this.allowArbitrary ?
- null :
- this.menu.findFirstSelectableItem()
- );
+ var highlightedItem;
+ this.menu.highlightItem(
+ this.allowArbitrary ?
+ null :
+ this.menu.findFirstSelectableItem()
+ );
+
+ highlightedItem = this.menu.findHighlightedItem();
+ // Scroll to the highlighted item, if it exists
+ if ( highlightedItem ) {
+ this.menu.scrollItemIntoView( highlightedItem );
}
};
* OO.ui.mixin.IndicatorElement indicators} and {@link OO.ui.mixin.TitledElement titles}.
* Please see the [OOUI documentation on MediaWiki] [1] for more information and examples.
*
+ * Although SelectFileWidget inherits from SelectFileInputWidget, it does not
+ * behave as an InputWidget, and can't be used in HTML forms.
+ *
* @example
* // A file select widget.
* var selectFile = new OO.ui.SelectFileWidget();
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets
*
* @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.mixin.IconElement
- * @mixins OO.ui.mixin.IndicatorElement
+ * @extends OO.ui.SelectFileInputWidget
* @mixins OO.ui.mixin.PendingElement
- * @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string[]|null} [accept=null] MIME types to accept. null accepts all types.
- * @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} [buttonOnly=false] Show only the select file button, no info field. Requires
+ * showDropTarget to be false.
* @cfg {boolean} [showDropTarget=false] Whether to show a drop target. Requires droppable to be
- * true.
+ * true. Not yet supported in multiple file mode.
* @cfg {number} [thumbnailSizeLimit=20] File size limit in MiB above which to not try and show a
* preview (for performance).
*/
OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) {
- var dragHandler;
+ var dragHandler, droppable,
+ isSupported = this.constructor.static.isSupported();
// Configuration initialization
config = $.extend( {
- accept: null,
- placeholder: OO.ui.msg( 'ooui-selectfile-placeholder' ),
notsupported: OO.ui.msg( 'ooui-selectfile-not-supported' ),
droppable: true,
+ buttonOnly: false,
showDropTarget: false,
thumbnailSizeLimit: 20
}, config );
+ if ( !isSupported ) {
+ config.disabled = true;
+ }
+
// Parent constructor
OO.ui.SelectFileWidget.parent.call( this, config );
// 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.$info
- } ) );
- OO.ui.mixin.LabelElement.call( this, config );
- OO.ui.mixin.TitledElement.call( this, config );
+ OO.ui.mixin.PendingElement.call( this );
- // Properties
- this.$info = $( '<span>' );
- this.showDropTarget = config.showDropTarget;
- this.thumbnailSizeLimit = config.thumbnailSizeLimit;
- this.isSupported = this.constructor.static.isSupported();
- this.currentFile = null;
- if ( Array.isArray( config.accept ) ) {
- this.accept = config.accept;
- } else {
- this.accept = null;
+ if ( !isSupported ) {
+ this.info.setValue( config.notsupported );
}
- this.placeholder = config.placeholder;
- this.notsupported = config.notsupported;
- this.onFileSelectedHandler = this.onFileSelected.bind( this );
- this.selectButton = new OO.ui.ButtonWidget( {
- $element: $( '<label>' ),
- classes: [ 'oo-ui-selectFileWidget-selectButton' ],
- label: OO.ui.msg( 'ooui-selectfile-button-select' ),
- disabled: this.disabled || !this.isSupported
- } );
-
- this.clearButton = new OO.ui.ButtonWidget( {
- classes: [ 'oo-ui-selectFileWidget-clearButton' ],
- framed: false,
- icon: 'clear',
- disabled: this.disabled
- } );
+ // Properties
+ droppable = config.droppable && isSupported;
+ // TODO: Support drop target when multiple is set
+ this.showDropTarget = droppable && config.showDropTarget && !this.multiple;
+ this.thumbnailSizeLimit = config.thumbnailSizeLimit;
// Events
- this.selectButton.$button.on( {
- keypress: this.onKeyPress.bind( this )
- } );
- this.clearButton.connect( this, {
- click: 'onClearClick'
- } );
- if ( config.droppable ) {
+ if ( droppable ) {
dragHandler = this.onDragEnterOrOver.bind( this );
this.$element.on( {
dragenter: dragHandler,
}
// Initialization
- this.addInput();
- this.$label.addClass( 'oo-ui-selectFileWidget-label' );
- this.$info
- .addClass( 'oo-ui-selectFileWidget-info' )
- .append( this.$icon, this.$label, this.clearButton.$element, this.$indicator );
-
- if ( config.droppable && config.showDropTarget ) {
+ if ( this.showDropTarget ) {
this.selectButton.setIcon( 'upload' );
this.$thumbnail = $( '<div>' ).addClass( 'oo-ui-selectFileWidget-thumbnail' );
this.setPendingElement( this.$thumbnail );
this.$element
- .addClass( 'oo-ui-selectFileWidget-dropTarget oo-ui-selectFileWidget' )
+ .addClass( 'oo-ui-selectFileWidget-dropTarget' )
.on( {
click: this.onDropTargetClick.bind( this )
} )
.append(
this.$thumbnail,
- this.$info,
+ this.info.$element,
this.selectButton.$element,
$( '<span>' )
.addClass( 'oo-ui-selectFileWidget-dropLabel' )
.text( OO.ui.msg( 'ooui-selectfile-dragdrop-placeholder' ) )
);
- } else {
- this.$element
- .addClass( 'oo-ui-selectFileWidget' )
- .append( this.$info, this.selectButton.$element );
+ this.fieldLayout.$element.remove();
+ } else if ( config.buttonOnly ) {
+ this.$element.append( this.selectButton.$element );
+ this.fieldLayout.$element.remove();
}
+
+ this.$input
+ .on( 'click', function ( e ) {
+ // Prevents dropTarget to get clicked which calls
+ // a click on this input
+ e.stopPropagation();
+ } );
+
+ this.$element.addClass( 'oo-ui-selectFileWidget' );
+
this.updateUI();
};
/* Setup */
-OO.inheritClass( OO.ui.SelectFileWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IconElement );
-OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IndicatorElement );
+OO.inheritClass( OO.ui.SelectFileWidget, OO.ui.SelectFileInputWidget );
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.TitledElement );
/* Static Properties */
/**
* Get the current value of the field
*
- * @return {File|null}
+ * For single file widgets returns a File or null.
+ * For multiple file widgets returns a list of Files.
+ *
+ * @return {File|File[]|null}
*/
OO.ui.SelectFileWidget.prototype.getValue = function () {
- return this.currentFile;
+ return this.multiple ? this.currentFiles : this.currentFiles[ 0 ];
};
/**
* Set the current value of the field
*
- * @param {File|null} file File to select
+ * @param {File[]|null} files Files to select
*/
-OO.ui.SelectFileWidget.prototype.setValue = function ( file ) {
- if ( this.currentFile !== file ) {
- this.currentFile = file;
- this.updateUI();
- this.emit( 'change', this.currentFile );
+OO.ui.SelectFileWidget.prototype.setValue = function ( files ) {
+ if ( files && !this.multiple ) {
+ files = files.slice( 0, 1 );
}
-};
-/**
- * Focus the widget.
- *
- * Focusses the select file button.
- *
- * @chainable
- * @return {OO.ui.Widget} The widget, for chaining
- */
-OO.ui.SelectFileWidget.prototype.focus = function () {
- this.selectButton.focus();
- return this;
+ function comparableFile( file ) {
+ // Use extend to convert to plain objects so they can be compared.
+ return $.extend( {}, file );
+ }
+
+ if ( !OO.compare(
+ files && files.map( comparableFile ),
+ this.currentFiles && this.currentFiles.map( comparableFile )
+ ) ) {
+ this.currentFiles = files || [];
+ this.emit( 'change', this.currentFiles );
+ }
};
/**
- * Blur the widget.
- *
- * @chainable
- * @return {OO.ui.Widget} The widget, for chaining
+ * @inheritdoc
*/
-OO.ui.SelectFileWidget.prototype.blur = function () {
- this.selectButton.blur();
- return this;
+OO.ui.SelectFileWidget.prototype.getFilename = function () {
+ return this.currentFiles.map( function ( file ) {
+ return file.name;
+ } ).join( ', ' );
};
/**
+ * Disable InputWidget#onEdit listener, onFileSelected is used instead.
* @inheritdoc
*/
-OO.ui.SelectFileWidget.prototype.simulateLabelClick = function () {
- this.focus();
-};
+OO.ui.SelectFileWidget.prototype.onEdit = function () {};
/**
- * Update the user interface when a file is selected or unselected
- *
- * @protected
+ * @inheritdoc
*/
OO.ui.SelectFileWidget.prototype.updateUI = function () {
- var $label;
- if ( !this.isSupported ) {
- this.$element.addClass( 'oo-ui-selectFileWidget-notsupported' );
- this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
- this.setLabel( this.notsupported );
+ // Too early, or not supported
+ if ( !this.selectButton || !this.constructor.static.isSupported() ) {
+ return;
+ }
+
+ // Parent method
+ OO.ui.SelectFileWidget.super.prototype.updateUI.call( this );
+
+ if ( this.currentFiles.length ) {
+ this.$element.removeClass( 'oo-ui-selectFileInputWidget-empty' );
+
+ if ( this.showDropTarget ) {
+ this.pushPending();
+ this.loadAndGetImageUrl( this.currentFiles[ 0 ] ).done( function ( url ) {
+ this.$thumbnail.css( 'background-image', 'url( ' + url + ' )' );
+ }.bind( this ) ).fail( function () {
+ this.$thumbnail.append(
+ new OO.ui.IconWidget( {
+ icon: 'attachment',
+ classes: [ 'oo-ui-selectFileWidget-noThumbnail-icon' ]
+ } ).$element
+ );
+ }.bind( this ) ).always( function () {
+ this.popPending();
+ }.bind( this ) );
+ this.$element.off( 'click' );
+ }
} else {
- this.$element.addClass( 'oo-ui-selectFileWidget-supported' );
- if ( this.currentFile ) {
- this.$element.removeClass( 'oo-ui-selectFileWidget-empty' );
- $label = $( [] );
- $label = $label.add(
- $( '<span>' )
- .addClass( 'oo-ui-selectFileWidget-fileName' )
- .text( this.currentFile.name )
- );
- this.setLabel( $label );
-
- if ( this.showDropTarget ) {
- this.pushPending();
- this.loadAndGetImageUrl().done( function ( url ) {
- this.$thumbnail.css( 'background-image', 'url( ' + url + ' )' );
- }.bind( this ) ).fail( function () {
- this.$thumbnail.append(
- new OO.ui.IconWidget( {
- icon: 'attachment',
- classes: [ 'oo-ui-selectFileWidget-noThumbnail-icon' ]
- } ).$element
- );
- }.bind( this ) ).always( function () {
- this.popPending();
- }.bind( this ) );
- this.$element.off( 'click' );
- }
- } else {
- if ( this.showDropTarget ) {
- this.$element.off( 'click' );
- this.$element.on( {
- click: this.onDropTargetClick.bind( this )
- } );
- this.$thumbnail
- .empty()
- .css( 'background-image', '' );
- }
- this.$element.addClass( 'oo-ui-selectFileWidget-empty' );
- this.setLabel( this.placeholder );
+ if ( this.showDropTarget ) {
+ this.$element.off( 'click' );
+ this.$element.on( {
+ click: this.onDropTargetClick.bind( this )
+ } );
+ this.$thumbnail
+ .empty()
+ .css( 'background-image', '' );
}
+ this.$element.addClass( 'oo-ui-selectFileInputWidget-empty' );
}
};
/**
* If the selected file is an image, get its URL and load it.
*
+ * @param {File} file File
* @return {jQuery.Promise} Promise resolves with the image URL after it has loaded
*/
-OO.ui.SelectFileWidget.prototype.loadAndGetImageUrl = function () {
+OO.ui.SelectFileWidget.prototype.loadAndGetImageUrl = function ( file ) {
var deferred = $.Deferred(),
- file = this.currentFile,
reader = new FileReader();
if (
- file &&
( OO.getProp( file, 'type' ) || '' ).indexOf( 'image/' ) === 0 &&
file.size < this.thumbnailSizeLimit * 1024 * 1024
) {
};
/**
- * Add the input to the widget
- *
- * @private
- */
-OO.ui.SelectFileWidget.prototype.addInput = function () {
- if ( this.$input ) {
- this.$input.remove();
- }
-
- if ( !this.isSupported ) {
- this.$input = null;
- return;
- }
-
- this.$input = $( '<input>' ).attr( 'type', 'file' );
- this.$input.on( 'change', this.onFileSelectedHandler );
- this.$input.on( 'click', function ( e ) {
- // Prevents dropTarget to get clicked which calls
- // a click on this input
- e.stopPropagation();
- } );
- this.$input.attr( {
- tabindex: -1
- } );
- if ( this.accept ) {
- this.$input.attr( 'accept', this.accept.join( ', ' ) );
- }
- this.selectButton.$button.append( this.$input );
-};
-
-/**
- * Determine if we should accept this file
- *
- * @private
- * @param {string} mimeType File MIME type
- * @return {boolean}
- */
-OO.ui.SelectFileWidget.prototype.isAllowedType = function ( mimeType ) {
- var i, mimeTest;
-
- if ( !this.accept || !mimeType ) {
- return true;
- }
-
- for ( i = 0; i < this.accept.length; i++ ) {
- mimeTest = this.accept[ i ];
- if ( mimeTest === mimeType ) {
- return true;
- } else if ( mimeTest.substr( -2 ) === '/*' ) {
- mimeTest = mimeTest.substr( 0, mimeTest.length - 1 );
- if ( mimeType.substr( 0, mimeTest.length ) === mimeTest ) {
- return true;
- }
- }
- }
-
- return false;
-};
-
-/**
- * Handle file selection from the input
- *
- * @private
- * @param {jQuery.Event} e
+ * @inheritdoc
*/
OO.ui.SelectFileWidget.prototype.onFileSelected = function ( e ) {
- var file = OO.getProp( e.target, 'files', 0 ) || null;
+ var files;
- if ( file && !this.isAllowedType( file.type ) ) {
- file = null;
+ if ( this.inputClearing ) {
+ return;
}
- this.setValue( file );
- this.addInput();
-};
+ files = this.filterFiles( e.target.files || [] );
-/**
- * Handle clear button click events.
- *
- * @private
- * @return {undefined/boolean} False to prevent default if event is handled
- */
-OO.ui.SelectFileWidget.prototype.onClearClick = function () {
- this.setValue( null );
- return false;
-};
+ // After a file is selected clear the native widget to avoid confusion
+ this.inputClearing = true;
+ this.$input[ 0 ].value = '';
+ this.inputClearing = false;
-/**
- * Handle key press events.
- *
- * @private
- * @param {jQuery.Event} e Key press event
- * @return {undefined/boolean} False to prevent default if event is handled
- */
-OO.ui.SelectFileWidget.prototype.onKeyPress = function ( e ) {
- if ( this.isSupported && !this.isDisabled() && this.$input &&
- ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
- ) {
- this.$input.trigger( 'click' );
- return false;
- }
+ this.setValue( files );
};
/**
* @return {undefined/boolean} False to prevent default if event is handled
*/
OO.ui.SelectFileWidget.prototype.onDropTargetClick = function () {
- if ( this.isSupported && !this.isDisabled() && this.$input ) {
+ if ( !this.isDisabled() && this.$input ) {
this.$input.trigger( 'click' );
return false;
}
* @return {undefined/boolean} False to prevent default if event is handled
*/
OO.ui.SelectFileWidget.prototype.onDragEnterOrOver = function ( e ) {
- var itemOrFile,
- droppableFile = false,
+ var itemsOrFiles,
+ hasDroppableFile = false,
dt = e.originalEvent.dataTransfer;
e.preventDefault();
e.stopPropagation();
- if ( this.isDisabled() || !this.isSupported ) {
+ if ( this.isDisabled() ) {
this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
dt.dropEffect = 'none';
return false;
// 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;
+ itemsOrFiles = dt.items || dt.files;
+ if ( itemsOrFiles && itemsOrFiles.length ) {
+ if ( this.filterFiles( itemsOrFiles ).length ) {
+ hasDroppableFile = true;
}
// 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;
+ hasDroppableFile = true;
}
- this.$element.toggleClass( 'oo-ui-selectFileWidget-canDrop', droppableFile );
- if ( !droppableFile ) {
+ this.$element.toggleClass( 'oo-ui-selectFileWidget-canDrop', hasDroppableFile );
+ if ( !hasDroppableFile ) {
dt.dropEffect = 'none';
}
* @return {undefined/boolean} False to prevent default if event is handled
*/
OO.ui.SelectFileWidget.prototype.onDrop = function ( e ) {
- var file = null,
+ var files,
dt = e.originalEvent.dataTransfer;
e.preventDefault();
e.stopPropagation();
this.$element.removeClass( 'oo-ui-selectFileWidget-canDrop' );
- if ( this.isDisabled() || !this.isSupported ) {
+ if ( this.isDisabled() ) {
return false;
}
- file = OO.getProp( dt, 'files', 0 );
- if ( file && !this.isAllowedType( file.type ) ) {
- file = null;
- }
- if ( file ) {
- this.setValue( file );
- }
+ files = this.filterFiles( dt.files || [] );
+ this.setValue( files );
return false;
};
* @inheritdoc
*/
OO.ui.SelectFileWidget.prototype.setDisabled = function ( disabled ) {
+ disabled = disabled || !this.constructor.static.isSupported();
+
+ // Parent method
OO.ui.SelectFileWidget.parent.prototype.setDisabled.call( this, disabled );
- if ( this.selectButton ) {
- this.selectButton.setDisabled( disabled );
- }
- if ( this.clearButton ) {
- this.clearButton.setDisabled( disabled );
- }
- return this;
};
/**