( function () { 'use strict'; var itemsClass = { proofread: '.tux-message-proofread', page: '.tux-message-pagemode', translate: '.tux-message' }; mw.translate = mw.translate || {}; mw.translate = $.extend( mw.translate, { getMessages: function ( messageGroup, language, offset, limit, filter ) { var api = new mw.Api(); return api.get( { action: 'query', list: 'messagecollection', mcgroup: messageGroup, mclanguage: language, mcoffset: offset, mclimit: limit, mcfilter: filter, mcprop: 'definition|translation|tags|properties', rawcontinue: 1, errorformat: 'html' } ); } } ); function MessageTable( container, options, settings ) { this.$container = $( container ); this.options = options; this.options = $.extend( {}, $.fn.messagetable.defaults, options ); this.settings = settings; // mode can be proofread, page or translate this.mode = this.options.mode; this.firstProofreadTipShown = false; this.initialized = false; this.$header = this.$container.siblings( '.tux-messagetable-header' ); // Container is between these in the dom. this.$loader = this.$container.siblings( '.tux-messagetable-loader' ); this.$loaderIcon = this.$loader.find( '.tux-loading-indicator' ); this.$loaderInfo = this.$loader.find( '.tux-messagetable-loader-info' ); this.$actionBar = this.$container.siblings( '.tux-action-bar' ); this.$statsBar = this.$actionBar.find( '.tux-message-list-statsbar' ); this.$proofreadOwnTranslations = this.$actionBar.find( '.tux-proofread-own-translations-button' ); this.messages = []; this.loading = false; this.init(); this.listen(); } MessageTable.prototype = { init: function () { this.$actionBar.removeClass( 'hide' ); }, listen: function () { var messageTable = this, $filterInput = this.$container.parent().find( '.tux-message-filter-box' ); // Vector has transitions of 250ms which affect layout. Let those finish. $( window ).on( 'scroll', $.debounce( 250, function () { messageTable.scroll(); if ( isLoaderVisible( messageTable.$loader ) ) { messageTable.load(); } } ) ).on( 'resize', $.throttle( 250, function () { messageTable.resize(); messageTable.scroll(); } ) ); if ( mw.translate.isPlaceholderSupported( $filterInput ) ) { $filterInput.prop( 'placeholder', mw.msg( 'tux-message-filter-placeholder' ) ); } $filterInput.on( 'textchange', $.debounce( 250, function () { messageTable.search( $filterInput.val() ); } ) ); this.$actionBar.find( 'button.proofread-mode-button' ).on( 'click', function () { messageTable.switchMode( 'proofread' ); } ); this.$actionBar.find( 'button.translate-mode-button' ).on( 'click', function () { messageTable.switchMode( 'translate' ); } ); this.$actionBar.find( 'button.page-mode-button' ).on( 'click', function () { messageTable.switchMode( 'page' ); } ); this.$proofreadOwnTranslations.click( function () { var $this = $( this ), hideMessage = mw.msg( 'tux-editor-proofreading-hide-own-translations' ), showMessage = mw.msg( 'tux-editor-proofreading-show-own-translations' ); if ( $this.hasClass( 'down' ) ) { messageTable.setHideOwnInProofreading( false ); $this.removeClass( 'down' ).text( hideMessage ); } else { messageTable.setHideOwnInProofreading( true ); $this.addClass( 'down' ).text( showMessage ); } } ); }, /** * Clear the message table */ clear: function () { this.$container.empty(); $( '.translate-tooltip' ).remove(); this.messages = []; // Any ongoing loading process will notice this and will reject results. this.loading = false; }, /** * Adds a new message using current mode. * * @param {Object} message */ add: function ( message ) { // Prepare the message for display mw.hook( 'mw.translate.messagetable.formatMessageBeforeTable' ).fire( message ); if ( this.mode === 'translate' ) { this.addTranslate( message ); } else if ( this.mode === 'proofread' ) { this.addProofread( message ); } else if ( this.mode === 'page' ) { this.addPageModeMessage( message ); } }, /** * Add a message to the message table for translation. * * @param {Object} message */ addTranslate: function ( message ) { var $message, targetLangDir, targetLangAttrib, targetLangCode = this.$container.data( 'targetlangcode' ), sourceLangCode = this.$container.data( 'sourcelangcode' ), sourceLangDir = $.uls.data.getDir( sourceLangCode ), status = message.properties.status, statusClass = 'tux-status-' + status, $messageWrapper = $( '
' ).addClass( 'row tux-message' ), statusMsg = ''; message.proofreadable = false; if ( message.tags.length && message.tags.indexOf( 'optional' ) >= 0 && status === 'untranslated' ) { status = 'optional'; statusClass = 'tux-status-optional'; } // Fuzzy translations need warning class if ( status === 'fuzzy' ) { statusClass = statusClass + ' tux-warning'; } // Label the status if it is not untranslated if ( status !== 'untranslated' ) { // Give grep a chance to find the usages: // tux-status-optional, tux-status-fuzzy, tux-status-proofread, // tux-status-translated, tux-status-saving, tux-status-unsaved statusMsg = 'tux-status-' + status; } if ( targetLangCode === mw.config.get( 'wgTranslateDocumentationLanguageCode' ) ) { targetLangAttrib = mw.config.get( 'wgContentLanguage' ); targetLangDir = $.uls.data.getDir( targetLangAttrib ); } else { targetLangAttrib = targetLangCode; targetLangDir = this.$container.data( 'targetlangdir' ); } $message = $( '
' ) .addClass( 'row message tux-message-item ' + status ) .append( $( '
' ) .addClass( 'eight columns tux-list-message' ) .append( $( '' ) .addClass( 'tux-list-source' ) .attr( { lang: sourceLangCode, dir: sourceLangDir } ) .text( message.definition ), // Bidirectional isolation. // This should be removed some day when proper // unicode-bidi: isolate // is supported everywhere $( '' ) .html( $( 'body' ).hasClass( 'rtl' ) ? '‏' : '‎' ), $( '' ) .addClass( 'tux-list-translation' ) .attr( { lang: targetLangAttrib, dir: targetLangDir } ) .text( message.translation || '' ) ), $( '
' ) .addClass( 'two columns tux-list-status text-center' ) .append( $( '' ) .addClass( statusClass ) .text( statusMsg ? mw.msg( statusMsg ) : '' ) ), $( '
' ) .addClass( 'two column tux-list-edit text-right' ) .append( $( '' ) .attr( { title: mw.msg( 'translate-edit-title', message.key ), href: mw.util.getUrl( message.title, { action: 'edit' } ) } ) .text( mw.msg( 'tux-edit' ) ) ) ); $messageWrapper.append( $message ); this.$container.append( $messageWrapper ); // Attach translate editor to the message $messageWrapper.translateeditor( { message: message } ); }, /** * Add a message to the message table for proofreading. * * @param {Object} message */ addProofread: function ( message ) { var $message, $icon; $message = $( '
' ) .addClass( 'row tux-message tux-message-proofread' ); this.$container.append( $message ); $message.proofread( { message: message, sourcelangcode: this.$container.data( 'sourcelangcode' ), targetlangcode: this.$container.data( 'targetlangcode' ) } ); $icon = $message.find( '.tux-proofread-action' ); if ( $icon.length === 0 ) { return; } // Add autotooltip to first available proofread action icon if ( this.firstProofreadTipShown ) { return; } this.firstProofreadTipShown = true; $icon.addClass( 'autotooltip' ); mw.loader.using( 'oojs-ui-core' ).done( function () { var tooltip = new OO.ui.PopupWidget( { padded: true, align: 'center', width: 250, classes: [ 'translate-tooltip' ], $content: $( '

' ).text( $icon.prop( 'title' ) ) } ); setTimeout( function () { var offset, $icon = $( '.autotooltip:visible' ); if ( !$icon.length ) { return; } offset = $icon.offset(); tooltip.$element.appendTo( 'body' ); tooltip.toggle( true ).toggleClipping( false ).togglePositioning( false ); tooltip.$element.css( { top: offset.top + $icon.outerHeight() + 5, left: offset.left + $icon.outerWidth() - tooltip.$element.width() / 2 - 15 } ); setTimeout( function () { tooltip.$element.remove(); }, 4000 ); }, 1000 ); } ); }, /** * Add a message to the message table for wiki page mode. * * @param {Object} message */ addPageModeMessage: function ( message ) { var $message; $message = $( '

' ) .addClass( 'row tux-message tux-message-pagemode' ); this.$container.append( $message ); $message.pagemode( { message: message, sourcelangcode: this.$container.data( 'sourcelangcode' ), targetlangcode: this.$container.data( 'targetlangcode' ) } ); }, /** * Search the message filter * * @param {string} query */ search: function ( query ) { var $note, $button, $result, resultCount = 0, matcher = new RegExp( '(^|\\s|\\b)' + escapeRegex( query ), 'gi' ); this.$container.find( itemsClass[ this.mode ] ).each( function () { var $message = $( this ), message = ( $message.data( 'translateeditor' ) || $message.data( 'pagemode' ) || $message.data( 'proofread' ) ).message; if ( matcher.test( message.definition ) || matcher.test( message.translation ) ) { $message.removeClass( 'hide' ); resultCount++; } else { $message.addClass( 'hide' ); } } ); $result = this.$container.find( '.tux-message-filter-result' ); if ( !$result.length ) { $note = $( '
' ) .addClass( 'advanced-search' ); $button = $( '