diff options
Diffstat (limited to 'www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Step.js')
-rw-r--r-- | www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Step.js | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Step.js b/www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Step.js new file mode 100644 index 00000000..de46c61a --- /dev/null +++ b/www/wiki/extensions/UploadWizard/resources/controller/uw.controller.Step.js @@ -0,0 +1,346 @@ +/* + * This file is part of the MediaWiki extension UploadWizard. + * + * UploadWizard is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * UploadWizard is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with UploadWizard. If not, see <http://www.gnu.org/licenses/>. + */ + +( function ( mw, uw, OO, $ ) { + /** + * Represents a step in the wizard. + * + * @class + * @mixins OO.EventEmitter + * @abstract + * @param {uw.ui.Step} ui The UI object that controls this step. + * @param {mw.Api} api + * @param {Object} config UploadWizard config object. + */ + uw.controller.Step = function UWControllerStep( ui, api, config ) { + var step = this; + + OO.EventEmitter.call( this ); + + /** + * @property {Object} config + */ + this.config = config; + /** + * @property {mw.Api} api + */ + this.api = api; + + this.ui = ui; + + this.uploads = []; + + /** + * Upload object event handlers to be bound on load & unbound on unload. + * This is an object literal where the keys are callback names, and + * values all callback. These callbacks will be called with the + * controller as content (`this`), and the upload as first argument. + * This'll effectively be: + * `upload.on( <key>, <value>.bind( this, upload ) );` + * + * @property {Object} + * + */ + this.uploadHandlers = { + 'remove-upload': this.removeUpload + }; + + this.ui.on( 'next-step', function () { + step.moveNext(); + } ); + + this.ui.on( 'previous-step', function () { + step.movePrevious(); + } ); + + /** + * @property {uw.controller.Step} nextStep + * The next step in the process. + */ + this.nextStep = null; + + /** + * @property {uw.controller.Step} previousStep + * The previous step in the process. + */ + this.previousStep = null; + }; + + OO.mixinClass( uw.controller.Step, OO.EventEmitter ); + + /** + * Set the next step in the process. + * + * @param {uw.controller.Step} step + */ + uw.controller.Step.prototype.setNextStep = function ( step ) { + this.nextStep = step; + this.ui.enableNextButton(); + }; + + /** + * Set the previous step in the process. + * + * @param {uw.controller.Step} step + */ + uw.controller.Step.prototype.setPreviousStep = function ( step ) { + this.previousStep = step; + this.ui.enablePreviousButton(); + }; + + /** + * Initialize this step. + * + * @param {mw.UploadWizardUpload[]} uploads List of uploads being carried forward. + */ + uw.controller.Step.prototype.load = function ( uploads ) { + var step = this; + + this.emit( 'load' ); + + this.uploads = uploads || []; + + // prevent the window from being closed as long as we have data + this.allowCloseWindow = mw.confirmCloseWindow( { + message: mw.message( 'mwe-upwiz-prevent-close' ).text(), + test: step.hasData.bind( this ) + } ); + + $.each( this.uploads, function ( i, upload ) { + upload.state = step.stepName; + + step.bindUploadHandlers( upload ); + } ); + + this.ui.load( uploads ); + uw.eventFlowLogger.logStep( this.stepName ); + }; + + /** + * Cleanup this step. + */ + uw.controller.Step.prototype.unload = function () { + var step = this; + + $.each( this.uploads, function ( i, upload ) { + step.unbindUploadHandlers( upload ); + } ); + + this.allowCloseWindow.release(); + this.ui.unload(); + + this.emit( 'unload' ); + }; + + /** + * Move to the next step. + */ + uw.controller.Step.prototype.moveNext = function () { + this.unload(); + + if ( this.nextStep ) { + this.nextStep.load( this.uploads ); + } + }; + + /** + * Move to the previous step. + */ + uw.controller.Step.prototype.movePrevious = function () { + this.unload(); + + if ( this.previousStep ) { + this.previousStep.load( this.uploads ); + } + }; + + /** + * Attaches controller-specific upload event handlers. + * + * @param {mw.UploadWizardUpload} upload + */ + uw.controller.Step.prototype.bindUploadHandlers = function ( upload ) { + var controller = this; + + $.each( this.uploadHandlers, function ( event, callback ) { + upload.on( event, callback, [ upload ], controller ); + } ); + }; + + /** + * Removes controller-specific upload event handlers. + * + * @param {mw.UploadWizardUpload} upload + */ + uw.controller.Step.prototype.unbindUploadHandlers = function ( upload ) { + var controller = this; + + $.each( this.uploadHandlers, function ( event, callback ) { + upload.off( event, callback, controller ); + } ); + }; + + /** + * Check if upload is able to be put through this step's changes. + * + * @return {boolean} + */ + uw.controller.Step.prototype.canTransition = function () { + return true; + }; + + /** + * Figure out what to do and what options to show after the uploads have stopped. + * Uploading has stopped for one of the following reasons: + * 1) The user removed all uploads before they completed, in which case we are at upload.length === 0. We should start over and allow them to add new ones + * 2) All succeeded - show link to next step + * 3) Some failed, some succeeded - offer them the chance to retry the failed ones or go on to the next step + * 4) All failed -- have to retry, no other option + * In principle there could be other configurations, like having the uploads not all in error or stashed state, but + * we trust that this hasn't happened. + * + * For uploads that have succeeded, now is the best time to add the relevant previews and details to the DOM + * in the right order. + * + * @return {boolean} Whether all of the uploads are in a successful state. + */ + uw.controller.Step.prototype.showNext = function () { + var okCount = this.getUploadStatesCount( this.finishState ), + $buttons; + + // abort if all uploads have been removed + if ( this.uploads.length === 0 ) { + return false; + } + + this.updateProgressBarCount( okCount ); + + $buttons = this.ui.$div.find( '.mwe-upwiz-buttons' ); + $buttons.show(); + + $buttons.find( '.mwe-upwiz-file-next-all-ok' ).hide(); + $buttons.find( '.mwe-upwiz-file-next-some-failed' ).hide(); + $buttons.find( '.mwe-upwiz-file-next-all-failed' ).hide(); + + if ( okCount === this.uploads.length ) { + $buttons.find( '.mwe-upwiz-file-next-all-ok' ).show(); + return true; + } + + if ( this.getUploadStatesCount( [ 'error', 'recoverable-error' ] ) === this.uploads.length ) { + $buttons.find( '.mwe-upwiz-file-next-all-failed' ).show(); + } else if ( this.getUploadStatesCount( 'transporting' ) === 0 ) { + $buttons.find( '.mwe-upwiz-file-next-some-failed' ).show(); + } + + return false; + }; + + /** + * @param {string|string[]} states List of upload states we want the count for + * @return {number} + */ + uw.controller.Step.prototype.getUploadStatesCount = function ( states ) { + var count = 0; + + // normalize to array of states, even though input can be 1 string + states = Array.isArray( states ) ? states : [ states ]; + + $.each( this.uploads, function ( i, upload ) { + if ( states.indexOf( upload.state ) > -1 ) { + count++; + } + } ); + + return count; + }; + + /** + * Function used by some steps to update progress bar for the whole + * batch of uploads. + */ + uw.controller.Step.prototype.updateProgressBarCount = function () {}; + + /** + * Check if this step has data, to test if the window can be close (i.e. if + * content is going to be lost) + * + * @return {boolean} + */ + uw.controller.Step.prototype.hasData = function () { + return this.uploads.length !== 0; + }; + + /** + * Add an upload. + * + * @param {mw.UploadWizardUpload} upload + */ + uw.controller.Step.prototype.addUpload = function ( upload ) { + this.uploads.push( upload ); + }; + + /** + * Remove an upload. + * + * @param {mw.UploadWizardUpload} upload + */ + uw.controller.Step.prototype.removeUpload = function ( upload ) { + // remove the upload from the uploads array + var index = this.uploads.indexOf( upload ); + if ( index !== -1 ) { + this.uploads.splice( index, 1 ); + } + + // let the upload object cleanup itself! + upload.remove(); + }; + + /** + * Remove multiple uploads. + * + * @param {mw.UploadWizardUpload[]} uploads + */ + uw.controller.Step.prototype.removeUploads = function ( uploads ) { + var i, + // clone the array of uploads, just to be sure it's not a reference + // to this.uploads, which will be modified (and we can't have that + // while we're looping it) + copy = uploads.slice(); + + for ( i = 0; i < copy.length; i++ ) { + this.removeUpload( copy[ i ] ); + } + }; + + /** + * Clear out uploads that are in error mode, perhaps before proceeding to the next step + */ + uw.controller.Step.prototype.removeErrorUploads = function () { + // We must not remove items from an array while iterating over it with $.each (it causes the + // next item to be skipped). Find and queue them first, then remove them. + var toRemove = []; + $.each( this.uploads, function ( i, upload ) { + if ( upload.state === 'error' || upload.state === 'recoverable-error' ) { + toRemove.push( upload ); + } + } ); + + this.removeUploads( toRemove ); + }; + +}( mediaWiki, mediaWiki.uploadWizard, OO, jQuery ) ); |