wikimedia/mediawiki-extensions-Translate

View on GitHub
resources/js/ext.translate.proofread.js

Summary

Maintainability
D
1 day
Test Coverage
( function () {
    'use strict';

    /**
     * Proofread Plugin
     *
     * Prepare a proofread UI with all the required actions
     * for a translation unit (message).
     * This is mainly used with the messagetable plugin in proofread mode,
     * but it is independent of messagetable.
     * Example usage:
     *
     * $( 'div.proofread' ).proofread( {
     *     message: messageObject, // Mandatory message object
     *     sourcelangcode: 'en', // Mandatory source language code
     *     targetlangcode: 'hi' // Mandatory target language code
     * } );
     *
     * @private
     * @param {Element} element
     * @param {Object} options
     * @param {Object} options.message
     * @param {string} options.sourcelangcode Language code.
     * @param {string} options.targetlangcode Language code.
     */
    function Proofread( element, options ) {
        this.$message = $( element );
        this.options = options;
        this.message = this.options.message;
        this.init();
        this.listen();
    }

    Proofread.prototype = {

        /**
         * Initialize the plugin
         *
         * @private
         */
        init: function () {
            var that = this;

            this.render();

            // No review before translating.
            if ( !this.message.translation ) {
                this.disableProofread();
            }

            // No review for fuzzy messages.
            if ( this.message.properties.status === 'fuzzy' ) {
                this.disableProofread();
            }

            if ( !mw.translate.canProofread() ) {
                this.disableProofread();
            }

            this.$message.translateeditor( {
                message: this.message,
                beforeSave: function ( translation ) {
                    that.$message.find( '.tux-proofread-translation' )
                        .html( mw.translate.formatMessageGently( translation || '', that.message.key ) )
                        .addClass( 'highlight' );
                },
                onSave: function ( translation ) {
                    that.$message.find( '.tux-proofread-translation' )
                        .text( translation )
                        .removeClass( 'highlight' );
                    that.message.translation = translation;
                    that.markSelfTranslation();

                    // `status` class is documented elsewhere
                    // eslint-disable-next-line mediawiki/class-doc
                    that.$message.find( '.tux-proofread-status' )
                        .removeClass( 'translated fuzzy proofread untranslated' )
                        .addClass( that.message.properties.status );
                }
            } );

        },

        /**
         * @private
         */
        render: function () {
            // List of all reviewers
            var reviewers = this.message.properties.reviewers || [];
            // The id of the current user, converted to string as the are in reviewers
            var userId = String( mw.config.get( 'wgUserId' ) );
            // List of all reviewers excluding the current user.
            var otherReviewers = reviewers.filter( function ( element ) {
                return element !== userId;
            } );
            /* Whether the current user if the last translator of this message.
             * Accepting own translations is prohibited. */
            var translatedBySelf = ( this.message.properties[ 'last-translator-text' ] === mw.user.getName() );
            var proofreadBySelf = reviewers.indexOf( userId ) > -1;

            var sourceLangDir = $.uls.data.getDir( this.options.sourcelangcode );

            // `status` class is documented elsewhere
            // eslint-disable-next-line mediawiki/class-doc
            var $proofreadAction = $( '<div>' )
                .attr( 'title', mw.msg( 'tux-proofread-action-tooltip' ) )
                .addClass(
                    'tux-proofread-action ' + this.message.properties.status + ' ' + ( proofreadBySelf ? 'accepted' : '' )
                );

            var $proofreadEdit = $( '<div>' )
                .addClass( 'tux-proofread-edit' )
                .append( $( '<span>' )
                    .addClass( 'tux-proofread-edit-label hide' )
                    .text( mw.msg( 'tux-proofread-edit-label' ) )
                )
                .on( 'mouseover', function () {
                    $( this ).find( '.tux-proofread-edit-label' ).removeClass( 'hide' );
                } )
                .on( 'mouseout', function () {
                    $( this ).find( '.tux-proofread-edit-label' ).addClass( 'hide' );
                } );

            var targetLangAttrib;
            if ( this.options.targetlangcode === mw.config.get( 'wgTranslateDocumentationLanguageCode' ) ) {
                targetLangAttrib = mw.config.get( 'wgContentLanguage' );
            } else {
                targetLangAttrib = this.options.targetlangcode;
            }

            var targetLangDir = $.uls.data.getDir( targetLangAttrib );

            // `status` class is documented elsewhere
            // eslint-disable-next-line mediawiki/class-doc
            this.$message.append(
                $( '<div>' )
                    .addClass( 'row tux-message-item-compact message' )
                    .append(
                        // `status` class is documented elsewhere
                        // eslint-disable-next-line mediawiki/class-doc
                        $( '<div>' )
                            .addClass( 'one column tux-proofread-status ' + this.message.properties.status ),
                        $( '<div>' )
                            .addClass( 'five columns tux-proofread-source' )
                            .attr( {
                                lang: this.options.sourcelangcode,
                                dir: sourceLangDir
                            } )
                            .text( this.message.definition ),
                        $( '<div>' )
                            .addClass( 'five columns tux-proofread-translation' )
                            .attr( {
                                lang: targetLangAttrib,
                                dir: targetLangDir
                            } )
                            .text( this.message.translation || '' ),
                        $( '<div>' )
                            .addClass( 'tux-proofread-action-block one column' )
                            .append(
                                $proofreadAction,
                                otherReviewers.length ?
                                    $( '<div>' )
                                        .addClass( 'tux-proofread-count' )
                                        .data( 'reviewCount', reviewers.length ) // To update when accepting
                                        .text( mw.language.convertNumber( reviewers.length ) ) :
                                    $( [] ),
                                $proofreadEdit
                            )
                    )
            ).addClass( this.message.properties.status );

            if ( !translatedBySelf && !proofreadBySelf ) {
                // This will get removed later if any of various other reasons prevent it
                this.message.proofreadable = true;
                this.message.proofreadAction = this.proofread.bind( this );
            }

            if ( translatedBySelf ) {
                this.markSelfTranslation();
            }

            /* Here we need to check that there are reviewers in the first place
             * before adding review markers */
            if ( reviewers.length && otherReviewers.length ) {
                this.$message.addClass( 'proofread-by-others' );
            }
        },

        /**
         * @private
         */
        disableProofread: function () {
            this.message.proofreadable = false;
            this.$message.find( '.tux-proofread-action' )
                .remove();
        },

        /**
         * Mark the message self translated.
         *
         * @private
         */
        markSelfTranslation: function () {
            // Own translations cannot be reviewed, so disable proofread
            this.disableProofread();
            if ( !this.$message.hasClass( 'own-translation' ) ) {
                this.$message.addClass( 'own-translation' )
                    .find( '.tux-proofread-action-block' )
                    .append( $( '<div>' )
                        .addClass( 'translated-by-self' )
                        .attr( 'title', mw.msg( 'tux-proofread-translated-by-self' ) )
                    );
            }
        },
        /**
         * Mark this message as proofread.
         *
         * @private
         */
        proofread: function () {
            var message = this.message,
                $message = this.$message,
                api = new mw.Api();

            var params = {
                action: 'translationreview',
                revision: this.message.properties.revision
            };

            if ( !mw.user.isAnon() ) {
                params.assert = 'user';
            }

            api.postWithToken( 'csrf', params ).done( function () {
                $message.find( '.tux-proofread-action' )
                    .removeClass( 'tux-notice' ) // in case, it failed previously
                    .addClass( 'accepted' );

                var $counter = $message.find( '.tux-proofread-count' );
                var reviews = $counter.data( 'reviewCount' );
                $counter.text( mw.language.convertNumber( reviews + 1 ) );

                // Update stats
                $( '.tux-action-bar .tux-statsbar' ).trigger(
                    'change',
                    [ 'proofread', message.properties.status ]
                );

                message.properties.status = 'proofread';

                if ( mw.track ) {
                    mw.track( 'ext.translate.event.proofread', message );
                }
            } ).fail( function ( errorCode ) {
                $message.find( '.tux-proofread-action' ).addClass( 'tux-notice' );
                if ( errorCode === 'assertuserfailed' ) {
                    // eslint-disable-next-line no-alert
                    alert( mw.msg( 'tux-session-expired' ) );
                }
            } );
        },

        /**
         * Attach event listeners
         *
         * @private
         */
        listen: function () {
            var that = this;

            this.$message.find( '.tux-proofread-action' ).on( 'click', function () {
                that.proofread();
                return false;
            } );

            this.$message.find( '.tux-proofread-edit' ).on( 'click', function () {
                // Make sure that the tooltip is hidden when going to the editor
                $( '.translate-tooltip' ).remove();
                that.$message.data( 'translateeditor' ).show();

                return false;
            } );
        }
    };

    /**
     * proofread PLUGIN DEFINITION
     *
     * @internal
     * @param {Object} options
     * @returns {jQuery}
     */
    $.fn.proofread = function ( options ) {
        return this.each( function () {
            var $this = $( this ),
                data = $this.data( 'proofread' );

            if ( !data ) {
                $this.data( 'proofread', new Proofread( this, options ) );
            }

        } );
    };

    $.fn.proofread.Constructor = Proofread;

}() );