# all files / src/ Process.js

86.36% Statements 38/44
74.07% Branches 20/27
87.5% Functions 7/8
86.36% Lines 38/44

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166

    1×

    31×

    31×
    5×

    1×

    1×
    31×

    1×
    42×

    40×

    40×

    1×

    39×
    1×

    1×
    1×
    1×

    38×

    38×

    38×

    4×

    34×

    31×

    26×
    26×
    16×

    5×

    31×

    1×
    42×

    42×
    42×

    1×
    9×
    9×

    1×
    33×
    33×

     /**
     * A Process is a list of steps that are called in sequence. The step can be a number, a jQuery promise,
     * or a function:
     *
     * - **number**: the process will wait for the specified number of milliseconds before proceeding.
     * - **promise**: the process will continue to the next step when the promise is successfully resolved
     *  or stop if the promise is rejected.
     * - **function**: the process will execute the function. The process will stop if the function returns
     *  either a boolean `false` or a promise that is rejected; if the function returns a number, the process
     *  will wait for that number of milliseconds before proceeding.
     *
     * If the process fails, an {@link OO.ui.Error error} is generated. Depending on how the error is
     * configured, users can dismiss the error and try the process again, or not. If a process is stopped,
     * its remaining steps will not be performed.
     *
     * @class
     *
     * @constructor
     * @param {number|jQuery.Promise|Function} step Number of miliseconds to wait before proceeding, promise
     *  that must be resolved before proceeding, or a function to execute. See #createStep for more information. see #createStep for more information
     * @param {Object} [context=null] Execution context of the function. The context is ignored if the step is
     *  a number or promise.
     */
    OO.ui.Process = function ( step, context ) {
    	// Properties
    	this.steps = [];

    	// Initialization
    	if ( step !== undefined ) {
    		this.next( step, context );
    	}
    };

    /* Setup */

    OO.initClass( OO.ui.Process );

    /* Methods */

    /**
     * Start the process.
     *
     * @return {jQuery.Promise} Promise that is resolved when all steps have successfully completed.
     *  If any of the steps return a promise that is rejected or a boolean false, this promise is rejected
     *  and any remaining steps are not performed.
     */
    OO.ui.Process.prototype.execute = function () {
    	var i, len, promise;

    	/**
    	 * Continue execution.
    	 *
    	 * @ignore
    	 * @param {Array} step A function and the context it should be called in
    	 * @return {Function} Function that continues the process
    	 */
    	function proceed( step ) {
    		return function () {
    			// Execute step in the correct context
    			var deferred,
    				result = step.callback.call( step.context );

    			if ( result === false ) {
    				// Use rejected promise for boolean false results
    				return $.Deferred().reject( [] ).promise();
    			}
    			if ( typeof result === 'number' ) {
    				Iif ( result < 0 ) {
    					throw new Error( 'Cannot go back in time: flux capacitor is out of service' );
    				}
    				// Use a delayed promise for numbers, expecting them to be in milliseconds
    				deferred = $.Deferred();
    				setTimeout( deferred.resolve, result );
    				return deferred.promise();
    			}
    			Iif ( result instanceof OO.ui.Error ) {
    				// Use rejected promise for error
    				return $.Deferred().reject( [ result ] ).promise();
    			}
    			Iif ( Array.isArray( result ) && result.length && result[ 0 ] instanceof OO.ui.Error ) {
    				// Use rejected promise for list of errors
    				return $.Deferred().reject( result ).promise();
    			}
    			// Duck-type the object to see if it can produce a promise
    			if ( result && $.isFunction( result.promise ) ) {
    				// Use a promise generated from the result
    				return result.promise();
    			}
    			// Use resolved promise for other results
    			return $.Deferred().resolve().promise();
    		};
    	}

    	if ( this.steps.length ) {
    		// Generate a chain reaction of promises
    		promise = proceed( this.steps[ 0 ] )();
    		for ( i = 1, len = this.steps.length; i < len; i++ ) {
    			promise = promise.then( proceed( this.steps[ i ] ) );
    		}
    	} else {
    		promise = $.Deferred().resolve().promise();
    	}

    	return promise;
    };

    /**
     * Create a process step.
     *
     * @private
     * @param {number|jQuery.Promise|Function} step
     *
     * - Number of milliseconds to wait before proceeding
     * - Promise that must be resolved before proceeding
     * - Function to execute
     *   - If the function returns a boolean false the process will stop
     *   - If the function returns a promise, the process will continue to the next
     *     step when the promise is resolved or stop if the promise is rejected
     *   - If the function returns a number, the process will wait for that number of
     *     milliseconds before proceeding
     * @param {Object} [context=null] Execution context of the function. The context is
     *  ignored if the step is a number or promise.
     * @return {Object} Step object, with `callback` and `context` properties
     */
    OO.ui.Process.prototype.createStep = function ( step, context ) {
    	Iif ( typeof step === 'number' || $.isFunction( step.promise ) ) {
    		return {
    			callback: function () {
    				return step;
    			},
    			context: null
    		};
    	}
    	Eif ( $.isFunction( step ) ) {
    		return {
    			callback: step,
    			context: context
    		};
    	}
    	throw new Error( 'Cannot create process step: number, promise or function expected' );
    };

    /**
     * Add step to the beginning of the process.
     *
     * @inheritdoc #createStep
     * @return {OO.ui.Process} this
     * @chainable
     */
    OO.ui.Process.prototype.first = function ( step, context ) {
    	this.steps.unshift( this.createStep( step, context ) );
    	return this;
    };

    /**
     * Add step to the end of the process.
     *
     * @inheritdoc #createStep
     * @return {OO.ui.Process} this
     * @chainable
     */
    OO.ui.Process.prototype.next = function ( step, context ) {
    	this.steps.push( this.createStep( step, context ) );
    	return this;
    };