MediaWiki:Gadget-translation-editor.js
Wygląd
Uwaga: aby zobaczyć zmiany po opublikowaniu, może zajść potrzeba wyczyszczenia pamięci podręcznej przeglądarki.
- Firefox / Safari: Przytrzymaj Shift podczas klikania Odśwież bieżącą stronę, lub naciśnij klawisze Ctrl+F5, lub Ctrl+R (⌘-R na komputerze Mac)
- Google Chrome: Naciśnij Ctrl-Shift-R (⌘-Shift-R na komputerze Mac)
- Edge: Przytrzymaj Ctrl, jednocześnie klikając Odśwież, lub naciśnij klawisze Ctrl+F5.
- Opera: Naciśnij klawisze Ctrl+F5.
var gadget = {};
var PREFERRED_LANG_OPTION_KEY = 'userjs-translation-editor-preferred-lang';
var PREFERRED_LANG_STORAGE_KEY = 'ext.gadget.translation-editor.preferred-lang';
var POST_EDIT_STORAGE_KEY = 'ext.gadget.translation-editor.post-edit';
gadget.preferredLang = !mw.user.isAnon()
? mw.user.options.get( PREFERRED_LANG_OPTION_KEY )
: mw.storage.get( PREFERRED_LANG_STORAGE_KEY );
var EDITBOX_TOP_OFFSET = 100;
var EDITBOX_SCROLL_DURATION = 500;
var PREVIEW_SCROLL_DURATION = 500;
var SUMMARY_PREVIEW_DELAY = 250;
mw.libs.String = require( 'mediawiki.String' );
mw.libs.specialCharacters = require( 'mediawiki.language.specialCharacters' );
mw.libs.langData = require( 'ext.gadget.langdata' );
mw.messages.set( {
'transl-parse-error': 'Nie udało się odczytać kodu strony. Przejdź na tryb edycji wikikodu i sprawdź, czy nie ma błędów składniowych.',
'transl-validate-error': 'Lista tłumaczeń zawiera niewspierane języki albo błędy składniowe.',
'transl-rename-success': 'Zmieniono nazwę języka „$1” na przyjęte w Wikisłowniku określenie „$2”.',
'transl-rename-conflict': 'W Wikisłowniku nazwy „$1” oraz „$2” są tożsame; wykryto obie na liście tłumaczeń.',
'transl-sorted-notice': 'Naprawiono kolejność tłumaczeń.',
'transl-load-error': 'Istnieje nowsza wersja strony. Przeładuj ją i spróbuj ponownie.',
'transl-invalid-code': 'Nie rozpoznano kodu języka: „$1”.',
'transl-select-lang': 'Wybierz język:',
'transl-lang-label': 'Tłumaczysz na<br><strong>$1</strong> <small>(<strong>nieznany kod</strong>)</small>',
'transl-lang-label-iso': 'Tłumaczysz na<br><strong>$1</strong> <small>(<a href="/wiki/Wikis%C5%82ownik:Kody_j%C4%99zyk%C3%B3w" target="_blank">$2</a>)</small>',
'transl-load-button': 'Załaduj',
'transl-save-button': 'Zapamiętaj',
'transl-forget-button': 'Zapomnij',
'transl-save-save': 'Zapisz',
'transl-save-cancel': 'Anuluj',
'transl-prompt-title': 'Ustawienia domyślnego języka',
'transl-save-prompt': 'Język o nazwie „$1” zostanie załadowany domyślnie podczas następnego uruchomienia edytora tłumaczeń.',
'transl-save-current': 'Twoje obecne ustawienie to „$1”.',
'transl-forget-prompt': 'Język o nazwie „$1” przestanie być ładowany domyślnie.',
'transl-save-success': 'Zapisano preferencje.',
'transl-rename-title': 'Zmiana nazwy języka',
'transl-rename-desc': 'Wskazana nazwa języka zastąpi „$1”, zachowując obecne tłumaczenia.',
'transl-rename-error': '<strong>Błąd:</strong> niewspierany język albo już obecny na liście.',
'transl-rename-apply': 'Zmień',
'transl-rename-cancel': 'Anuluj',
'transl-summary-preview': '($1)',
'transl-summary-placeholder': 'opcjonalne uzasadnienie wykonanych zmian, np. wskazanie użytych źródeł',
'transl-summary-desc': 'Opis zmian:',
'transl-summary-edit': '(edytuj)',
'transl-compare-title': 'Porównanie wersji',
'transl-compare-old': 'Aktualna wersja',
'transl-compare-new': 'Wersja po zapisaniu zmian',
'transl-clear-title': 'Anulowanie zmian',
'transl-clear-prompt': 'Czy na pewno chcesz wycofać wprowadzone zmiany i rozpocząć edycję od nowa?',
'transl-clear-confirm': 'Potwierdź',
'transl-clear-cancel': 'Anuluj',
'transl-apply-button': 'Pokaż podgląd',
'transl-compare-button': 'Wykaz zmian',
'transl-submit-button': 'Zapisz',
'transl-clear-button': 'Wyczyść',
'transl-draft-change': '(edytuj)',
'transl-draft-remove': '(usuń)',
'transl-draft-restore': '(przywróć)',
'transl-draft-rename': '(język)',
'transl-draft-abort': '(wstrzymaj)',
'transl-keyboard': '(klawiatura ekranowa)',
'transl-keyboard-close': 'Zamknij',
'transl-rcf': '(<a href="//pl.wiktionary.org/wiki/Specjalna:Pusta_strona/ostatnie_t%C5%82umaczenia" target="_blank">ostatnie zmiany</a>)',
'transl-report-error': '(<a href="//pl.wiktionary.org/w/index.php?title=Wikidyskusja:Narz%C4%99dzia/Edytor_t%C5%82umacze%C5%84&action=edit§ion=new&editintro=Wikidyskusja:Narz%C4%99dzia/Edytor_t%C5%82umacze%C5%84/Nag%C5%82%C3%B3wek&nosummary=" target="_blank">zgłoś problem</a>)',
'transl-help': '(<a href="//pl.wiktionary.org/wiki/Wikis%C5%82ownik:Narz%C4%99dzia/Edytor_t%C5%82umacze%C5%84/Przewodnik" target="_blank">pomoc</a>)',
'transl-lang-suggest-code': 'Język z kodem <strong>$1</strong>',
'transl-forbidden-lang': 'Narzędzie nie wspiera tłumaczeń na język: „$1”.',
'transl-confirm-close': 'Masz niezapisane tłumaczenia. Czy na pewno chcesz opuścić stronę?',
'transl-alert-pending': 'Ostatnie zmiany w tłumaczeniach na „$1” nie zostaną zapisane. Wróć i przyciśnij „$2”, aby je uwzględnić.',
'transl-preview-parsing': 'poczekaj, trwa parsowanie wikikodu',
'transl-summary-renamed': 'przemianowano $1',
'transl-summary-rename-mapping': '$1 na $2',
'transl-summary-added': 'dodano $1',
'transl-summary-modified': 'zmodyfikowano $1',
'transl-summary-deleted': 'usunięto $1',
'transl-summary-sorted': 'sortowanie',
'transl-load-tooltip': 'Rozpocznij edycję wybranego języka',
'transl-save-tooltip': 'Automatycznie rozpoczynaj edycję wybranego języka przy następnym uruchomieniu',
'transl-forget-tooltip': 'Nie rozpoczynaj edycji zapamiętanego języka przy następnym uruchomieniu',
'transl-keyboard-tooltip': 'Otwórz klawiaturę ekranową',
'transl-rcf-tooltip': 'Przeglądaj ostatnio zmienione tłumaczenia',
'transl-report-tooltip': 'Przejdź do strony zgłaszania problemów (otwiera się w nowej zakładce)',
'transl-help-tooltip': 'Przejdź do strony z pomocą (otwiera się w nowej zakładce)',
'transl-apply-tooltip': 'Wygeneruj podgląd wprowadzonych tłumaczeń na poniższej liście',
'transl-compare-tooltip': 'Porównaj dotychczasową wersję tłumaczeń z wprowadzonymi właśnie zmianami',
'transl-submit-tooltip': 'Zapisz zmiany i przeładuj stronę [Ctrl+Enter]',
'transl-clear-tooltip': 'Przywróć formularz do stanu pierwotnego',
'transl-summary-tooltip': 'Odsłoń pole do wprowadzania opisu zmian',
'transl-draft-change-tooltip': 'Edytuj tłumaczenia na ten język',
'transl-draft-remove-tooltip': 'Oznacz język do usunięcia z listy tłumaczeń',
'transl-draft-restore-tooltip': 'Nie usuwaj tego języka z listy tłumaczeń',
'transl-draft-rename-tooltip': 'Zmień nazwę języka, zachowując tłumaczenia',
'transl-draft-abort-tooltip': 'Anuluj zapytanie i pokaż surowy wikitekst'
} );
var config = mw.config.get( [
'wgPageName',
'wgRevisionId',
'wgArticleId',
'wgCommentCodePointLimit',
'wgUserLanguage'
] );
var api = new mw.Api( { parameters: {
formatversion: 2,
errorformat: 'html',
errorlang: config.wgUserLanguage,
errorsuselocal: true
} } );
var forbiddenLanguageNames = [ 'polski', 'użycie międzynarodowe' ];
var forbiddenLanguageCodes = null;
var forbiddenTranslations = [ 'polski język migowy' ];
var softStressMarkLanguages = [ 'białoruski', 'białoruski (taraszkiewica)', 'bułgarski', 'karpatorusiński', 'rosyjski', 'ukraiński' ];
function init( $transl, $defn, $button ) {
var $translList = $transl.parent().next( 'ul' );
gadget.activeLang = '';
gadget.drafts = {};
gadget.draftMetadata = {};
forbiddenLanguageCodes = forbiddenLanguageNames.filter( function ( lang ) {
return lang in mw.libs.langData.lang2code;
} ).map( function ( lang ) {
return mw.libs.langData.lang2code[ lang ];
} );
return $.when(
api.get( {
prop: 'revisions',
rvprop: [ 'timestamp', 'content', 'ids' ],
rvslots: 'main',
titles: config.wgPageName,
curtimestamp: true
} ),
api.getMessages( [ 'wikimedia-copyrightwarning' ] ).then( function ( messages ) {
return api.parse( messages[ 'wikimedia-copyrightwarning' ], {
prop: 'text',
wrapoutputclass: '',
disablelimitreport: true
} ).done( function ( text ) {
var $p = $( text ).first( 'p' );
mw.messages.set( 'transl-submit-legal', $p.html().trim() );
} );
} ),
!!Number( mw.user.options.get( 'gadget-false-blue-links' ) )
? mw.loader.using( 'ext.gadget.false-blue-links' ).done( function ( require ) {
mw.libs.falseBlueLinks = require( 'ext.gadget.false-blue-links' );
} )
: null,
!!Number( mw.user.options.get( 'gadget-term-preview' ) )
? mw.loader.using( 'ext.gadget.term-preview' ).done( function ( require ) {
mw.libs.termPreview = require( 'ext.gadget.term-preview' );
} )
: null
).fail( function ( code, data ) {
mw.notify( api.getErrorMessage( data ), { type: 'error' } );
} ).then( function ( res ) {
var revision = res[ 0 ].query.pages[ 0 ].revisions[ 0 ],
deferred = $.Deferred();
if ( revision.revid !== config.wgRevisionId ) {
deferred.reject( mw.msg( 'transl-load-error' ) );
} else {
gadget.content = revision.slots.main.content;
gadget.starttimestamp = res.curtimestamp;
gadget.basetimestamp = revision.timestamp;
if ( analyzePage( $translList, $defn ) ) {
deferred.resolve( createMenu( $transl, $defn ) );
} else {
deferred.reject( mw.msg( 'transl-parse-error' ) );
}
}
return deferred.promise();
} ).done( function ( gui ) {
gui.$wrapper.fadeIn();
$translList.hide();
$button.on( 'click', function () {
$translList.toggle();
gui.$wrapper.toggle();
gui.$langSelector.trigger( 'focus' );
} );
if ( gui.$wrapper.offset().top - $( window ).scrollTop() > EDITBOX_TOP_OFFSET ) {
$( 'html, body' ).animate( {
scrollTop: gui.$wrapper.offset().top - EDITBOX_TOP_OFFSET
}, EDITBOX_SCROLL_DURATION );
}
} ).fail( function ( message ) {
mw.notify( message, { type: 'error' } );
$button.hide();
} );
}
function analyzePage( $translList, $defn ) {
var a2, a3, a4, b, langSection, translations, langs, drafts, metadata,
a = gadget.content.indexOf( ' ({' + '{język polski' );
if ( a === -1 ) {
return false;
}
b = gadget.content.indexOf( '\n== ', a );
b = ( b !== -1 ) ? b : gadget.content.length;
langSection = gadget.content.slice( 0, b );
a2 = langSection.indexOf( '{' + '{tłumaczenia}}\n', a );
b = langSection.indexOf( '{' + '{źródła}}', a2 );
if ( a2 === -1 || b === -1 ) {
return false;
}
translations = langSection.slice( 0, b );
a3 = translations.indexOf( '\n*', a2 );
if (
a3 !== -1 &&
translations.slice( a3 ).indexOf( '{{zobtłum' ) !== -1
) {
a3 = translations.lastIndexOf( '{{zobtłum' );
a3 = translations.indexOf( '\n*', a3 );
}
if ( a3 !== -1 ) {
langs = [];
drafts = {};
metadata = {};
translations.slice( a3 + 1, b ).split( '\n' ).forEach( function ( line, i ) {
var lang, terms,
res = line.match( /^\* *([^:]+): *(.+)/ );
if ( res && res[ 1 ] && res[ 2 ] ) {
lang = res[ 1 ].trim();
terms = res[ 2 ].trim();
langs.push( lang );
metadata[ lang ] = {
$original: $translList.children().eq( i ),
terms: terms
};
if ( forbiddenTranslations.indexOf( lang ) !== -1 ) {
drafts[ lang ] = terms;
metadata[ lang ].unsupported = true;
} else {
drafts[ lang ] = parseTerms( terms ) || terms;
}
}
} );
if (
!langs.length || langs.length !== Object.keys( drafts ).length ||
langs.length !== $translList.children().length
) {
return false;
}
normalizeDrafts( drafts, metadata );
if ( !validateDrafts( $defn, drafts, metadata ) ) {
mw.notify( mw.msg( 'transl-validate-error' ), { type: 'warn' } );
}
gadget.drafts = drafts;
gadget.draftMetadata = metadata;
a3++;
} else {
a3 = b;
}
gadget.initialDrafts = $.extend( {}, gadget.drafts );
gadget.startIndex = a3;
gadget.endIndex = b;
gadget.drafts = sortByLanguage( gadget.drafts );
if ( testSortDivergence() ) {
mw.notify( mw.msg( 'transl-sorted-notice' ) );
}
return true;
}
function parseTerms( terms ) {
var obj = {},
reTerm = /^(\(\d+\.\d+\)) *(.+)/,
reSubterm = /^\[\[([^\]]+?)\]\](?: +\{\{([^\}]+?)\}\})?$/;
terms.split( / *; */ ).forEach( function ( term ) {
var m = term.match( reTerm );
if ( obj === null || m === null || m[ 1 ] in obj ) {
obj = null;
return;
}
obj[ m[ 1 ] ] = m[ 2 ].trim().split( / *, */ ).map( function ( subterm ) {
var base, template,
mm = subterm.match( reSubterm );
if ( mm !== null ) {
base = mw.format( '[' + '[$1]]', mm[ 1 ].trim() );
template = mm[ 2 ] && mw.format( '{' + '{$1}}', mm[ 2 ].trim() );
if ( template === '{' + '{f}}' ) {
// [[Special:PermaLink/7109640#Hiperonimy i hiponimy oraz femininum.]]
template = '{' + '{ż}}';
}
} else {
base = subterm;
}
return { base: base, template: template };
} );
} );
return obj;
}
function renameDraftLanguage( oldName, newName, drafts, draftMetadata ) {
drafts = drafts || gadget.drafts;
draftMetadata = draftMetadata || gadget.draftMetadata;
drafts[ newName ] = drafts[ oldName ];
draftMetadata[ newName ] = draftMetadata[ oldName ];
if ( draftMetadata[ newName ].editState === 'added' ) {
// no-op
} else if ( !( 'renamedFrom' in draftMetadata[ newName ] ) ) {
draftMetadata[ newName ].renamedFrom = oldName;
} else if ( draftMetadata[ newName ].renamedFrom === newName ) {
delete draftMetadata[ newName ].renamedFrom;
}
delete drafts[ oldName ];
delete draftMetadata[ oldName ];
issueParseRequest( newName, drafts[ newName ], draftMetadata[ newName ] );
}
function normalizeDrafts( drafts, metadata ) {
Object.keys( drafts ).map( function ( draftLang ) {
var re = new RegExp( mw.format( '^$1$', mw.util.escapeRegExp( draftLang ) ), 'i' ),
aliases = [].concat( Object.keys( mw.libs.langData.aliases ).filter( function ( lang ) {
return re.test( lang );
} ).map( function ( lang ) {
return mw.libs.langData.aliases[ lang ];
} ), Object.keys( mw.libs.langData.lang2code ).filter( function ( lang ) {
return re.test( lang );
} ) );
return aliases.length ? { original: draftLang, replacement: aliases[ 0 ] } : null;
} ).filter( function ( data ) {
return data && data.original !== data.replacement;
} ).forEach( function ( data ) {
if ( data.replacement in drafts ) {
metadata[ data.original ].duplicate = true;
mw.notify( mw.msg( 'transl-rename-conflict', data.original, data.replacement ), { type: 'warn' } );
} else {
renameDraftLanguage( data.original, data.replacement, drafts, metadata );
metadata[ data.replacement ].normalizedTo = data.replacement;
mw.notify( mw.msg( 'transl-rename-success', data.original, data.replacement ) );
}
} );
}
function validateDrafts( $defn, drafts, metadata ) {
Object.keys( drafts ).filter( function ( lang ) {
return !(
'broken' in metadata[ lang ] ||
'unsupported' in metadata[ lang ]
);
} ).forEach( function ( lang ) {
if (
!$.isPlainObject( drafts[ lang ] ) ||
$.isEmptyObject( drafts[ lang ] ) ||
!validateDraft( $defn, drafts[ lang ] )
) {
metadata[ lang ].broken = true;
}
} );
return !Object.keys( metadata ).some( function ( lang ) {
return 'broken' in metadata[ lang ];
} );
}
function validateDraft( $defn, draft ) {
var reNum = /\((\d+)\.(\d+)\)/,
draftKeys = Object.keys( draft );
return draftKeys.every( function ( term ) {
return $defn.filter( function () {
return $( this ).text() === term;
} ).length && !draft[ term ].some( function ( subterm ) {
return reNum.test( subterm.base );
} );
} ) && draftKeys.slice().sort( function ( a, b ) {
var ma = reNum.exec( a ),
mb = reNum.exec( b );
return ( ma[ 1 ] - mb[ 1 ] ) || ( ma[ 2 ] - mb[ 2 ] );
} ).every( function ( value, index ) {
return value === draftKeys[ index ];
} );
}
function comparatorPl( a, b ) {
// ignore spaces and hyphens (dictionary order): https://w.wiki/bkn
a = a.replace( / /g, '' ).replace( /-/g, '' );
b = b.replace( / /g, '' ).replace( /-/g, '' );
return a.localeCompare( b, 'pl' );
}
function prepareDraft() {
return Object.keys( gadget.drafts ).filter( function ( lang ) {
return gadget.draftMetadata[ lang ].editState !== 'deleted';
} ).map( function ( lang ) {
return mw.format( '* $1: $2\n', lang, serializeDraft( gadget.drafts[ lang ] ) );
} ).join( '' );
}
function resetForms( gui, clearSummary ) {
gadget.activeLang = null;
gui.$langLabel.html( mw.msg( 'transl-select-lang' ) );
gui.$langSelector.val( '' ).trigger( 'focus' );
gui.$saveButton.prop( 'disabled', true );
gui.$textInputs.find( 'input' ).val( '' ).prop( 'disabled', true );
gui.$textArea.find( 'textarea' ).val( '' );
gui.$apply.prop( 'disabled', true );
if ( clearSummary ) {
gui.$summaryEdit.show();
gui.$summaryInput.hide();
gui.$summaryPreview.text( mw.msg( 'transl-summary-preview', '' ) );
}
}
function resetSubmitButtons( gui ) {
var hasChanged = hasPendingChanges();
gui.$diff.prop( 'disabled', !hasChanged );
gui.$submit.prop( 'disabled', !hasChanged );
gui.$clear.prop( 'disabled', !hasChanged );
}
function hasPendingChanges() {
return Object.keys( gadget.draftMetadata ).some( function ( lang ) {
var metadata = gadget.draftMetadata[ lang ];
return 'editState' in metadata || 'renamedFrom' in metadata;
} ) || testSortDivergence();
}
function testDraftDivergence( lang ) {
var initial,
metadata = gadget.draftMetadata[ lang ];
if ( 'renamedFrom' in metadata ) {
initial = gadget.initialDrafts[ metadata.renamedFrom ];
} else {
initial = gadget.initialDrafts[ lang ];
}
return JSON.stringify( initial ) !== JSON.stringify( gadget.drafts[ lang ] );
}
function testSortDivergence() {
var renamedLangs = Object.keys( gadget.drafts ).filter( function ( lang ) {
return 'renamedFrom' in gadget.draftMetadata[ lang ];
} ).map( function ( lang ) {
return gadget.draftMetadata[ lang ].renamedFrom;
} ),
normalizedLangs = Object.keys( gadget.drafts ).filter( function ( lang ) {
return 'normalizedTo' in gadget.draftMetadata[ lang ];
} ).map( function ( lang ) {
return gadget.draftMetadata[ lang ].normalizedTo;
} ),
deletedLangs = Object.keys( gadget.drafts ).filter( function ( lang ) {
return gadget.draftMetadata[ lang ].editState === 'deleted';
} ),
unsortedInitial = Object.keys( gadget.initialDrafts ).filter( function ( lang ) {
return renamedLangs.indexOf( lang ) === -1
&& normalizedLangs.indexOf( lang ) === -1
&& deletedLangs.indexOf( lang ) === -1;
} ),
processedInitial = Object.keys( gadget.drafts ).filter( function ( lang ) {
return unsortedInitial.indexOf( lang ) !== -1;
} );
// assertion: unsortedInitial.length === processedInitial.length
return processedInitial.some( function ( value, index ) {
return value !== unsortedInitial[ index ];
} );
}
function onLoadLang( gui, targetLang, evt ) {
var isBrokenTranslation,
lang = targetLang || gui.$langSelector.val().trim();
if ( evt ) {
evt.preventDefault();
}
if ( !lang ) {
return;
}
resetForms( gui );
if ( lang in mw.libs.langData.aliases ) {
lang = mw.libs.langData.aliases[ lang ];
}
if ( forbiddenLanguageNames.indexOf( lang ) !== -1 ) {
mw.notify( mw.msg( 'transl-forbidden-lang', lang ), { type: 'error' } );
return;
}
isBrokenTranslation = lang in gadget.draftMetadata && 'broken' in gadget.draftMetadata[ lang ];
gadget.activeLang = lang;
gui.$langLabel.html( lang in mw.libs.langData.lang2code
? mw.msg( 'transl-lang-label-iso', lang, mw.libs.langData.lang2code[ lang ] )
: mw.msg( 'transl-lang-label', lang )
);
if ( !( lang in mw.libs.langData.lang2code ) ) {
mw.notify( mw.msg( 'transl-invalid-code', lang ), { type: 'warn' } );
}
gui.$saveButton
.prop( 'disabled', false )
.attr( gadget.preferredLang === lang ? {
value: mw.msg( 'transl-forget-button' ),
title: mw.msg( 'transl-forget-tooltip' )
} : {
value: mw.msg( 'transl-save-button' ),
title: mw.msg( 'transl-save-tooltip' )
} );
if ( isBrokenTranslation ) {
gui.$textInputs.hide();
gui.$textArea.show().find( 'textarea' ).val( gadget.draftMetadata[ lang ].terms );
} else {
gui.$textInputs.show().find( 'input' ).prop( 'disabled', false );
gui.$textArea.hide();
if ( lang in gadget.drafts ) {
gui.$textInputs.children().each( loadDrafts.bind( null, gadget.drafts[ lang ] ) );
}
}
selectPreviewItem( gui, lang );
gui.$apply.prop( 'disabled', false );
setTimeout( function () {
var $el = isBrokenTranslation ? gui.$textArea.find( 'textarea' ) : gui.$textInputs.find( 'input' ).first();
$el.trigger( 'focus' );
}, 1 );
}
function loadDrafts( draft, i, el ) {
var $el = $( el ),
defn = $el.data( 'defn' ),
$inputs = $el.children( 'input' ),
$text = $inputs.first(),
$tmpl = $inputs.last(),
text = [],
tmpl = [];
if ( !draft[ defn ] ) {
return true;
}
draft[ defn ].forEach( function ( obj ) {
text.push( stripBrackets( obj.base ) );
tmpl.push( stripBrackets( obj.template || '' ) );
} );
$text.val( text.join( ', ' ) );
if ( tmpl.join( '' ) !== '' ) {
$tmpl.val( tmpl.join( ',' ) );
}
}
function onSaveLang( gui, evt ) {
var message, updateLang, newValue;
if ( gadget.activeLang !== gadget.preferredLang ) {
message = mw.msg( 'transl-save-prompt', gadget.activeLang );
if ( !!gadget.preferredLang ) {
message += ' ' + mw.msg( 'transl-save-current', gadget.preferredLang );
}
updateLang = true;
} else {
message = mw.msg( 'transl-forget-prompt', gadget.preferredLang );
updateLang = false;
}
newValue = updateLang ? gadget.activeLang : null;
gui.$confirmDialog.append( message ).data( {
newValue: newValue,
updateLang: updateLang
} ).dialog( 'open' );
}
function savePreferredLangRequest( gui ) {
var newValue = gui.$confirmDialog.data( 'newValue' ),
updateLang = gui.$confirmDialog.data( 'updateLang' );
function onSuccess() {
gadget.preferredLang = newValue;
if ( updateLang ) {
gui.$saveButton.attr( {
value: mw.msg( 'transl-forget-button' ),
title: mw.msg( 'transl-forget-tooltip' )
} );
} else {
gui.$saveButton.attr( {
value: mw.msg( 'transl-save-button' ),
title: mw.msg( 'transl-save-tooltip' )
} );
}
mw.notify( mw.msg( 'transl-save-success' ) );
}
if ( !mw.user.isAnon() ) {
return api.saveOption( PREFERRED_LANG_OPTION_KEY, newValue ).done( onSuccess );
} else {
mw.storage.set( PREFERRED_LANG_STORAGE_KEY, newValue );
onSuccess();
return $().promise();
}
}
function stripBrackets( s ) {
return s.replace( /^[\[\{]{2}([^\|\[\]\{\}]+?)[\]\}]{2}$/, '$1' );
}
function abortParseRequest( lang ) {
var metadata = gadget.draftMetadata[ lang ];
if ( metadata && 'request' in metadata ) {
metadata.request.abort();
}
}
function abortAllParseRequests() {
Object.keys( gadget.draftMetadata ).forEach( function ( lang ) {
if ( 'request' in gadget.draftMetadata[ lang ] ) {
gadget.draftMetadata[ lang ].request.abort();
}
} );
}
function issueParseRequest( lang, draft, metadata ) {
var apiPromise = api.get( {
action: 'parse',
text: typeof draft === 'object' ? serializeDraft( draft ) : draft,
prop: [ 'text', 'links' ],
wrapoutputclass: '',
contentmodel: 'wikitext',
disablelimitreport: true
} );
abortParseRequest( lang );
metadata.request = apiPromise.then( function ( data ) {
return parsedWikitextCallback( data, lang );
}, function ( code, data ) {
mw.notify( api.getErrorMessage( data ), { type: 'warn' } );
} ).always( function () {
delete metadata.request;
} ).promise( {
abort: apiPromise.abort
} );
return apiPromise;
}
function onApplyChanges( gui, $defn, evt ) {
var $line,
rawDraft = '',
draft = {},
metadata = gadget.draftMetadata[ gadget.activeLang ] || {},
isFreshDraft = !( gadget.activeLang in gadget.drafts );
if ( evt ) {
evt.preventDefault();
}
if ( 'broken' in metadata ) {
rawDraft = gui.$textArea.find( 'textarea' ).val();
draft = parseTerms( rawDraft ) || {};
if ( !$.isEmptyObject( draft ) && validateDraft( $defn, draft ) ) {
gui.$textInputs.show().find( 'input' ).prop( 'disabled', false );
gui.$textArea.hide();
gui.$textInputs.children().each( loadDrafts.bind( null, draft ) );
delete metadata.broken;
} else {
mw.notify( mw.msg( 'transl-validate-error' ), { type: 'warn' } );
return;
}
} else {
if (
gui.$textInputs.children().filter( constructDrafts.bind( null, draft, gadget.activeLang ) ).length
!== gui.$textInputs.children().length ||
$.isEmptyObject( draft )
) {
return;
}
}
gadget.drafts[ gadget.activeLang ] = !$.isEmptyObject( draft ) ? draft : rawDraft;
if ( isFreshDraft ) {
gadget.drafts = sortByLanguage( gadget.drafts );
gadget.draftMetadata[ gadget.activeLang ] = metadata;
}
if ( !( 'renamedFrom' in metadata || gadget.activeLang in gadget.initialDrafts ) ) {
metadata.editState = 'added';
} else if ( testDraftDivergence( gadget.activeLang ) ) {
metadata.editState = 'modified';
} else {
delete metadata.editState;
}
issueParseRequest( gadget.activeLang, gadget.drafts[ gadget.activeLang ], metadata );
previewTranslation( gui, gadget.activeLang );
previewSummary( gui );
resetSubmitButtons( gui );
$line = selectPreviewItem( gui, gadget.activeLang );
if ( $line.length ) {
// https://stackoverflow.com/a/18927969
gui.$previewbox.animate( {
scrollTop: gui.$previewbox.scrollTop() - gui.$previewbox.offset().top + $line.offset().top
}, PREVIEW_SCROLL_DURATION );
}
}
function sortByLanguage( drafts ) {
// https://stackoverflow.com/a/31102605
var sortedCopy = {};
Object.keys( drafts ).sort( comparatorPl ).forEach( function ( lang ) {
sortedCopy[ lang ] = drafts[ lang ];
} );
return sortedCopy;
}
function constructDrafts( draft, lang, i, el ) {
var terms, tmpls, temp, arr,
$this = $( el ),
$inputs = $this.children( 'input' ),
text = $inputs.first().val().trim(),
tmpl = $inputs.last().val().trim(),
combAcuteAccent = String.fromCharCode( 0x301 ),
success = true;
if ( !text ) {
return true;
}
terms = text.split( / *, */ );
tmpls = tmpl ? tmpl.split( / *, */ ) : null;
arr = [];
terms.filter( function ( term ) {
return term !== '';
} ).map( function ( term ) {
// replace tabs with spaces, delete soft hyphen marks
return term.replace( /\t/g, ' ' ).replace( '\u00ad', '' );
} ).forEach( function ( term, j ) {
var base, template, temp2;
if ( /[\[\]\(\)\{\}<>\/]/.test( term ) ) {
base = term;
} else if (
softStressMarkLanguages.indexOf( lang ) !== -1 &&
( term.indexOf( '`' ) !== -1 || term.indexOf( combAcuteAccent ) !== -1 )
) {
base = mw.format(
'[' + '[$1|$2]]',
term.replaceAll( '`', '' ).replaceAll( combAcuteAccent, '' ),
term.replace( /`(.)/g, '$1' + combAcuteAccent )
);
} else {
base = '[' + '[' + term + ']]';
}
if ( tmpls && tmpls[ j ] ) {
temp2 = tmpls[ j ].split( / *\/ */ );
if ( temp2.every( function ( el ) { return el.match( /[mfżnw]/ ); } ) ) {
template = temp2.map( function ( el ) {
return mw.format( '{' + '{$1}}', el );
} ).join( '/' );
} else if ( /[\[\]\(\)\{\}<>\/]/.test( tmpls[ j ] ) ) {
template = tmpls[ j ];
} else {
template = '{' + '{' + tmpls[ j ] + '}}';
}
// [[Special:PermaLink/7109640#Hiperonimy i hiponimy oraz femininum.]]
template = template.replace( /\{{2}f\}{2}/, '{' + '{ż}}' );
if ( lang === 'niderlandzki' && template.indexOf( '{' + '{w}}' ) !== -1 ) {
// uporczywe używanie kwalifikatora rodzaju wspólnego
mw.notify( 'Nieprawidłowy rodzaj wspólny („w”) dla języka niderlandzkiego. Użyj „m”, „ż” lub „n”.', { type: 'error' } );
success = false;
}
}
arr.push( {
base: base,
template: template
} );
} );
draft[ $this.data( 'defn' ) ] = arr;
return success;
}
function parsedWikitextCallback( data, lang ) {
var $parsed, $els, links;
$parsed = $( data.parse.text.replace( '\n', '' ) ).first( 'p' );
$els = $parsed.find( 'a' );
if ( !$els.length || !( lang in mw.libs.langData.lang2code ) ) {
return $parsed;
}
// TODO: użycie modułu sectionLinks
$els.each( function () {
var href = $( this ).attr( 'href' );
if ( href.indexOf( '#' ) === -1 ) {
href += '#' + mw.libs.langData.lang2code[ lang ];
$( this ).attr( 'href', href );
}
} );
if ( 'falseBlueLinks' in mw.libs ) {
links = data.parse.links.filter( function ( obj ) {
return obj.ns === 0 && obj.exists;
} ).map( function ( obj ) {
return obj.title;
} );
return mw.libs.falseBlueLinks.inspectTitles( links ).then( function () {
mw.libs.falseBlueLinks.processElements( $parsed );
return $parsed;
} );
} else {
return $parsed;
}
}
function serializeDraft( draft ) {
var copy;
if ( typeof( draft ) === 'string' ) {
return draft;
}
copy = $.extend( true, {}, draft );
return $.map( copy, function ( data, defn ) {
return defn + ' ' + $.map( data, function ( word ) {
return ( word.template
? word.base + ' ' + word.template
: word.base
);
} ).join( ', ' );
} ).join( '; ' );
}
function previewTranslation( gui, lang, $li ) {
var $preview, $serializedPreview,
draft = gadget.drafts[ lang ],
metadata = gadget.draftMetadata[ lang ],
editStateClasses = [
'transl-entry-added',
'transl-entry-modified',
'transl-entry-deleted'
];
$li = $li || gui.$preview.find( mw.format( '[data-lang="$1"]', lang ) );
if ( !$li.length ) {
$li = addPreviewItem( gui, lang, true );
}
$li.find( '.transl-preview-entry' ).prevAll().remove().end().replaceWith(
$( '<span>' ).text( mw.format( '$1: ', lang ) ),
$preview = $( '<span>' ).addClass( 'transl-preview-entry' )
);
$serializedPreview = $( '<code>' )
.addClass( 'transl-preview-entry' )
.text( serializeDraft( draft ) );
if ( metadata.request ) {
$li.find( '.transl-draft-change, .transl-draft-remove, .transl-draft-restore, .transl-draft-rename' ).hide();
$li.find( '.transl-draft-abort' ).show();
$preview.addClass( 'transl-parsing' ).text( mw.msg( 'transl-preview-parsing' ) );
metadata.request.done( function ( $parsed ) {
$preview.removeClass( 'transl-parsing' ).html( $parsed.html() );
} ).fail( function () {
$preview.replaceWith( $serializedPreview );
} ).always( function () {
$li.find( '.transl-draft-change, .transl-draft-remove, .transl-draft-rename' ).show();
$li.find( '.transl-draft-restore, .transl-draft-abort' ).hide();
} );
} else {
$preview.replaceWith( $serializedPreview );
}
if ( metadata.editState === 'added' ) {
$li.removeClass( editStateClasses ).addClass( 'transl-entry-added' );
} else if ( metadata.editState === 'deleted' ) {
$li.removeClass( editStateClasses ).addClass( 'transl-entry-deleted' );
} else if ( metadata.editState === 'modified' || 'renamedFrom' in metadata ) {
// placed last to never override deletion status
$li.removeClass( editStateClasses ).addClass( 'transl-entry-modified' );
}
if ( !( 'broken' in metadata ) ) {
$li.removeClass( 'transl-entry-issues' );
}
}
function makePreview( gui ) {
gui.$preview.empty();
Object.keys( gadget.drafts ).forEach( function ( lang ) {
var $li = addPreviewItem( gui, lang, false );
if ( 'renamedFrom' in gadget.draftMetadata[ lang ] ) {
previewTranslation( gui, lang, $li );
}
} );
// assertion: already sorted
}
function addPreviewItem( gui, lang, sort ) {
var $items, sortFn,
$li = makePreviewItem( gui, lang );
gui.$preview.append( $li );
if ( sort ) {
$items = gui.$preview.children();
sortFn = Array.prototype.sort.bind( $items );
sortFn( function ( a, b ) {
return comparatorPl( a.dataset.lang, b.dataset.lang );
} );
gui.$preview.append( $items );
}
return $li;
}
function selectPreviewItem( gui, lang ) {
var $li = gui.$preview.find( mw.format( '[data-lang="$1"]', lang ) );
gui.$preview.children()
.removeClass( 'transl-active' )
.find( '.transl-draft-change' ).removeClass( 'disabled' );
$li
.addClass( 'transl-active' )
.find( '.transl-draft-change' ).addClass( 'disabled' );
return $li;
}
function makePreviewItem( gui, lang ) {
var $li, $preview, $change, $remove, $restore, $rename, $abort,
metadata = gadget.draftMetadata[ lang ];
$li = $( '<li>' ).attr( 'data-lang', lang );
$change = $( '<small>' )
.addClass( 'transl-draft-change' )
.text( mw.msg( 'transl-draft-change' ) )
.attr( 'title', mw.msg( 'transl-draft-change-tooltip' ) );
$remove = $( '<small>' )
.addClass( 'transl-draft-remove' )
.text( mw.msg( 'transl-draft-remove' ) )
.attr( 'title', mw.msg( 'transl-draft-remove-tooltip' ) );
$restore = $( '<small>' )
.addClass( 'transl-draft-restore' )
.text( mw.msg( 'transl-draft-restore' ) )
.attr( 'title', mw.msg( 'transl-draft-restore-tooltip' ) )
.hide();
$rename = $( '<small>' )
.addClass( 'transl-draft-rename' )
.text( mw.msg( 'transl-draft-rename' ) )
.attr( 'title', mw.msg( 'transl-draft-rename-tooltip' ) );
$abort = $( '<small>' )
.addClass( 'transl-draft-abort' )
.text( mw.msg( 'transl-draft-abort' ) )
.attr( 'title', mw.msg( 'transl-draft-abort-tooltip' ) )
.hide();
$change.on( 'click', function () {
if ( lang !== gadget.activeLang ) {
gadget.activeLang = lang;
onLoadLang( gui, lang );
}
} );
$remove.on( 'click', function () {
if ( lang in gadget.initialDrafts || 'renamedFrom' in metadata ) {
metadata.editState = 'deleted';
$li.addClass( 'transl-entry-deleted' );
$remove.hide();
$restore.show();
$rename.hide();
} else {
delete gadget.drafts[ lang ];
delete gadget.draftMetadata[ lang ];
$li.remove();
if ( lang === gadget.activeLang ) {
resetForms( gui );
}
}
previewSummary( gui );
resetSubmitButtons( gui );
} );
$restore.on( 'click', function () {
if ( lang in gadget.initialDrafts || 'renamedFrom' in metadata ) {
if ( testDraftDivergence( lang ) ) {
metadata.editState = 'modified';
} else {
delete metadata.editState;
}
$li.removeClass( 'transl-entry-deleted' );
$remove.show();
$restore.hide();
$rename.show();
}
previewSummary( gui );
resetSubmitButtons( gui );
} );
$rename.on( 'click', function () {
gui.$renameDialog.data( 'activeLang', lang ).dialog( 'open' );
} );
$abort.on( 'click', function () {
abortParseRequest( lang );
} );
$preview = $( '<span>' )
.addClass( 'transl-preview-entry' )
.appendTo( $li );
if ( '$original' in metadata ) {
$preview.html( metadata.$original.html() );
}
$( '<span>' )
.addClass( 'transl-draft-actions' )
.append( $change, $remove, $restore, $rename, $abort )
.appendTo( $li );
if ( 'broken' in metadata || 'duplicate' in metadata ) {
$li.addClass( 'transl-entry-issues' );
} else if ( 'unsupported' in metadata ) {
$change.addClass( 'disabled' );
}
return $li;
}
function makeSummary( custom ) {
var s, renamedMappings, renamedLangs, addedLangs, addedDrafts, deletedLangs, modifiedLangs,
sortDivergence = testSortDivergence();
custom = ( custom || '' ).trim();
function buildString( renamed, added, modified, deleted, delimiter ) {
var generated,
out = [];
if ( renamedLangs.length ) {
out.push( mw.msg( 'transl-summary-renamed', renamed.join( ', ' ) ) );
}
if ( addedLangs.length ) {
out.push( mw.msg( 'transl-summary-added', added.join( delimiter || ', ' ) ) );
}
if ( modifiedLangs.length ) {
out.push( mw.msg( 'transl-summary-modified', modified.join( ', ' ) ) );
}
if ( deletedLangs.length ) {
out.push( mw.msg( 'transl-summary-deleted', deleted.join( ', ' ) ) );
}
if ( sortDivergence ) {
out.push( mw.msg( 'transl-summary-sorted' ) );
}
generated = out.join( ' •• ' );
if ( !!custom && !!generated ) {
return custom + '; ' + generated;
} else {
return custom + generated;
}
}
renamedMappings = Object.keys( gadget.drafts ).filter( function ( lang ) {
return 'renamedFrom' in gadget.draftMetadata[ lang ];
} ).map( function ( to ) {
var from = gadget.draftMetadata[ to ].renamedFrom;
return mw.msg( 'transl-summary-rename-mapping', from, to );
} );
renamedLangs = Object.keys( gadget.drafts ).filter( function ( lang ) {
return 'renamedFrom' in gadget.draftMetadata[ lang ];
} ).map( function ( lang ) {
return gadget.draftMetadata[ lang ].renamedFrom;
} );
addedLangs = Object.keys( gadget.drafts ).filter( function ( lang ) {
return gadget.draftMetadata[ lang ].editState === 'added';
} );
addedDrafts = addedLangs.map( function ( lang ) {
return mw.format( '$1: $2', lang, serializeDraft( gadget.drafts[ lang ] ) );
} );
modifiedLangs = Object.keys( gadget.drafts ).filter( function ( lang ) {
return gadget.draftMetadata[ lang ].editState === 'modified';
} );
deletedLangs = Object.keys( gadget.drafts ).filter( function ( lang ) {
return gadget.draftMetadata[ lang ].editState === 'deleted';
} );
s = buildString( renamedMappings, addedDrafts, modifiedLangs, deletedLangs, ' • ' );
if ( mw.libs.String.codePointLength( s ) > config.wgCommentCodePointLimit ) {
s = buildString( renamedLangs, addedLangs, modifiedLangs, deletedLangs );
}
return s;
}
function trimSummary( summary ) {
if ( mw.libs.String.codePointLength( summary ) > config.wgCommentCodePointLimit ) {
summary = mw.libs.String.trimCodePointLength( '', summary, config.wgCommentCodePointLimit - 3 ).newVal;
summary += '...';
}
return summary;
}
function testProcessedActiveLang( gui ) {
var storedDraft = gadget.drafts[ gadget.activeLang ] || {},
freshDraft = {};
gui.$textInputs.children().each( constructDrafts.bind( null, freshDraft, gadget.activeLang ) );
if ( JSON.stringify( freshDraft ) !== JSON.stringify( storedDraft ) ) {
alert( mw.msg( 'transl-alert-pending', gadget.activeLang, mw.msg( 'transl-apply-button' ) ) );
return false;
}
return true;
}
function onSubmit( gui ) {
var summary = makeSummary( gui.$summaryInput.val() ),
pageDraft = gadget.content.slice( 0, gadget.startIndex ) +
prepareDraft() +
gadget.content.slice( gadget.endIndex, gadget.content.length );
gui.$submit.prop( 'disabled', true );
gui.$summaryInput.prop( 'disabled', true );
gui.$submitSpinner.css( 'visibility', 'visible' );
api.postWithEditToken( api.assertCurrentUser( {
action: 'edit',
title: config.wgPageName,
text: pageDraft,
tags: 'translation-editor',
summary: trimSummary( summary ),
notminor: true,
nocreate: true,
starttimestamp: gadget.starttimestamp,
basetimestamp: gadget.basetimestamp
} ) )
.done( function () {
mw.storage.set( POST_EDIT_STORAGE_KEY, config.wgArticleId.toString() );
location.reload();
} )
.fail( function ( code, data ) {
gui.$submit.prop( 'disabled', false );
gui.$summaryInput.prop( 'disabled', false );
gui.$submitSpinner.css( 'visibility', 'hidden' );
mw.notify( api.getErrorMessage( data ), { type: 'error' } );
} );
}
function onCompare( gui ) {
var newContent = gadget.content.slice( 0, gadget.startIndex ) +
prepareDraft() +
gadget.content.slice( gadget.endIndex, gadget.content.length );
gui.$diffSpinner.css( 'visibility', 'visible' );
gui.$compareDialog.data( 'parseRequest', api.post( {
action: 'compare',
fromrev: config.wgRevisionId,
toslots: 'main',
'totext-main': newContent
} ).done( function ( res ) {
var $diff = $( res.compare.body );
gui.$compareTable.append( $diff );
gui.$compareDialog.dialog( 'open' );
} ).always( function () {
gui.$compareDialog.removeData( 'parseRequest' );
gui.$diffSpinner.css( 'visibility', 'hidden' );
} ).fail( function ( code, data ) {
mw.notify( api.getErrorMessage( data ), { type: 'warn' } );
} ) );
}
// [mediawiki/extensions/WikiEditor] / modules / jquery.wikiEditor.toolbar.js
function buildCharacter( character, actions ) {
if ( typeof character === 'string' ) {
character = {
label: character,
action: {
type: 'replace',
options: {
peri: character,
selectPeri: false
}
}
};
// In some cases the label for the character isn't the same as the
// character that gets inserted (e.g. Hebrew vowels)
} else if ( character && 0 in character && 1 in character ) {
character = {
label: character[ 0 ],
action: {
type: 'replace',
options: {
peri: character[ 1 ],
selectPeri: false
}
}
};
}
if ( character && 'action' in character && 'label' in character ) {
actions[ character.label ] = character.action;
if ( character.titleMsg !== undefined ) {
return mw.html.element( 'span', {
rel: character.label,
title: mw.msg( character.titleMsg )
}, character.label );
} else {
return mw.html.element( 'span', {
rel: character.label
}, character.label );
}
}
mw.log( 'A character for the toolbar was undefined. This is not supposed to happen. Double check the config.' );
// bug 31673; also an additional fix for bug 24208...
return '';
}
function unicodeToAscii( s ) {
var n, ch, index,
out = '',
utf = 'ąåãáćçęèłńóõüśźż',
ascii = 'aaaacceelnoouszz';
for ( n = 0; n < s.length; n++ ) {
ch = s.charAt( n );
index = utf.indexOf( ch );
if ( index !== -1 ) {
out += ascii.charAt( index );
} else {
out += ch;
}
}
return out;
}
function previewSummary( gui ) {
var summary = makeSummary( gui.$summaryInput.val() );
return api.get( {
action: 'parse',
summary : trimSummary( summary ),
prop: ''
} ).done( function ( res ) {
gui.$summaryPreview.html( mw.msg( 'transl-summary-preview', res.parse.parsedsummary ) );
} ).fail( function ( code, data ) {
if ( !( code === 'http' && data && data.textStatus === 'abort' ) ) {
mw.notify( api.getErrorMessage( data ), { type: 'warn' } );
}
} ).always( function () {
gui.$summaryInput.removeData( 'parseRequest' );
} );
}
function createMenu( $transl, $defn ) {
var allowCloseWindow, langSelectorConfig,
gui = {},
$activeTextInput = $( [] ),
charActions = {};
allowCloseWindow = mw.confirmCloseWindow( {
test: hasPendingChanges,
message: mw.msg( 'transl-confirm-close' ),
namespace: 'translationeditor-editwarning'
} );
gui.$langSelector = $( '<input>' )
.attr( {
id: 'transl-langselector',
type: 'text',
size: 22,
autocapitalize: 'off'
} );
gui.$textInputs = $( '<div>' )
.attr( 'id', 'transl-textinputs' );
gui.$textArea = $( '<div>' )
.attr( 'id', 'transl-textarea' )
.append( $( '<textarea>' )
.attr( {
rows: 6,
cols: 71
} )
)
.hide();
$defn.each( function ( i, el ) {
var $num,
$el = $( el );
$( '<div>' )
.data( 'defn', $el.text() )
.append(
$num = $( '<span>' )
.addClass( 'transl-def-label' )
.text( $el.text() ),
$( '<input>' )
.attr( {
type: 'text',
size: 50
} ),
$( '<input>' )
.attr( {
type: 'text',
size: 1
} )
)
.appendTo( gui.$textInputs );
if ( 'termPreview' in mw.libs ) {
mw.libs.termPreview.enablePreview( $num, 'pl' );
}
} );
gui.$loadButton = $( '<input>' )
.attr( {
type: 'button',
value: mw.msg( 'transl-load-button' ),
title: mw.msg( 'transl-load-tooltip' )
} )
.on( 'click', onLoadLang.bind( this, gui, null ) );
gui.$saveButton = $( '<input>' )
.attr( {
type: 'button',
value: mw.msg( 'transl-save-button' ),
title: mw.msg( 'transl-save-tooltip' )
} )
.on( 'click', onSaveLang.bind( this, gui ) );
gui.$apply = $( '<input>' )
.attr( {
type: 'button',
value: mw.msg( 'transl-apply-button' ),
title: mw.msg( 'transl-apply-tooltip' )
} )
.on( 'click', onApplyChanges.bind( this, gui, $defn ) );
gui.$submitSpinner = $.createSpinner().css( 'margin-right', '1em' ).hide();
gui.$compareTable = $( '<table>' ).addClass( [
'diff', 'diff-editfont-' + mw.user.options.get( 'editfont' )
] ).append(
$( '<col>' ).addClass( 'diff-marker' ),
$( '<col>' ).addClass( 'diff-content' ),
$( '<col>' ).addClass( 'diff-marker' ),
$( '<col>' ).addClass( 'diff-content' ),
$( '<tr>' ).addClass( 'diff-title' ).append(
$( '<td>' ).addClass( 'diff-otitle' ).attr( 'colspan', 2 ).text( mw.msg( 'transl-compare-old' ) ),
$( '<td>' ).addClass( 'diff-ntitle' ).attr( 'colspan', 2 ).text( mw.msg( 'transl-compare-new' ) )
)
);
gui.$compareDialog = $( '<div>' ).addClass( 'transl-compare-wrapper' ).dialog( {
resizable: false,
modal: true,
autoOpen: false,
width: 1000,
title: mw.msg( 'transl-compare-title' ),
close: function ( event, ui ) {
gui.$compareTable.find( 'tr' ).first().nextAll().remove();
}
} ).append( gui.$compareTable );
gui.$submitSpinner = $.createSpinner().css( 'visibility', 'hidden' );
gui.$submit = $( '<input>' )
.attr( {
type: 'button',
value: mw.msg( 'transl-submit-button' ),
title: mw.msg( 'transl-submit-tooltip' )
} )
.prop( 'disabled', true )
.on( 'click', function ( evt ) {
abortAllParseRequests();
if ( !gadget.activeLang || testProcessedActiveLang( gui ) ) {
allowCloseWindow.release();
onSubmit( gui );
}
} );
gui.$diffSpinner = $.createSpinner().css( 'visibility', 'hidden' );
gui.$diff = $( '<input>' )
.attr( {
type: 'button',
value: mw.msg( 'transl-compare-button' ),
title: mw.msg( 'transl-compare-tooltip' )
} )
.prop( 'disabled', true )
.on( 'click', function ( evt ) {
abortAllParseRequests();
onCompare( gui );
} );
gui.$clearDialog = $( '<p>' ).text( mw.msg( 'transl-clear-prompt' ) ).dialog( {
resizable: false,
modal: true,
autoOpen: false,
draggable: false,
title: mw.msg( 'transl-clear-title' ),
dialogClass: 'transl-dialog-no-close',
buttons: [
{
text: mw.msg( 'transl-clear-confirm' ),
click: function () {
analyzePage( gui.$wrapper.next( 'ul' ), $defn );
resetForms( gui, true );
resetSubmitButtons( gui );
makePreview( gui );
previewSummary( gui );
gui.$clearDialog.dialog( 'close' );
}
},
{
text: mw.msg( 'transl-clear-cancel' ),
click: function () {
gui.$clearDialog.dialog( 'close' );
}
}
]
} );
gui.$clear = $( '<input>' )
.attr( {
type: 'button',
value: mw.msg( 'transl-clear-button' ),
title: mw.msg( 'transl-clear-tooltip' )
} )
.prop( 'disabled', true )
.on( 'click', function ( evt ) {
abortAllParseRequests();
gui.$clearDialog.dialog( 'open' );
} );
gui.$specialCharsButton = $( '<a>' )
.attr( {
href: '#',
title: mw.msg( 'transl-keyboard-tooltip' )
} )
.html( mw.msg( 'transl-keyboard' ) )
.on( 'click', function () {
gui.$keyboard.show();
return false;
} );
gui.$previewbox = $( '<div>' )
.attr( 'id', 'transl-preview' )
.append( gui.$preview = $( '<ul>' ) );
gui.$keyboard = $( '<div>' )
.attr( 'id', 'transl-keyboard' )
.hide()
.append(
gui.$keyboardSelect = $( '<select>' ),
$( '<input>' )
.attr( {
type: 'button',
value: mw.msg( 'transl-keyboard-close' )
} )
.on( 'click', function ( evt ) {
evt.preventDefault();
gui.$keyboard.hide();
} ),
gui.$keyboardChars = $( '<div>' )
.attr( 'id', 'transl-special-chars' )
)
.appendTo( mw.util.$content );
$.each( mw.libs.specialCharacters, function ( group, chars ) {
var $group = $( '<div>' )
.hide()
.attr( 'data-group', group )
.appendTo( gui.$keyboardChars );
$( '<option>' )
.attr( 'value', group )
.text( mw.msg( 'special-characters-group-' + group ) )
.appendTo( gui.$keyboardSelect );
chars.forEach( function ( character ) {
$( buildCharacter( character, charActions ) ).appendTo( $group );
} );
} );
gui.$keyboardSelect.on( 'change', function () {
var group = $( this ).find( ':selected' ).attr( 'value' );
gui.$keyboardChars.children().hide();
gui.$keyboardChars.find( '[data-group=' + group + ']' ).show();
mw.storage.set( 'ext.gadget.translation-editor.keyboard-group', group );
} );
gui.$keyboardSelect
.find( mw.format(
'[value="$1"]',
mw.storage.get( 'ext.gadget.translation-editor.keyboard-group' ) || 'latin'
) )
.prop( 'selected', true )
.end()
.trigger( 'change' );
gui.$keyboardChars.on( 'click', 'span', function () {
var label = $( this ).text(),
action = charActions[ label ],
replace = ( action.type === 'replace' );
if ( !$activeTextInput.is( ':disabled' ) ) {
// jquery.wikiEditor.toolbar.js, doAction()
$activeTextInput.textSelection(
'encapsulateSelection',
$.extend( {}, action.options, {
replace: replace
} )
).trigger( 'keypress' ).trigger( 'input' );
}
} );
gui.$summarybox = $( '<div>' )
.attr( 'id', 'transl-edit-summary' )
.append(
$( '<label>' )
.attr( 'for', 'transl-summary' )
.text( mw.msg( 'transl-summary-desc' ) ),
' ',
gui.$summaryPreview = $( '<i>' )
.text( mw.msg( 'transl-summary-preview', '' ) ),
' ',
$( '<small>' ).append(
gui.$summaryEdit = $( '<a>' )
.attr( {
href: '#',
title: mw.msg( 'transl-summary-tooltip' )
} )
.text( mw.msg( 'transl-summary-edit' ) )
.on( 'click', function () {
gui.$summaryInput.show().focus();
$( this ).hide();
return false;
} )
),
$( '<br>' ),
gui.$summaryInput = $( '<input>' )
.attr( {
type: 'text',
size: 100,
id: 'transl-summary',
placeholder: mw.msg( 'transl-summary-placeholder' )
} )
.hide()
);
gui.$summaryInput.on( 'input', mw.util.debounce( SUMMARY_PREVIEW_DELAY, function ( evt ) {
if ( gui.$summaryInput.data( 'parseRequest' ) ) {
gui.$summaryInput.data( 'parseRequest' ).abort();
}
gui.$summaryInput.data( 'parseRequest', previewSummary( gui ) );
} ) );
gui.$summaryInput.on( 'input', function ( evt ) {
if ( gui.$summaryInput.data( 'parseRequest' ) ) {
gui.$summaryInput.data( 'parseRequest' ).abort();
}
} );
gui.$editbox = $( '<div>' )
.attr( 'id', 'transl-editbox' )
.append(
$( '<div>' ).append(
gui.$langLabel = $( '<p>' )
.attr( 'id', 'transl-lang-label' ),
gui.$langSelector,
$( '<div>' )
.attr( 'id', 'transl-selector-buttons' )
.append(
gui.$loadButton, gui.$saveButton
)
),
$( '<div>' ).append(
gui.$textInputs,
gui.$textArea,
$( '<span>' ).append(
$( '<small>' ).append( gui.$specialCharsButton ),
' ',
$( '<small>' )
.html( mw.msg( 'transl-rcf' ) )
.attr( 'title', mw.msg( 'transl-rcf-tooltip' ) ),
' ',
$( '<small>' )
.html( mw.msg( 'transl-report-error' ) )
.attr( 'title', mw.msg( 'transl-report-tooltip' ) ),
' ',
$( '<small>' )
.html( mw.msg( 'transl-help' ) )
.attr( 'title', mw.msg( 'transl-help-tooltip' ) )
)
),
$( '<div>' )
.attr( 'id', 'transl-edit-buttons' )
.append(
gui.$apply, $( '<br>' ),
gui.$diff, gui.$diffSpinner, $( '<br>' ),
gui.$submit, gui.$submitSpinner, $( '<br>' ),
gui.$clear ),
gui.$summarybox,
$( '<hr>' ),
gui.$previewbox
);
gui.$editbox.on( 'focus', 'input[type="text"], textarea', function () {
$activeTextInput = $( this );
} );
gui.$wrapper = $( '<div>' )
.attr( 'id', 'transl-wrapper' )
.hide()
.append(
gui.$editbox,
$( '<small>' )
.addClass( [ 'plainlinks', 'editpage-copywarn' ] )
.html( mw.msg( 'transl-submit-legal' ) )
)
.insertAfter( $transl.parent() );
langSelectorConfig = {
fetch: function ( input, response, maxRows ) {
input = unicodeToAscii( input.toLowerCase() ).trim();
response( Object.keys( mw.libs.langData.lang2code ).filter( function ( lang ) {
return (
unicodeToAscii( lang.toLowerCase() ).indexOf( input ) === 0 &&
forbiddenLanguageNames.indexOf( lang ) === -1
);
} ).concat( Object.keys( mw.libs.langData.aliases ).filter( function ( lang ) {
return unicodeToAscii( lang.toLowerCase() ).indexOf( input ) === 0;
} ).map( function ( lang ) {
return mw.libs.langData.aliases[ lang ];
} ) ).filter( function ( lang, i, self ) {
return self.indexOf( lang ) === i;
} ) );
},
result: {
render: function ( suggestion, context ) {
context.data.$container.find( '.suggestions-special' ).hide();
this.text( suggestion );
}
},
special: {
render: function ( query, context ) {
var $label, $result;
if ( !this.children().length ) {
this.append(
$label = $( '<div>' ).addClass( 'special-label' ),
$result = $( '<div>' ).addClass( 'special-query' )
);
} else {
$label = this.find( '.special-label' );
$result = this.find( '.special-query' );
}
query = query.toLowerCase();
if (
query in mw.libs.langData.code2lang &&
forbiddenLanguageCodes.indexOf( query ) === -1
) {
$label.html( mw.msg( 'transl-lang-suggest-code', query ) );
$result.text( mw.libs.langData.code2lang[ query ] );
this.show();
}
}
},
highlightInput: true
};
gui.$langSelector.suggestions( $.extend( true, {}, langSelectorConfig, {
result: {
select: function ( $input ) {
onLoadLang( gui, null );
}
},
special: {
select: function ( $input ) {
var lang = this.find( '.special-query' ).text();
$input.val( lang );
onLoadLang( gui, null );
}
}
} ) )
.on( 'keypress', function ( evt ) {
if ( evt.keyCode === 13 ) { // Enter
gui.$loadButton.trigger( 'click' );
}
} )
.on( 'paste cut drop', function ( evt ) {
$( this ).trigger( 'keypress' );
} );
gui.$renameDialog = $( '<div>' ).append(
gui.$renameDescription = $( '<p>' ),
gui.$renameSelector = $( '<input>' )
.attr( {
id: 'transl-rename-selector',
type: 'text',
size: 22
} )
.suggestions( $.extend( true, {}, langSelectorConfig, {
result: {
select: function ( $input ) {
gui.$renameErrorNotice.hide();
}
},
special: {
select: function ( $input ) {
gui.$renameErrorNotice.hide();
$input.val( this.find( '.special-query' ).text() );
}
}
} ) )
.on( 'keypress', function ( evt ) {
var buttons;
if ( evt.keyCode === 13 ) { // Enter
buttons = gui.$renameDialog.dialog( 'option', 'buttons' );
buttons[ 0 ].click();
}
} )
.on( 'paste cut drop', function ( evt ) {
$( this ).trigger( 'keypress' );
} ),
gui.$renameErrorNotice = $( '<p>' )
.html( mw.msg( 'transl-rename-error' ) ).hide()
).dialog( {
resizable: false,
minHeight: 150,
modal: true,
autoOpen: false,
draggable: false,
title: mw.msg( 'transl-rename-title' ),
dialogClass: 'transl-dialog-no-close',
buttons: [
{
text: mw.msg( 'transl-rename-apply' ),
click: function () {
var oldLang = gui.$renameDialog.data( 'activeLang' ),
newLang = gui.$renameSelector.val().trim();
if ( newLang === '' || newLang === oldLang ) {
return;
}
if (
forbiddenLanguageNames.indexOf( newLang ) !== -1 ||
forbiddenTranslations.indexOf( newLang ) !== -1 ||
newLang in gadget.drafts
) {
gui.$renameErrorNotice.show();
} else {
renameDraftLanguage( oldLang, newLang );
gadget.drafts = sortByLanguage( gadget.drafts );
gui.$preview.find( mw.format( '[data-lang="$1"]', oldLang ) ).remove();
previewTranslation( gui, newLang );
if ( [ oldLang, newLang ].indexOf( gadget.activeLang ) !== -1 ) {
onLoadLang( gui, newLang );
} else if ( !( newLang in mw.libs.langData.lang2code ) ) {
mw.notify( mw.msg( 'transl-invalid-code', newLang ), { type: 'warn' } );
}
previewSummary( gui );
resetSubmitButtons( gui );
gui.$renameDialog.dialog( 'close' );
}
}
},
{
text: mw.msg( 'transl-rename-cancel' ),
click: function () {
gui.$renameDialog.dialog( 'close' );
}
}
],
open: function ( event, ui ) {
var lang = gui.$renameDialog.data( 'activeLang' );
gui.$renameDescription.text( mw.msg( 'transl-rename-desc', lang ) );
gui.$renameErrorNotice.hide();
gui.$renameSelector.val( '' ).focus();
}
} );
gui.$confirmSpinner = $.createSpinner().css( 'margin-right', '1em' ).hide();
gui.$confirmDialog = $( '<p>' ).dialog( {
resizable: false,
minHeight: 150,
modal: true,
autoOpen: false,
draggable: false,
title: mw.msg( 'transl-prompt-title' ),
dialogClass: 'transl-dialog-no-close',
buttons: [
{
text: mw.msg( 'transl-save-save' ),
click: function () {
if ( gui.$confirmDialog.data( 'hasPendingRequest' ) ) {
return;
} else {
gui.$confirmSpinner.show();
gui.$confirmDialog.data( 'hasPendingRequest', true );
}
savePreferredLangRequest( gui )
.fail( function ( code, data ) {
mw.notify( api.getErrorMessage( data ), { type: 'warn' } );
} )
.always( function () {
gui.$confirmDialog.dialog( 'close' );
} );
}
},
{
text: mw.msg( 'transl-save-cancel' ),
click: function () {
if ( !gui.$confirmDialog.data( 'hasPendingRequest' ) ) {
gui.$confirmDialog.dialog( 'close' );
}
}
}
],
close: function ( event, ui ) {
gui.$confirmDialog.empty().removeData( 'hasPendingRequest' );
gui.$confirmSpinner.hide();
}
} );
gui.$textInputs.find( 'input' ).on( 'keypress', function ( evt ) {
if ( evt.key === 'Enter' && !evt.ctrlKey ) {
onApplyChanges( gui, $defn );
}
} );
gui.$confirmSpinner.prependTo( gui.$confirmDialog.parent().find( '.ui-dialog-buttonset' ) );
$( document ).on( 'keypress', function ( evt ) {
if (
evt.key === 'Enter' && evt.ctrlKey &&
gui.$wrapper.is( ':visible' ) && !gui.$submit.prop( 'disabled' )
) {
gui.$submit.trigger( 'click' );
return false;
}
} );
resetForms( gui );
resetSubmitButtons( gui );
makePreview( gui );
previewSummary( gui );
if ( !!gadget.preferredLang ) {
gadget.activeLang = gadget.preferredLang;
gui.$langSelector.val( gadget.activeLang );
onLoadLang( gui, gadget.activeLang );
}
return gui;
}
module.exports = { init: init };