%PDF- %PDF-
Direktori : /www/varak.net/wiki.varak.net/extensions/Translate/resources/js/ |
Current File : /www/varak.net/wiki.varak.net/extensions/Translate/resources/js/ext.translate.editor.helpers.js |
/*! * Translate editor additional helper functionality */ ( function ( $, mw ) { 'use strict'; var translateEditorHelpers = { showDocumentationEditor: function () { var $infoColumnBlock = this.$editor.find( '.infocolumn-block' ), $editColumn = this.$editor.find( '.editcolumn' ), $messageDescEditor = $infoColumnBlock.find( '.message-desc-editor' ), $messageDescViewer = $infoColumnBlock.find( '.message-desc-viewer' ); $infoColumnBlock .removeClass( 'five' ) .addClass( 'seven' ); $editColumn .removeClass( 'seven' ) .addClass( 'five' ); $messageDescViewer.addClass( 'hide' ); $messageDescEditor.removeClass( 'hide' ); $messageDescEditor.find( '.tux-textarea-documentation' ).focus(); // So that the link won't be followed return false; }, hideDocumentationEditor: function () { var $infoColumnBlock = this.$editor.find( '.infocolumn-block' ), $editColumn = this.$editor.find( '.editcolumn' ), $messageDescEditor = $infoColumnBlock.find( '.message-desc-editor' ), $messageDescViewer = $infoColumnBlock.find( '.message-desc-viewer' ); $infoColumnBlock .removeClass( 'seven' ) .addClass( 'five' ); $editColumn .removeClass( 'five' ) .addClass( 'seven' ); $messageDescEditor.addClass( 'hide' ); $messageDescViewer.removeClass( 'hide' ); }, /** * Save the documentation * * @return {jQuery.Promise} */ saveDocumentation: function () { var translateEditor = this, api = new mw.Api(), newDocumentation = translateEditor.$editor.find( '.tux-textarea-documentation' ).val(); return api.postWithToken( 'csrf', { action: 'edit', title: translateEditor.message.title .replace( /\/[a-z-]+$/, '/' + mw.config.get( 'wgTranslateDocumentationLanguageCode' ) ), text: newDocumentation } ).done( function ( response ) { var $messageDesc = translateEditor.$editor.find( '.infocolumn-block .message-desc' ); if ( response.edit.result === 'Success' ) { api.parse( newDocumentation ).done( function ( parsedDocumentation ) { $messageDesc.html( parsedDocumentation ); } ).fail( function ( errorCode, results ) { $messageDesc.html( newDocumentation ); mw.log( 'Error parsing documentation ' + errorCode + ' ' + results.error.info ); } ); // A collapsible element may have been added $( '.mw-identical-title' ).makeCollapsible(); translateEditor.hideDocumentationEditor(); } else { mw.notify( 'Error saving message documentation' ); mw.log( 'Error saving documentation', response ); } } ).fail( function ( errorCode, results ) { mw.notify( 'Error saving message documentation' ); mw.log( 'Error saving documentation', errorCode, results ); } ); }, /** * Shows the message documentation. * * @param {Object} documentation A documentation object as returned by API. */ showMessageDocumentation: function ( documentation ) { var $descEditLink, documentationDir, expand, $messageDescViewer, $messageDoc, readMore, langAttr, $readMore = null; if ( !mw.config.get( 'wgTranslateDocumentationLanguageCode' ) ) { return; } $messageDescViewer = this.$editor.find( '.message-desc-viewer' ); $descEditLink = $messageDescViewer.find( '.message-desc-edit' ); $messageDoc = $messageDescViewer.find( '.message-desc' ); // Display the documentation only if it's not empty and // documentation language is configured if ( documentation.error ) { // TODO: better error handling, especially since the presence of documentation // is heavily hinted at in the UI return; } else if ( documentation.value ) { documentationDir = $.uls.data.getDir( documentation.language ); // Show the documentation and set appropriate // lang and dir attributes. // The message documentation is assumed to be written // in the content language of the wiki. langAttr = { lang: documentation.language, dir: documentationDir }; // Possible classes: // * mw-content-ltr // * mw-content-rtl // (The direction classes are needed, because the documentation // is likely to be MediaWiki-formatted text.) $messageDoc .attr( langAttr ) .addClass( 'mw-content-' + documentationDir ) .html( documentation.html ); $messageDoc.find( 'a[href]' ).prop( 'target', '_blank' ); this.$editor.find( '.tux-textarea-documentation' ) .attr( langAttr ) .val( documentation.value ); $descEditLink.text( mw.msg( 'tux-editor-edit-desc' ) ); if ( documentation.html.length > 500 ) { expand = function () { $messageDoc.removeClass( 'compact' ); $readMore.text( mw.msg( 'tux-editor-message-desc-less' ) ); }; readMore = function () { if ( $messageDoc.hasClass( 'compact' ) ) { expand(); } else { $messageDoc.addClass( 'compact' ); $readMore.text( mw.msg( 'tux-editor-message-desc-more' ) ); } }; $readMore = $( '<span>' ) .addClass( 'read-more column' ) .text( mw.msg( 'tux-editor-message-desc-more' ) ) .click( readMore ); $messageDescViewer.find( '.message-desc-control' ) .prepend( $readMore ); $messageDoc.addClass( 'long compact' ).on( 'mouseenter mouseleave', expand ); } // Enable the collapsible elements, // used in {{Identical}} on translatewiki.net $( '.mw-identical-title' ).makeCollapsible(); } else { $descEditLink.text( mw.msg( 'tux-editor-add-desc' ) ); } $messageDescViewer.removeClass( 'hide' ); }, /** * Shows uneditable documentation. * * @param {Object} documentation A gettext object as returned by API. */ showUneditableDocumentation: function ( documentation ) { var dir; if ( documentation.error ) { return; } dir = $.uls.data.getDir( documentation.language ); this.$editor.find( '.uneditable-documentation' ) .attr( { lang: documentation.language, dir: dir } ) .addClass( 'mw-content-' + dir ) .html( documentation.html ) .removeClass( 'hide' ); }, /** * Shows the translations from other languages * * @param {Array} translations An inotherlanguages array as returned by the translation helpers API. */ showAssistantLanguages: function ( translations ) { var translateEditor = this; if ( translations.error ) { // Do not proceed if errored/unsupported return; } $.each( translations, function ( index ) { var $otherLanguage, langAttr, translation = translations[ index ]; langAttr = { lang: translation.language, dir: $.uls.data.getDir( translation.language ) }; $otherLanguage = $( '<div>' ) .addClass( 'row in-other-language' ) .append( $( '<div>' ) .addClass( 'nine columns suggestiontext' ) .attr( langAttr ) .text( translation.value ), $( '<div>' ) .addClass( 'three columns language text-right' ) .attr( langAttr ) .text( $.uls.data.getAutonym( translation.language ) ) ); translateEditor.suggestionAdder( $otherLanguage, translation.value ); translateEditor.$editor.find( '.in-other-languages-title' ) .removeClass( 'hide' ) .after( $otherLanguage ); } ); }, /** * Shows the translation suggestions from Translation Memory * * @param {Array} suggestions A ttmserver array as returned by API. */ showTranslationMemory: function ( suggestions ) { var $heading, $tmSuggestions, $messageList, translationLang, translationDir, translateEditor = this; if ( !suggestions.length ) { return; } // Container for the suggestions $tmSuggestions = $( '<div>' ).addClass( 'tm-suggestions' ); $heading = this.$editor.find( '.tm-suggestions-title' ); $heading.after( $tmSuggestions ); $messageList = $( '.tux-messagelist' ); translationLang = $messageList.data( 'targetlangcode' ); translationDir = $messageList.data( 'targetlangdir' ); $.each( suggestions, function ( index, translation ) { var $translation, alreadyOnTheList = false; if ( translation.local && translation.location === translateEditor.message.title ) { // Do not add self-suggestions return true; } // See if it is already listed, and increment use count $tmSuggestions.find( '.tm-suggestion' ).each( function () { var $uses, count, $suggestion = $( this ); if ( $suggestion.find( '.suggestiontext ' ).text() === translation.target ) { // Update the message and data value $uses = $suggestion.find( '.n-uses' ); count = $uses.data( 'n' ) + 1; $uses.data( 'n', count ); $uses.text( mw.msg( 'tux-editor-n-uses', count ) + ' 〉' ); // Halt processing alreadyOnTheList = true; return false; } } ); if ( alreadyOnTheList ) { // Continue to the next one return true; } $translation = $( '<div>' ) .addClass( 'row tm-suggestion' ) .append( $( '<div>' ) .addClass( 'nine columns suggestiontext' ) .attr( { lang: translationLang, dir: translationDir } ) .text( translation.target ), $( '<div>' ) .addClass( 'three columns quality text-right' ) .text( mw.msg( 'tux-editor-tm-match', mw.language.convertNumber( Math.floor( translation.quality * 100 ) ) ) ), $( '<div>' ) .addClass( 'row text-right' ) .append( $( '<a>' ) .addClass( 'n-uses' ) .data( 'n', 1 ) ) ); translateEditor.suggestionAdder( $translation, translation.target ); $tmSuggestions.append( $translation ); } ); // Show the heading only if we actually have suggestions if ( $tmSuggestions.length ) { $heading.removeClass( 'hide' ); } }, /** * Shows the translation from machine translation systems * * @param {Array} suggestions */ showMachineTranslations: function ( suggestions ) { var $mtSuggestions, $messageList, translationLang, translationDir, translateEditor = this; if ( !suggestions.length ) { return; } $mtSuggestions = this.$editor.find( '.tm-suggestions' ); if ( !$mtSuggestions.length ) { $mtSuggestions = $( '<div>' ).addClass( 'tm-suggestions' ); } this.$editor.find( '.tm-suggestions-title' ) .removeClass( 'hide' ) .after( $mtSuggestions ); $messageList = $( '.tux-messagelist' ); translationLang = $messageList.data( 'targetlangcode' ); translationDir = $messageList.data( 'targetlangdir' ); $.each( suggestions, function ( index, translation ) { var $translation; $translation = $( '<div>' ) .addClass( 'row tm-suggestion' ) .append( $( '<div>' ) .addClass( 'nine columns suggestiontext' ) .attr( { lang: translationLang, dir: translationDir } ) .text( translation.target ), $( '<div>' ) .addClass( 'three columns text-right service' ) .text( translation.service ) ); translateEditor.suggestionAdder( $translation, translation.target ); $mtSuggestions.append( $translation ); } ); }, /** * Makes the $source element clickable and clicking it will replace the * translation textarea with the given suggestion. * * @param {jQuery} $source * @param {string} suggestion Text to add */ suggestionAdder: function ( $source, suggestion ) { var inserter, $target = this.$editor.find( '.tux-textarea-translation' ); inserter = function () { var selection; if ( window.getSelection ) { selection = window.getSelection().toString(); } else if ( document.selection && document.selection.type !== 'Control' ) { selection = document.selection.createRange().text; } if ( !selection ) { $target.val( suggestion ).focus().trigger( 'input' ); } }; $source.on( 'click', inserter ); $source.addClass( 'shortcut-activated' ); }, /** * Shows the support options for the translator. * * @param {Object} support A support object as returned by API. */ showSupportOptions: function ( support ) { // Support URL if ( support.url ) { this.$editor.find( '.help a' ).attr( 'href', support.url ); this.$editor.find( '.help' ).removeClass( 'hide' ); } }, /** * Adds buttons for quickly inserting insertables. * * @param {Object} insertables A insertables object as returned by API. */ addInsertables: function ( insertables ) { var i, count = insertables.length, $sourceMessage = this.$editor.find( '.sourcemessage' ), $buttonArea = this.$editor.find( '.tux-editor-insert-buttons' ), $textarea = this.$editor.find( '.tux-textarea-translation' ); for ( i = 0; i < count; i++ ) { // The dir and lang attributes must be set here, // because the language of the insertables is the language // of the source message and not of the translation. // The direction may appear confusing, for example, // in tvar strings, which would appear with the dollar sign // on the wrong end. $( '<button>' ) .prop( { lang: $sourceMessage.prop( 'lang' ), dir: $sourceMessage.prop( 'dir' ) } ) .addClass( 'insertable shortcut-activated' ) .text( insertables[ i ].display ) .data( 'iid', i ) .appendTo( $buttonArea ); } $buttonArea.on( 'click', '.insertable', function () { var data = insertables[ $( this ).data( 'iid' ) ]; $textarea.textSelection( 'encapsulateSelection', { pre: data.pre, post: data.post } ); $textarea.focus().trigger( 'input' ); } ); this.resizeInsertables( $textarea ); }, /** * Loads and shows the translation helpers. */ showTranslationHelpers: function () { // API call to get translation suggestions from other languages // callback should render suggestions to the editor's info column var translateEditor = this, api = new mw.Api(); api.get( { action: 'translationaids', title: this.message.title } ).done( function ( result ) { translateEditor.$editor.find( '.infocolumn .loading' ).remove(); if ( !result.helpers ) { mw.log( 'API did not return any translation helpers.' ); return false; } translateEditor.showMessageDocumentation( result.helpers.documentation ); translateEditor.showUneditableDocumentation( result.helpers.gettext ); translateEditor.showAssistantLanguages( result.helpers.inotherlanguages ); translateEditor.showTranslationMemory( result.helpers.ttmserver ); translateEditor.showMachineTranslations( result.helpers.mt ); translateEditor.showSupportOptions( result.helpers.support ); translateEditor.addDefinitionDiff( result.helpers.definitiondiff ); translateEditor.addInsertables( result.helpers.insertables ); // Load the possible warnings as soon as possible, do not wait // for the user to make changes. Otherwise users might try confirming // translations which fail checks. Confirmation seems to work but // the message will continue to appear outdated. if ( translateEditor.message.properties && translateEditor.message.properties.status === 'fuzzy' ) { translateEditor.validateTranslation(); } mw.translateHooks.run( 'showTranslationHelpers', result.helpers, translateEditor.$editor ); } ).fail( function ( errorCode, results ) { mw.log( 'Error loading translation aids', errorCode, results ); } ); } }; mw.translate = mw.translate || {}; mw.translate = $.extend( mw.translate, { /** * Get the documentation edit URL for a title * * @param {string} title Message title with namespace * @return {string} URL for editing the documentation */ getDocumentationEditURL: function ( title ) { return mw.util.getUrl( title + '/' + mw.config.get( 'wgTranslateDocumentationLanguageCode' ), { action: 'edit' } ); } } ); // Extend the translate editor mw.translate.editor = mw.translate.editor || {}; $.extend( mw.translate.editor, translateEditorHelpers ); }( jQuery, mediaWiki ) );