MediaWiki:Gadget-morfeusz-linker-special.js
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 morfeusz,
MAX_CHARACTERS = 500,
REQUEST_DELAY_WARN = 2500, // ms
PREVIEW_DELAY = 250, // ms
issueTracker = 'Wikidyskusja:Narzędzia/Linkowanie automatyczne',
api = new mw.Api( { parameters: { formatversion: 2 } } );
mw.libs.MorfeuszAnalyzer = require( 'ext.gadget.morfeusz-analyzer' ).MorfeuszAnalyzer;
morfeusz = new mw.libs.MorfeuszAnalyzer();
if ( !!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' );
} );
}
mw.messages.set( {
'ml-special-fieldset-label': 'Linkowanie polskich słów',
'ml-special-fieldset-description': 'Wprowadzony niżej niesformatowany tekst zostanie przepuszczony przez ' +
'<a href="http://morfeusz.sgjp.pl/doc/about/" target=_blank>analizator morfologiczny Morfeusz</a>, ' +
'zamieniając rozpoznane słowa na <a href="' + mw.util.getUrl( 'WS:Linki' ) +
'" target=_blank>linki w składni wiki</a>. ' +
'Program Morfeusz oraz dane fleksyjne SGJP/Polimorf udostępniono na licencji BSD 2-Clause ' +
'(<a href="http://morfeusz.sgjp.pl/doc/license/" target=_blank>warunki korzystania</a>).',
'ml-special-input-label': 'Tekst bez wikilinków (maks. {{PLURAL:$1|$1 znak|$1 znaki|$1 znaków}}):',
'ml-special-input-placeholder': 'Wpisz tekst.',
'ml-special-send-button': 'Wyślij',
'ml-special-report-issue': 'Zgłoś problem',
'ml-special-progress-bar-label': 'Trwa wysyłanie danych...',
'ml-special-failed-request': 'Zapytanie zwróciło następujące błędy: <ul>$1</ul>',
'ml-special-failed-request-empty': 'brak wyników',
'ml-special-warn-long-request': 'Zapytanie trwa dłużej niż zwykle. Możesz spróbować wysłać dane ponownie.',
'ml-special-wikitext-header': 'Rezultat',
'ml-special-wikitext-label': 'Poniższy kod można swobodnie edytować.',
'ml-special-wikitext-help': '<p>Zostały wyróżnione <span class="ml-special-wikilink-more">linki</span>, ' +
'którym można przyporządkować więcej niż jedną formę podstawową, ' +
'oraz <span class="ml-special-wikilink-unknown">słowa</span> nienotowane ' +
'w słowniku. Naciśnięcie na pierwsze skutkuje wyświetleniem dymku z polem ' +
'listy i dostępnymi opcjami do wyboru.</p>' +
'<p>Słownik: <strong id="ml-special-dict-id">?</strong>.</p>' +
'<p>Wersja programu Morfeusz: <strong id="ml-special-version">?</strong>.</p>',
'ml-special-clipboard-button': 'Skopiuj do schowka',
'ml-special-clipboard-notice': 'Skopiowano do schowka',
'ml-special-preview-header': 'Podgląd'
} );
function processItems( items ) {
var out = [];
function appendString( s ) {
var lastItem = out[ out.length - 1 ];
if ( typeof lastItem === 'string' ) {
out[ out.length - 1 ] += s;
} else {
out.push( s );
}
}
items.forEach( function ( item ) {
var uniqueLemmas, $span;
if ( item.unknown ) {
// non-linkable element with special styling
out.push( $span = $( '<span>' ).addClass( 'ml-special-wikilink-unknown' ).text( item.form ) );
} else if ( !( 'lemmas' in item ) ) {
// regular non-linkable element
appendString( item.form );
} else {
uniqueLemmas = item.lemmas.map( function ( obj ) {
return obj.lemma;
} ).filter( function ( lemma, index, arr ) {
return arr.indexOf( lemma ) == index; // remove duplicates
} );
if ( uniqueLemmas.length === 1 ) {
// linkable, but no need to disambiguate; generate link and append to result
appendString( mw.libs.MorfeuszAnalyzer.makeWikilink( uniqueLemmas[ 0 ], item.form ) );
} else {
// linkable with selector for disambiguation
$span = $( '<span>' )
.addClass( 'ml-special-wikilink-more' )
.text( mw.libs.MorfeuszAnalyzer.makeWikilink( uniqueLemmas[ 0 ], item.form ) );
out.push( $span.data( 'ml-data', {
form: item.form,
lemmas: uniqueLemmas,
selected: uniqueLemmas[ 0 ],
$el: $span
} ) );
}
}
if ( $span ) {
$span.attr( 'data-ml-initial', $span.text() );
}
} );
return out;
}
// https://stackoverflow.com/a/6150060
function selectEditableContents( el ) {
var range, sel;
range = document.createRange();
range.selectNodeContents( el );
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
function previewWikitext( text, $container ) {
var request = api.get( {
action: 'parse',
text: text,
prop: [ 'text', 'links' ],
contentmodel: 'wikitext',
disablelimitreport: true
} );
return request.then( function ( data ) {
var links,
$parsed = $( data.parse.text );
$parsed.find( 'a' ).each( function () {
var $this = $( this );
$this.attr( 'href', $this.attr( 'href' ) + '#pl' );
} );
if ( 'falseBlueLinks' in mw.libs ) {
links = data.parse.links.filter( function ( obj ) {
return obj.ns === 0 && obj.exists;
} ).map( function ( obj ) {
return obj.title;
} );
if ( links.length ) {
return mw.libs.falseBlueLinks.inspectTitles( links ).then( function () {
mw.libs.falseBlueLinks.processElements( $parsed );
return $parsed;
} );
}
}
return $parsed;
} ).done( function ( $parsed ) {
$container.html( $parsed.html() );
} ).promise( {
abort: request.abort
} );
}
function handleRequestFailure( code, data, messageBox ) {
var messageHtml,
errors = [];
if ( code === undefined || ( code === 'http' && data.textStatus && data.textStatus === 'abort' ) ) {
return false;
} else if ( code === 'http' ) {
errors.push( data.textStatus );
} else if ( code === 'empty' || code === 'malformed' ) {
errors.push( mw.msg( 'ml-special-failed-request-empty' ) );
} else if ( code === 'error' ) {
errors.concat( data.errors );
} else {
errors.push( code );
}
errors = errors.map( function ( error ) {
return mw.format( '<li>$1</li>', error );
} );
messageHtml = mw.msg( 'ml-special-failed-request', errors.join( '' ) );
messageBox.setLabel( new OO.ui.HtmlSnippet( messageHtml ) );
messageBox.setType( 'error' );
return true;
}
function initialize() {
var textInput, submitButton, reportButton, progressBarField, messageBox, messageField,
mainPanel, wikitextFieldset, formPopup, formMenuSelect, clipboardButton,
$wikitext, $preview, $output, request, requestTimer;
function warnLongRequest() {
messageBox.setLabel( mw.msg( 'ml-special-warn-long-request' ) );
messageBox.setType( 'warning' );
messageField.toggle( true );
}
textInput = new OO.ui.MultilineTextInputWidget( {
autocomplete: false,
autofocus: true,
autosize: true,
maxLength: MAX_CHARACTERS,
rows: 3,
placeholder: mw.msg( 'ml-special-input-placeholder' ),
spellcheck: true,
validate: 'non-empty',
label: String( MAX_CHARACTERS )
} ).on( 'change', function ( value ) {
textInput.setLabel( String( MAX_CHARACTERS - value.length ) );
} ).on( 'enter', function ( evt ) {
if ( evt.originalEvent.ctrlKey ) {
submitButton.emit( 'click' );
}
} );
submitButton = new OO.ui.ButtonWidget( {
label: mw.msg( 'ml-special-send-button' ),
flags: [ 'primary', 'progressive' ]
} ).on( 'click', function () {
if ( request && 'abort' in request ) {
request.abort();
}
clearTimeout( requestTimer );
textInput.getValidity().then( function () {
$output.hide();
progressBarField.toggle( true );
messageField.toggle( false );
request = morfeusz.analyze( textInput.getValue() );
requestTimer = setTimeout( warnLongRequest, REQUEST_DELAY_WARN );
return request;
} ).then( function ( data ) {
$( '#ml-special-dict-id' ).text( data.dictionaryId );
$( '#ml-special-version' ).text( data.libraryVersion );
return processItems( data.items );
} ).then( function ( items ) {
var serialized = $( '<p>' ).append( items ).text();
request = previewWikitext( serialized, $preview );
return request.done( function () {
items.forEach( function ( item, index, arr ) {
if ( typeof item === 'string' ) {
arr[ index ] = item.replace( /\n/g, '<br>' );
}
} );
$wikitext.html( items );
clearTimeout( requestTimer );
messageField.toggle( false );
progressBarField.toggle( false );
$output.show();
} );
} ).fail( function ( code, data ) {
if ( handleRequestFailure( code, data, messageBox ) ) {
clearTimeout( requestTimer );
messageField.toggle( true );
progressBarField.toggle( false );
}
} );
} );
reportButton = new OO.ui.ButtonWidget( {
label: mw.msg( 'ml-special-report-issue' ),
framed: false,
icon: 'feedback'
} ).on( 'click', function () {
window.open( mw.util.getUrl( issueTracker ) );
} );
progressBarField = new OO.ui.FieldLayout( new OO.ui.ProgressBarWidget(), {
label: mw.msg( 'ml-special-progress-bar-label' ),
align: 'top'
} ).toggle( false );
messageBox = new OO.ui.MessageWidget();
messageField = new OO.ui.FieldLayout( messageBox ).toggle( false );
mainPanel = new OO.ui.PanelLayout( {
content: [
new OO.ui.FieldsetLayout( {
label: mw.msg( 'ml-special-fieldset-label' ),
items: [
new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
label: new OO.ui.HtmlSnippet( mw.msg( 'ml-special-fieldset-description' ) )
} ) ),
new OO.ui.FieldLayout( textInput, {
label: mw.msg( 'ml-special-input-label', MAX_CHARACTERS ),
align: 'top'
} ),
new OO.ui.FieldLayout( new OO.ui.Widget( {
content: [
new OO.ui.HorizontalLayout( {
items: [ submitButton, reportButton ]
} )
]
} ) ),
progressBarField,
messageField
]
} )
],
framed: true,
padded: true,
expanded: false
} );
$wikitext = $( '<div>' )
.addClass( [ 'mw-editfont-monospace', 'ml-special-wikitext-container' ] )
.prop( 'contenteditable', true )
.on( 'select.editable', function () {
selectEditableContents( this );
} )
.on( 'input', function ( evt ) {
if ( $preview.data( 'parseRequest' ) ) {
$preview.data( 'parseRequest' ).abort();
}
$wikitext.find( '[data-ml-initial]' ).each( function () {
var $wikilink = $( this );
if ( $wikilink.text() !== $wikilink.data( 'ml-initial' ) ) {
$wikilink.removeClass().removeAttr( 'data-ml-initial' );
}
} );
formPopup.toggle( false );
} )
.on( 'input', mw.util.debounce( PREVIEW_DELAY, function ( evt ) {
$preview.data( 'parseRequest', previewWikitext( $wikitext.text(), $preview ) );
} ) );
formMenuSelect = new OO.ui.MenuSelectWidget( {
autoHide: false,
hideOnChoose: false,
hideWhenOutOfView: false
} ).on( 'choose', function ( option ) {
var data = option.getData();
data.selected = option.getLabel();
data.$el.text( mw.libs.MorfeuszAnalyzer.makeWikilink( data.selected, data.form ) );
data.$el.attr( 'data-ml-initial', data.$el.text() );
formPopup.toggle( 'false' ); // doesn't work, but...
$wikitext.trigger( 'click' ); // this hack does the trick
if ( $preview.data( 'parseRequest' ) ) {
$preview.data( 'parseRequest' ).abort();
}
$preview.data( 'parseRequest', previewWikitext( $wikitext.text(), $preview ) );
} );
formPopup = new OO.ui.PopupWidget( {
content: [ formMenuSelect ],
autoClose: true
} );
$( document.body ).append( formPopup.$element );
$wikitext.on( 'click', '.ml-special-wikilink-more', function ( evt ) {
var $wikilink = $( this ),
data = $wikilink.data( 'ml-data' );
formMenuSelect.clearItems().addItems( data.lemmas.map( function ( lemma ) {
return new OO.ui.MenuOptionWidget( {
data: data,
label: lemma,
disabled: lemma === data.selected
} );
} ) );
formMenuSelect.toggle( true );
formMenuSelect.unbindDocumentKeyDownListener();
formMenuSelect.unbindDocumentKeyPressListener();
formPopup.setFloatableContainer( $wikilink );
formPopup.toggle( true );
formPopup.setSize( formMenuSelect.$element.width(), formMenuSelect.$element.height() );
} );
clipboardButton = new OO.ui.ButtonWidget( {
label: mw.msg( 'ml-special-clipboard-button' ),
icon: 'articles'
} ).on( 'click', function () {
$wikitext.trigger( 'select.editable' );
document.execCommand( 'copy' );
mw.notify( mw.msg( 'ml-special-clipboard-notice' ) );
} );
wikitextFieldset = new OO.ui.FieldsetLayout( {
items: [
new OO.ui.FieldLayout( new OO.ui.Widget( {
$content: $wikitext
} ), {
label: mw.msg( 'ml-special-wikitext-label' ),
align: 'top',
help: new OO.ui.HtmlSnippet( mw.msg( 'ml-special-wikitext-help' ) )
} ),
new OO.ui.FieldLayout( clipboardButton ),
]
} );
$preview = $( '<div>' );
$output = $( '<div>' ).append(
$( '<h2>' ).text( mw.msg( 'ml-special-wikitext-header' ) ),
wikitextFieldset.$element,
$( '<h2>' ).text( mw.msg( 'ml-special-preview-header' ) ),
$preview
).hide();
$( '#mw-content-text' ).empty().append( mainPanel.$element, $output );
}
$( initialize );