diff options
Diffstat (limited to 'www/wiki/extensions/Translate/resources/js/ext.translate.quickedit.js')
-rw-r--r-- | www/wiki/extensions/Translate/resources/js/ext.translate.quickedit.js | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/www/wiki/extensions/Translate/resources/js/ext.translate.quickedit.js b/www/wiki/extensions/Translate/resources/js/ext.translate.quickedit.js new file mode 100644 index 00000000..26d73906 --- /dev/null +++ b/www/wiki/extensions/Translate/resources/js/ext.translate.quickedit.js @@ -0,0 +1,402 @@ +/*! + * JavaScript that implements the Ajax translation interface, which was at the + * time of writing this probably the biggest usability problem in the extension. + * Most importantly, it speeds up translating and keeps the list of translatable + * messages open. It also allows multiple translation dialogs, for doing quick + * updates to other messages or documentation, or translating multiple languages + * simultaneously together with the "In other languages" display included in + * translation helpers and implemented by utils/TranslationhHelpers.php. + * The form itself is implemented by utils/TranslationEditPage.php, which is + * called from Special:Translate/editpage?page=Namespace:pagename. + * + * TODO list: + * * Instead of hc'd onscript, give them a class and use necessary triggers + * + * @author Niklas Laxström + * @license GPL-2.0+ + */ + +( function ( $, mw, autosize ) { + 'use strict'; + var dialogwidth = false, + preloads = {}; + + mw.translate = mw.translate || {}; + function MessageCheckUpdater( callback ) { + this.act = function () { + callback(); + delete this.timeoutID; + }; + + this.setup = function () { + var self = this; + + this.cancel(); + this.timeoutID = window.setTimeout( self.act, 1000 ); + }; + + this.cancel = function () { + if ( typeof this.timeoutID === 'number' ) { + window.clearTimeout( this.timeoutID ); + delete this.timeoutID; + } + }; + } + + /** + * This is JS port same method of TranslateUtils.php + */ + function convertWhiteSpaceToHTML( text ) { + return mw.html.escape( text ) + .replace( /^ /gm, ' ' ) + .replace( / $/gm, ' ' ) + .replace( / {2}/g, '  ' ) + .replace( /\n/g, '<br />' ); + } + + function addAccessKeys( dialog ) { + var buttons = { + a: '.mw-translate-save', + s: '.mw-translate-next', + d: '.mw-translate-skip', + h: '.mw-translate-history' + }; + + $.each( buttons, function ( key, selector ) { + $( selector ) + .val( function ( i, b ) { + return b.replace( / \(.\)$/, '' ); + } ) + .removeAttr( 'accesskey' ) + .attr( 'title', '' ); + + dialog.find( selector ) + .val( function ( i, b ) { + return b + ' (_)'.replace( '_', key ); + } ) + .attr( 'accesskey', key ) + .attr( 'title', '[' + mw.util.tooltipAccessKeyPrefix + key + ']' ); + } ); + } + + function registerFeatures( callbacks, form, page, group ) { + var $identical, textarea, checker; + + // Enable the collapsible element + $identical = $( '.mw-identical-title' ); + if ( $.isFunction( $identical.makeCollapsible ) ) { + $identical.makeCollapsible(); + } + + if ( mw.config.get( 'trlKeys' ) || $( '.tqe-inlineeditable' ).length ) { + if ( callbacks.next === undefined ) { + form.find( '.mw-translate-next, .mw-translate-skip' ).prop( 'disabled', true ); + } else { + form.find( '.mw-translate-next' ).click( function () { + if ( callbacks.next ) { + callbacks.next(); + } + } ); + form.find( '.mw-translate-skip' ).click( function () { + if ( callbacks.close ) { + callbacks.close(); + } + if ( callbacks.next ) { + callbacks.next(); + } + } ); + } + } else { + form.find( '.mw-translate-next, .mw-translate-skip' ) + .prop( 'disabled', true ) + .css( 'display', 'none' ); + } + form.find( '.mw-translate-close' ).click( function () { + if ( callbacks.close ) { + callbacks.close(); + } + } ); + + form.find( '.mw-translate-history' ).click( function () { + window.open( mw.util.getUrl( form.find( 'input[name=title]' ).val(), { action: 'history' } ) ); + return false; + } ); + + form.find( '.mw-translate-support, .mw-translate-askpermission' ).click( function () { + // Can use .data() only with 1.4.3 or newer + window.open( $( this ).attr( 'data-load-url' ) ); + return false; + } ); + + form.find( 'input, textarea' ).focus( function () { + addAccessKeys( form ); + } ); + + form.find( 'input#summary' ).focus( function () { + $( this ).css( 'width', '85%' ); + } ); + + textarea = form.find( '.mw-translate-edit-area' ); + textarea.css( 'display', 'block' ); + autosize( textarea ); + textarea[ 0 ].focus(); + + if ( form.find( '.mw-translate-messagechecks' ) ) { + checker = new MessageCheckUpdater( function () { + var url = mw.util.getUrl( 'Special:Translate/editpage', { + suggestions: 'checks', + page: page, + loadgroup: group + } ); + $.post( url, { translation: textarea.val() }, function ( mydata ) { + form.find( '.mw-translate-messagechecks' ).replaceWith( mydata ); + } ); + } ); + + textarea.keyup( function () { + checker.setup(); + } ); + } + + } + + mw.translate = $.extend( mw.translate, { + init: function () { + var $inlines, $first, title, group, prev; + + dialogwidth = $( window ).width() * 0.8; + $inlines = $( '.tqe-inlineeditable' ); + $inlines.dblclick( mw.translate.inlineEditor ); + + $first = $inlines.first(); + if ( $first.length ) { + title = $first.data( 'title' ); + group = $first.data( 'group' ); + mw.translate.loadEditor( null, title, group, $.noop ); + } + + prev = null; + $inlines.each( function () { + if ( prev ) { + prev.next = this; + } + prev = this; + } ); + }, + + openDialog: function ( page, group ) { + var id, dialogElement, dialog, callbacks; + + id = 'jsedit' + page.replace( /[^a-zA-Z0-9_]/g, '_' ); + dialogElement = $( '#' + id ); + + if ( dialogElement.size() > 0 ) { + dialogElement.dialog( 'option', 'position', 'top' ); + dialogElement.dialog( 'open' ); + return false; + } + + dialog = $( '<div>' ).attr( 'id', id ).appendTo( $( 'body' ) ); + + callbacks = {}; + callbacks.close = function () { + dialog.dialog( 'close' ); + }; + callbacks.next = function () { + mw.translate.openNext( page, group ); + }; + callbacks.success = function ( text ) { + var $td = $( '.tqe-inlineeditable' ).filter( function () { + return $( this ).data( 'title' ) === page.replace( '_', ' ' ); + } ); + $td + .html( convertWhiteSpaceToHTML( text ) ) + // T41233: hacky, but better than nothing. + // T130390: must be attr for IE/Edge. + .attr( 'dir', 'auto' ) + .removeClass( 'untranslated' ) + .addClass( 'justtranslated' ); + }; + mw.translate.openEditor( dialog, page, group, callbacks ); + + dialog.dialog( { + bgiframe: true, + width: dialogwidth, + title: page, + position: 'top', + resize: function () { + $( '#' + id + ' textarea' ).width( '100%' ); + }, + resizeStop: function () { + dialogwidth = $( '#' + id ).width(); + } + } ); + + return false; + }, + + loadEditor: function ( $target, page, group, callback ) { + var id, preload, url; + + // Try if it has been cached + id = 'preload-' + page.replace( /[^a-zA-Z0-9_]/g, '_' ); + preload = preloads[ id ]; + + if ( preload !== undefined ) { + if ( $target ) { + $target.html( preloads[ id ] ); + delete preloads[ id ]; + } + callback(); + return; + } + + // Load the editor into provided target or cache it locally + url = mw.util.getUrl( 'Special:Translate/editpage', { + suggestions: 'sync', + page: page, + loadgroup: group + } ); + if ( $target ) { + $target.load( url, callback ); + delete preloads[ id ]; + } else { + $.get( url, function ( data ) { + preloads[ id ] = data; + } ); + } + + }, + + openEditor: function ( element, page, group, callbacks ) { + var $target = $( element ), + spinner = $( '<div>' ).attr( 'class', 'mw-ajax-loader' ); + + $target.html( $( '<div>' ).attr( 'class', 'mw-ajax-dialog' ).html( spinner ) ); + + mw.translate.loadEditor( $target, page, group, function () { + var form; + + if ( callbacks.load ) { + callbacks.load( $target ); + } + + form = $target.find( 'form' ); + registerFeatures( callbacks, form, page, group ); + form.on( 'submit', function () { + mw.translateHooks.run( 'beforeSubmit', form ); + $( this ).ajaxSubmit( { + dataType: 'json', + success: function ( json ) { + mw.translateHooks.run( 'afterSubmit', form ); + if ( json.error ) { + if ( json.error.code === 'emptypage' ) { + window.alert( mw.msg( 'api-error-emptypage' ) ); + } else { + window.alert( json.error.info + ' (' + json.error.code + ')' ); + } + } else if ( json.edit.result === 'Failure' ) { + window.alert( mw.msg( 'translate-js-save-failed' ) ); + } else if ( json.edit.result === 'Success' ) { + if ( callbacks.close ) { + callbacks.close(); + } + if ( callbacks.success ) { + callbacks.success( form.find( '.mw-translate-edit-area' ).val() ); + } + } else { + window.alert( mw.msg( 'translate-js-save-failed' ) ); + } + } + } ); + return false; + } ); + } ); + }, + + openNext: function ( title, group ) { + var key, value, + messages = mw.config.get( 'trlKeys' ), + found = false; + + for ( key in messages ) { + if ( !messages.hasOwnProperty( key ) ) { + continue; + } + + value = messages[ key ]; + if ( found ) { + return mw.translate.openDialog( value, group ); + } else if ( value === title ) { + found = true; + } + } + window.alert( mw.msg( 'translate-js-nonext' ) ); + }, + + inlineEditor: function () { + var $this, current, page, group, next, callbacks, ntitle, ngroup, sel; + $this = $( this ); + + if ( $this.hasClass( 'tqe-editor-loaded' ) ) { + // Editor is open, do not replace it + return; + } + + current = $this.html(); + $this.addClass( 'tqe-editor-loaded' ); + + page = $this.data( 'title' ); + group = $this.data( 'group' ); + next = $( this.next ); + callbacks = {}; + + callbacks.success = function ( text ) { + // Update the cell value with the new translation + $this + .html( convertWhiteSpaceToHTML( text ) ) + // T41233: hacky, but better than nothing. + // T130390: must be attr for IE/Edge. + .attr( 'dir', 'auto' ) + .removeClass( 'untranslated' ) + .addClass( 'justtranslated' ); + }; + callbacks.close = function () { + $this.html( current ); + $this.removeClass( 'tqe-editor-loaded' ); + }; + callbacks.load = function ( editor ) { + var $header = $( '<div class="tqe-fakeheader"></div>' ); + $header.text( page ); + $header.append( '<input type=button class="mw-translate-close" value="X" />' ); + + $( editor ).find( 'form' ).prepend( $header ); + }; + if ( next.length ) { + callbacks.next = function () { + next.dblclick(); + }; + // Preload the next item + ntitle = next.data( 'title' ); + ngroup = next.data( 'group' ); + + mw.translate.loadEditor( null, ntitle, ngroup, $.noop ); + } + mw.translate.openEditor( $this, page, group, callbacks ); + + // Remove any text selection caused by double clicking + sel = window.getSelection ? window.getSelection() : document.selection; + + if ( sel ) { + if ( sel.removeAllRanges ) { + sel.removeAllRanges(); + } + if ( sel.empty ) { + sel.empty(); + } + } + } + } ); + + $( document ).ready( mw.translate.init ); +} )( jQuery, mediaWiki, autosize ); |