/** * ProcessDialog windows encapsulate a {@link OO.ui.Process process} and all of the code necessary * to complete it. If the process terminates with an error, a customizable {@link OO.ui.Error error * interface} alerts users to the trouble, permitting the user to dismiss the error and try again when * relevant. The ProcessDialog class is always extended and customized with the actions and content * required for each process. * * The process dialog box consists of a header that visually represents the ‘working’ state of long * processes with an animation. The header contains the dialog title as well as * two {@link OO.ui.ActionWidget action widgets}: a ‘safe’ action on the left (e.g., ‘Cancel’) and * a ‘primary’ action on the right (e.g., ‘Done’). * * Like other windows, the process dialog is managed by a {@link OO.ui.WindowManager window manager}. * Please see the [OOjs UI documentation on MediaWiki][1] for more information and examples. * * @example * // Example: Creating and opening a process dialog window. * function MyProcessDialog( config ) { * MyProcessDialog.super.call( this, config ); * } * OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog ); * * MyProcessDialog.static.title = 'Process dialog'; * MyProcessDialog.static.actions = [ * { action: 'save', label: 'Done', flags: 'primary' }, * { label: 'Cancel', flags: 'safe' } * ]; * * MyProcessDialog.prototype.initialize = function () { * MyProcessDialog.super.prototype.initialize.apply( this, arguments ); * this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } ); * this.content.$element.append( '

This is a process dialog window. The header contains the title and two buttons: \'Cancel\' (a safe action) on the left and \'Done\' (a primary action) on the right.

' ); * this.$body.append( this.content.$element ); * }; * MyProcessDialog.prototype.getActionProcess = function ( action ) { * var dialog = this; * if ( action ) { * return new OO.ui.Process( function () { * dialog.close( { action: action } ); * } ); * } * return MyProcessDialog.super.prototype.getActionProcess.call( this, action ); * }; * * var windowManager = new OO.ui.WindowManager(); * $( 'body' ).append( windowManager.$element ); * * var dialog = new MyProcessDialog(); * windowManager.addWindows( [ dialog ] ); * windowManager.openWindow( dialog ); * * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs * * @abstract * @class * @extends OO.ui.Dialog * * @constructor * @param {Object} [config] Configuration options */ OO.ui.ProcessDialog = function OoUiProcessDialog( config ) { // Parent constructor OO.ui.ProcessDialog.super.call( this, config ); // Initialization this.$element.addClass( 'oo-ui-processDialog' ); }; /* Setup */ OO.inheritClass( OO.ui.ProcessDialog, OO.ui.Dialog ); /* Methods */ /** * Handle dismiss button click events. * * Hides errors. * * @private */ OO.ui.ProcessDialog.prototype.onDismissErrorButtonClick = function () { this.hideErrors(); }; /** * Handle retry button click events. * * Hides errors and then tries again. * * @private */ OO.ui.ProcessDialog.prototype.onRetryButtonClick = function () { this.hideErrors(); this.executeAction( this.currentAction ); }; /** * @inheritdoc */ OO.ui.ProcessDialog.prototype.onActionResize = function ( action ) { if ( this.actions.isSpecial( action ) ) { this.fitLabel(); } return OO.ui.ProcessDialog.super.prototype.onActionResize.call( this, action ); }; /** * @inheritdoc */ OO.ui.ProcessDialog.prototype.initialize = function () { // Parent method OO.ui.ProcessDialog.super.prototype.initialize.call( this ); // Properties this.$navigation = $( '
' ); this.$location = $( '
' ); this.$safeActions = $( '
' ); this.$primaryActions = $( '
' ); this.$otherActions = $( '
' ); this.dismissButton = new OO.ui.ButtonWidget( { label: OO.ui.msg( 'ooui-dialog-process-dismiss' ) } ); this.retryButton = new OO.ui.ButtonWidget(); this.$errors = $( '
' ); this.$errorsTitle = $( '
' ); // Events this.dismissButton.connect( this, { click: 'onDismissErrorButtonClick' } ); this.retryButton.connect( this, { click: 'onRetryButtonClick' } ); // Initialization this.title.$element.addClass( 'oo-ui-processDialog-title' ); this.$location .append( this.title.$element ) .addClass( 'oo-ui-processDialog-location' ); this.$safeActions.addClass( 'oo-ui-processDialog-actions-safe' ); this.$primaryActions.addClass( 'oo-ui-processDialog-actions-primary' ); this.$otherActions.addClass( 'oo-ui-processDialog-actions-other' ); this.$errorsTitle .addClass( 'oo-ui-processDialog-errors-title' ) .text( OO.ui.msg( 'ooui-dialog-process-error' ) ); this.$errors .addClass( 'oo-ui-processDialog-errors oo-ui-element-hidden' ) .append( this.$errorsTitle, this.dismissButton.$element, this.retryButton.$element ); this.$content .addClass( 'oo-ui-processDialog-content' ) .append( this.$errors ); this.$navigation .addClass( 'oo-ui-processDialog-navigation' ) .append( this.$safeActions, this.$location, this.$primaryActions ); this.$head.append( this.$navigation ); this.$foot.append( this.$otherActions ); }; /** * @inheritdoc */ OO.ui.ProcessDialog.prototype.getActionWidgets = function ( actions ) { var i, len, widgets = []; for ( i = 0, len = actions.length; i < len; i++ ) { widgets.push( new OO.ui.ActionWidget( $.extend( { framed: true }, actions[ i ] ) ) ); } return widgets; }; /** * @inheritdoc */ OO.ui.ProcessDialog.prototype.attachActions = function () { var i, len, other, special, others; // Parent method OO.ui.ProcessDialog.super.prototype.attachActions.call( this ); special = this.actions.getSpecial(); others = this.actions.getOthers(); if ( special.primary ) { this.$primaryActions.append( special.primary.$element ); } for ( i = 0, len = others.length; i < len; i++ ) { other = others[ i ]; this.$otherActions.append( other.$element ); } if ( special.safe ) { this.$safeActions.append( special.safe.$element ); } this.fitLabel(); this.$body.css( 'bottom', this.$foot.outerHeight( true ) ); }; /** * @inheritdoc */ OO.ui.ProcessDialog.prototype.executeAction = function ( action ) { var process = this; return OO.ui.ProcessDialog.super.prototype.executeAction.call( this, action ) .fail( function ( errors ) { process.showErrors( errors || [] ); } ); }; /** * Fit label between actions. * * @private * @chainable */ OO.ui.ProcessDialog.prototype.fitLabel = function () { var width = Math.max( this.$safeActions.is( ':visible' ) ? this.$safeActions.width() : 0, this.$primaryActions.is( ':visible' ) ? this.$primaryActions.width() : 0 ); this.$location.css( { paddingLeft: width, paddingRight: width } ); return this; }; /** * Handle errors that occurred during accept or reject processes. * * @private * @param {OO.ui.Error[]|OO.ui.Error} errors Errors to be handled */ OO.ui.ProcessDialog.prototype.showErrors = function ( errors ) { var i, len, $item, actions, items = [], abilities = {}, recoverable = true, warning = false; if ( errors instanceof OO.ui.Error ) { errors = [ errors ]; } for ( i = 0, len = errors.length; i < len; i++ ) { if ( !errors[ i ].isRecoverable() ) { recoverable = false; } if ( errors[ i ].isWarning() ) { warning = true; } $item = $( '
' ) .addClass( 'oo-ui-processDialog-error' ) .append( errors[ i ].getMessage() ); items.push( $item[ 0 ] ); } this.$errorItems = $( items ); if ( recoverable ) { abilities[this.currentAction] = true; // Copy the flags from the first matching action actions = this.actions.get( { actions: this.currentAction } ); if ( actions.length ) { this.retryButton.clearFlags().setFlags( actions[0].getFlags() ); } } else { abilities[this.currentAction] = false; this.actions.setAbilities( abilities ); } if ( warning ) { this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-continue' ) ); } else { this.retryButton.setLabel( OO.ui.msg( 'ooui-dialog-process-retry' ) ); } this.retryButton.toggle( recoverable ); this.$errorsTitle.after( this.$errorItems ); this.$errors.removeClass( 'oo-ui-element-hidden' ).scrollTop( 0 ); }; /** * Hide errors. * * @private */ OO.ui.ProcessDialog.prototype.hideErrors = function () { this.$errors.addClass( 'oo-ui-element-hidden' ); if ( this.$errorItems ) { this.$errorItems.remove(); this.$errorItems = null; } }; /** * @inheritdoc */ OO.ui.ProcessDialog.prototype.getTeardownProcess = function ( data ) { // Parent method return OO.ui.ProcessDialog.super.prototype.getTeardownProcess.call( this, data ) .first( function () { // Make sure to hide errors this.hideErrors(); }, this ); };