wikimedia/mediawiki-core

View on GitHub
resources/src/mediawiki.action/mediawiki.action.protect.js

Summary

Maintainability
A
0 mins
Test Coverage
/*!
 * JavaScript for the mediawiki.action.protect module
 */
( function () {
    var cascadeableLevels = require( './config.json' ).CascadingRestrictionLevels,
        cascadeCheckbox = $( '#mwProtect-cascade' ).length ?
            OO.ui.CheckboxInputWidget.static.infuse( $( '#mwProtect-cascade' ) ) :
            false,
        mwProtectUnchained = new OO.ui.CheckboxInputWidget( {
            selected: false,
            id: 'mwProtectUnchained'
        } ),
        levelSelectors = $( '[id ^= mwProtect-level-]' ).map( function () {
            return OO.ui.infuse( this );
        } ).toArray(),
        expiryInputs = $( '[id ^= mwProtect-][id $= -expires]' ).map( function () {
            return OO.ui.infuse( this );
        } ).toArray(),
        expirySelectors = $( '[id ^= mwProtectExpirySelection-]' ).map( function () {
            return OO.ui.infuse( this );
        } ).toArray(),
        chainedInputs = [ levelSelectors, expirySelectors, expiryInputs ];

    /**
     * Enable/disable protection selectors and expiry inputs
     *
     * @ignore
     * @param {boolean} enable
     */
    function toggleUnchainedInputs( enable ) {
        chainedInputs.forEach( function ( widgets ) {
            widgets.slice( 1 ).forEach( function ( widget ) {
                widget.setDisabled( !enable );
            } );
        } );
    }

    /**
     * Are all actions protected at the same level, with the same expiry time?
     *
     * @ignore
     * @return {boolean}
     */
    function areAllTypesMatching() {
        return chainedInputs.every( function ( widgets ) {
            return widgets.every( function ( widget ) {
                return widget.getValue() === widgets[ 0 ].getValue();
            } );
        } );
    }

    /**
     * Is protection chaining off?
     *
     * @ignore
     * @return {boolean}
     */
    function isUnchained() {
        return mwProtectUnchained.isElementAttached() ? mwProtectUnchained.isSelected() : true;
    }

    /**
     * Find the highest protection level in any selector
     *
     * @ignore
     * @return {number}
     */
    function getMaxLevel() {
        return Math.max.apply( Math, levelSelectors.map( function ( widget ) {
            return widget.dropdownWidget.getMenu().getItems().map( function ( item ) {
                return item.selected;
            } ).indexOf( true );
        } ) );
    }

    /**
     * Set all protection level selectors to the same protection level
     *
     * @ignore
     * @param {number|string} val Protection level index or value
     */
    function setAllSelectors( val ) {
        levelSelectors.forEach( function ( widget ) {
            if ( typeof val === 'number' ) {
                widget.setValue( widget.dropdownWidget.getMenu().getItems()[ val ].getData() );
            } else {
                widget.setValue( val );
            }
        } );
    }

    /**
     * Set the value of all widgets to the value of the first widget
     *
     * @ignore
     * @param {OO.ui.Widget[]} widgets Array of widgets
     */
    function setAllToFirst( widgets ) {
        widgets.slice( 1 ).forEach( function ( widget ) {
            widget.setValue( widgets[ 0 ].getValue() );
        } );
    }

    /**
     * Enables or disables the cascade checkbox depending on the current selected levels
     * Disables expiry inputs when there is not protection
     *
     * @ignore
     * @return {boolean}
     */
    function updateCascadeAndExpire() {
        levelSelectors.forEach( function ( val, index ) {
            var disable = !val.getValue() || index && !isUnchained();
            expirySelectors[ index ].setDisabled( disable );
            expiryInputs[ index ].setDisabled( disable );
        } );
        if ( cascadeCheckbox ) {
            levelSelectors.some( function ( widget ) {
                if ( cascadeableLevels.indexOf( widget.getValue() ) === -1 ) {
                    cascadeCheckbox.setSelected( false ).setDisabled( true );
                    return true;
                }
                cascadeCheckbox.setDisabled( false );
                return false;
            } );
        }
    }

    // Enable on inputs on submit
    $( '#mw-Protect-Form' ).on( 'submit', function () {
        chainedInputs.forEach( function ( widgets ) {
            widgets.forEach( function ( widget ) {
                widget.setDisabled( false );
            } );
        } );
    } );

    // Change value of chained selectors and expiry inputs
    expirySelectors.forEach( function ( widget ) {
        widget.on( 'change', function ( val ) {
            if ( isUnchained() ) {
                if ( val !== 'othertime' ) {
                    expiryInputs[ expirySelectors.indexOf( widget ) ].setValue( '' );
                }
            } else {
                setAllToFirst( expirySelectors );
                if ( val !== 'othertime' ) {
                    expiryInputs[ 0 ].setValue( '' );
                    setAllToFirst( expiryInputs );
                }
            }
        } );
    } );

    // Change value of chained inputs and expiry selectors
    expiryInputs.forEach( function ( widget ) {
        widget.on( 'change', function ( val ) {
            if ( isUnchained() ) {
                if ( val ) {
                    expirySelectors[ expiryInputs.indexOf( widget ) ].setValue( 'othertime' );
                }
            } else {
                setAllToFirst( expiryInputs );
                if ( val ) {
                    expirySelectors[ 0 ].setValue( 'othertime' );
                    setAllToFirst( expirySelectors );
                }
            }
        } );
    } );

    // Change value of chained level selectors and update cascade checkbox
    levelSelectors.forEach( function ( widget ) {
        widget.on( 'change', function ( val ) {
            if ( !isUnchained() ) {
                setAllSelectors( val );
            }
            updateCascadeAndExpire();
        } );
    } );

    // If there is only one protection type, there is nothing to chain
    if ( $( '.oo-ui-panelLayout-framed .oo-ui-panelLayout-framed' ).length > 1 ) {
        mwProtectUnchained.on( 'change', function () {
            toggleUnchainedInputs( isUnchained() );
            if ( !isUnchained() ) {
                setAllSelectors( getMaxLevel() );
                setAllToFirst( expirySelectors );
                setAllToFirst( expiryInputs );
            }
            updateCascadeAndExpire();
        } ).setSelected( !areAllTypesMatching() );
        $( '.oo-ui-panelLayout-framed .oo-ui-panelLayout-framed' ).first().after(
            ( new OO.ui.FieldLayout( mwProtectUnchained, {
                label: mw.msg( 'protect-unchain-permissions' ),
                align: 'inline'
            } ) ).$element
        );
        toggleUnchainedInputs( !areAllTypesMatching() );
    }

    var reasonList = OO.ui.infuse( $( '#wpProtectReasonSelection' ) ),
        reason = OO.ui.infuse( $( '#mwProtect-reason' ) );

    // Arbitrary 75 to leave some space for the autogenerated null edit's summary
    mw.widgets.visibleCodePointLimitWithDropdown( reason, reasonList, mw.config.get( 'wgCommentCodePointLimit' ) - 75 );

    updateCascadeAndExpire();
}() );