MediaWiki:Gadget-iwrm.js: различия между версиями
Перейти к навигации
Перейти к поиску
Содержимое удалено Содержимое добавлено
Stjn (обсуждение | вклад) fix |
Stjn (обсуждение | вклад) уточнение проверки на разлогин |
||
(не показано 13 промежуточных версий этого же участника) | |||
Строка 57: | Строка 57: | ||
'wgTitle', |
'wgTitle', |
||
'wgUserGroups', |
'wgUserGroups', |
||
'wgUserName' |
'wgUserName', |
||
] ); |
] ); |
||
// Special:BlankPage or individual page |
// Special:BlankPage, category or individual page |
||
var _isSiteWide = true; |
var _isSiteWide = true; |
||
var _isCategory = _c.wgNamespaceNumber === 14; |
|||
if ( _c.wgCanonicalSpecialPageName !== 'Blankpage' ) { |
if ( _c.wgCanonicalSpecialPageName !== 'Blankpage' ) { |
||
_isSiteWide = false; |
_isSiteWide = false; |
||
Строка 141: | Строка 142: | ||
// Expression for interwiki links: [[$1:$2]] |
// Expression for interwiki links: [[$1:$2]] |
||
var _regularExp = /\[{2}:?([a-z-]+):([^\[\]\|\n]+)(?:\|([^\|\n]*?))?\]{2}/gi; |
var _regularExp = /\[{2}:? *([a-z-]+) *: *([^\[\]\|\n]+)(?:\|([^\|\n]*?))?\]{2}/gi; |
||
// Expression for interwiki links in language: [[$1]] ({{lang-$2|$3}}$4 |
// Expression for interwiki links in language: [[$1]] ({{lang-$2|$3}}$4 |
||
Строка 147: | Строка 148: | ||
// Expression for prefixes: [[:$1:]] |
// Expression for prefixes: [[:$1:]] |
||
var _regularPrefix = /:?([a-z-]+):/gi; |
var _regularPrefix = / *:? *([a-z-]+) *: */gi; |
||
// Expression for templates: {{iw}}, {{нп5}}, {{не переведено 5}} |
// Expression for templates: {{iw}}, {{нп5}}, {{не переведено 5}} |
||
Строка 204: | Строка 205: | ||
notAllowed: 'Чтобы не распатрулировать статьи, скрипт можно использовать только автопатрулируемым, патрулирующим и администраторам.', |
notAllowed: 'Чтобы не распатрулировать статьи, скрипт можно использовать только автопатрулируемым, патрулирующим и администраторам.', |
||
noArticle: ' |
noArticle: 'Страница «$1» не найдена. Вероятно, до неё добрались удалисты, переименисты или коммунисты.', |
||
noLinks: 'Не обнаружено прямых интервики-ссылок. Пропустите страницу.', |
noLinks: 'Не обнаружено прямых интервики-ссылок. Пропустите страницу.', |
||
unchangedCode: 'Вы не заполнили первый параметр в одном из шаблонов или не заполнили текст ссылки. Изменения не были записаны, проверьте все поля ввода.', |
unchangedCode: 'Вы не заполнили первый параметр в одном из шаблонов или не заполнили текст ссылки. Изменения не были записаны, проверьте все поля ввода.', |
||
Строка 275: | Строка 276: | ||
// Check sitelinks |
// Check sitelinks |
||
IWRM.api.checkSitelinks = |
IWRM.api.checkSitelinks = ( lang, title ) => { |
||
function getDBname( lang ) { |
|||
if ( lang === 'be-tarask' ) { |
|||
return 'be_x_old' + 'wiki'; |
|||
} |
|||
return lang.split( '-' ).join( '_' ) + 'wiki'; |
|||
} |
|||
return _wikidataApi.get( { |
return _wikidataApi.get( { |
||
action: 'wbgetentities', |
action: 'wbgetentities', |
||
sites: |
sites: getDBname( lang ), |
||
sitefilter: _c.wgDBname, |
sitefilter: _c.wgDBname, |
||
titles: title, |
titles: title, |
||
Строка 288: | Строка 297: | ||
// Get full article text |
// Get full article text |
||
IWRM.api.getFullText = |
IWRM.api.getFullText = ( title ) => { |
||
if ( !title ) { |
if ( !title ) { |
||
_ui.fn.notify( 'script', null, { |
_ui.fn.notify( 'script', null, { |
||
Строка 299: | Строка 308: | ||
// Success |
// Success |
||
function resolvedWithSuccess( data ) { |
function resolvedWithSuccess( data ) { |
||
var revision = |
var revision = OO.getProp( data, 'query', 'pages', 0, 'revisions', 0 ); |
||
if ( revision ) { |
if ( revision ) { |
||
Строка 305: | Строка 314: | ||
// Modify the text prematurely to show the changes |
// Modify the text prematurely to show the changes |
||
IWRM.data.modifiedText = replaceLinks( revision.content, |
IWRM.data.modifiedText = replaceLinks( revision.content, ( obj, text ) => { |
||
var modified = getTemplate( obj ); |
var modified = getTemplate( obj ); |
||
Строка 313: | Строка 322: | ||
} |
} |
||
var isMissing = |
var isMissing = OO.getProp( data, 'query', 'pages', 0, 'missing' ); |
||
if ( isMissing ) { |
if ( isMissing ) { |
||
_ui.fn.notify( 'missingtitle', null ); |
_ui.fn.notify( 'missingtitle', null, { replace: title } ); |
||
} |
} |
||
return; |
|||
} |
} |
||
Строка 335: | Строка 345: | ||
// Send a request to CheckWiki server |
// Send a request to CheckWiki server |
||
IWRM.api.submitCheckWiki = |
IWRM.api.submitCheckWiki = ( title ) => { |
||
return $.get( 'https://checkwiki.toolforge.org/cgi-bin/checkwiki.cgi', { |
return $.get( 'https://checkwiki.toolforge.org/cgi-bin/checkwiki.cgi', { |
||
project: _c.wgDBname, |
project: _c.wgDBname, |
||
Строка 345: | Строка 355: | ||
// Get rate limits for the account |
// Get rate limits for the account |
||
IWRM.api.getRateLimits = |
IWRM.api.getRateLimits = () => { |
||
// Success |
// Success |
||
function resolvedWithSuccess( data ) { |
function resolvedWithSuccess( data ) { |
||
var data_rl = data |
var data_rl = OO.getProp( data, 'query', 'userinfo', 'ratelimits' ); |
||
if ( data_rl ) { |
if ( data_rl ) { |
||
var limitsList = Object.keys( data_rl ); |
var limitsList = Object.keys( data_rl ); |
||
if ( limitsList.length === 0 ) { |
if ( limitsList.length === 0 ) { |
||
IWRM.prefs.limit = _searchHighLimit; |
|||
return IWRM.prefs.limit; |
|||
} |
} |
||
} |
} |
||
return IWRM. |
return IWRM.prefs.limit; |
||
} |
} |
||
Строка 364: | Строка 375: | ||
_ui.fn.notify( error, data, { replace: _locale.requests.getRateLimits } ); |
_ui.fn.notify( error, data, { replace: _locale.requests.getRateLimits } ); |
||
return IWRM. |
return IWRM.prefs.limit; |
||
} |
} |
||
Строка 377: | Строка 388: | ||
// Get search request data |
// Get search request data |
||
IWRM.api.getSearchData = |
IWRM.api.getSearchData = ( customList ) => { |
||
if ( !offset ) { |
|||
offset = 0; |
|||
} |
|||
var didJustLoad = IWRM.data.title === ''; |
var didJustLoad = IWRM.data.title === ''; |
||
var hasCustomList = typeof customList !== 'undefined'; |
|||
var searchOptions = { |
var searchOptions = { |
||
Строка 390: | Строка 399: | ||
srsearch: _searchRequest, |
srsearch: _searchRequest, |
||
srsort: 'last_edit_desc', |
srsort: 'last_edit_desc', |
||
sroffset: offset, |
sroffset: IWRM.offset, |
||
srlimit: IWRM.prefs.limit, |
srlimit: IWRM.prefs.limit, |
||
formatversion: 2 |
formatversion: 2 |
||
Строка 397: | Строка 406: | ||
// Success |
// Success |
||
function resolvedWithSuccess( data ) { |
function resolvedWithSuccess( data ) { |
||
var data_sr = data |
var data_sr = OO.getProp( data, 'query', 'search' ); |
||
var offset = OO.getProp( data, 'continue', 'sroffset' ); |
|||
if ( data_sr ) { |
if ( data_sr ) { |
||
if ( offset ) { |
|||
IWRM.offset = offset; |
|||
} |
|||
for ( var i = 0; i < data_sr.length; i++ ) { |
for ( var i = 0; i < data_sr.length; i++ ) { |
||
var title = data_sr[ i ].title; |
var title = data_sr[ i ].title; |
||
Строка 418: | Строка 432: | ||
} |
} |
||
// |
// API call or a promise for custom list |
||
function doApiCall( searchOptions ) { |
|||
if ( !_isSiteWide && didJustLoad ) { |
|||
if ( customList ) { |
|||
searchOptions.srsearch = _c.wgPageName; |
|||
IWRM.list = customList.map( el => { |
|||
searchOptions.srwhat = 'nearmatch'; |
|||
return { data: el }; |
|||
} ); |
|||
IWRM.data.title = customList[ 0 ]; |
|||
// Do a pseudo-call to something |
|||
return mw.loader.using( 'mediawiki.util', () => { |
|||
return IWRM.list; |
|||
} ); |
|||
} |
|||
return _api.get( searchOptions ) |
|||
.done( resolvedWithSuccess ) |
|||
.fail( resolvedWithFailure ); |
|||
} |
|||
// Add different options for non site-wide search |
|||
if ( !_isSiteWide ) { |
|||
if ( _isCategory ) { |
|||
// Request the pages in category |
|||
searchOptions.srsearch = `${ searchOptions.srsearch } incategory:"${ _c.wgTitle }"`; |
|||
} else if ( didJustLoad ) { |
|||
// Request the current page |
|||
searchOptions.srsearch = _c.wgPageName; |
|||
searchOptions.srwhat = 'nearmatch'; |
|||
} |
|||
} |
} |
||
// Return a promise |
// Return a promise |
||
if ( didJustLoad ) { |
if ( didJustLoad ) { |
||
return IWRM.api.getRateLimits().then( |
return IWRM.api.getRateLimits().then( ( limit ) => { |
||
searchOptions.srlimit = limit; |
searchOptions.srlimit = limit; |
||
return |
return doApiCall( searchOptions ); |
||
} ); |
} ); |
||
} |
} |
||
return |
return doApiCall( searchOptions ); |
||
} |
} |
||
// Request diff with changes |
// Request diff with changes |
||
IWRM.api.loadDiff = |
IWRM.api.loadDiff = ( title ) => { |
||
if ( !title ) { |
if ( !title ) { |
||
_ui.fn.notify( 'script', null, { |
_ui.fn.notify( 'script', null, { |
||
Строка 460: | Строка 499: | ||
// Submit changes |
// Submit changes |
||
IWRM.api.submitChanges = |
IWRM.api.submitChanges = ( title, modified, summary ) => { |
||
if ( !title ) { |
if ( !title ) { |
||
_ui.fn.notify( 'script', null, { |
_ui.fn.notify( 'script', null, { |
||
Строка 482: | Строка 521: | ||
if ( !summary ) { |
if ( !summary ) { |
||
summary = ''; |
summary = ''; |
||
} |
|||
// Generic function for API call |
|||
function doApiCall() { |
|||
return _api.edit( |
|||
title, |
|||
function( revision ) { |
|||
var text = _ui.fn.parseChanges( revision.content ); |
|||
if ( text === false ) { |
|||
text = revision.content; |
|||
} |
|||
return { |
|||
text: text, |
|||
summary: summary, |
|||
minor: true, |
|||
watchlist: 'nochange', |
|||
tags: IWRM.prefs.tag |
|||
} |
|||
} |
|||
).done( resolvedWithSuccess ).fail( resolvedWithFailure ); |
|||
} |
} |
||
Строка 511: | Строка 529: | ||
// Send a request to CheckWiki server |
// Send a request to CheckWiki server |
||
IWRM.api.submitCheckWiki( title ); |
IWRM.api.submitCheckWiki( data.title || title ); |
||
// Remove data for this article |
// Remove data for this article |
||
Строка 517: | Строка 535: | ||
// Show post-edit confirmation |
// Show post-edit confirmation |
||
mw.config.set( { |
|||
wgCurRevisionId: data.newrevid, |
|||
wgRevisionId: data.newrevid, |
|||
} ); |
|||
mw.hook( 'postEdit' ).fire(); |
mw.hook( 'postEdit' ).fire(); |
||
} |
} |
||
Строка 532: | Строка 554: | ||
// Try to submit once more for edit conflicts |
// Try to submit once more for edit conflicts |
||
return doApiCall(); |
return doApiCall(); |
||
} |
|||
// Generic function for API call |
|||
function doApiCall() { |
|||
return _api.edit( |
|||
title, |
|||
( revision ) => { |
|||
var text = _ui.fn.parseChanges( revision.content ); |
|||
if ( text === false ) { |
|||
text = revision.content; |
|||
} |
|||
return { |
|||
assertuser: _c.wgUserName, |
|||
text: text, |
|||
summary: summary, |
|||
minor: true, |
|||
watchlist: 'nochange', |
|||
tags: IWRM.prefs.tag |
|||
} |
|||
} |
|||
).done( resolvedWithSuccess ).fail( resolvedWithFailure ); |
|||
} |
} |
||
Строка 615: | Строка 659: | ||
// Get page index from a list, see getSearchData |
// Get page index from a list, see getSearchData |
||
function getPageIndex( title ) { |
function getPageIndex( title ) { |
||
return IWRM.list.findIndex( |
return IWRM.list.findIndex( ( page ) => { |
||
return page.data === title; |
return page.data === title; |
||
} ); |
} ); |
||
Строка 626: | Строка 670: | ||
// Check if OOUI element exists |
// Check if OOUI element exists |
||
function ifUIElementsExist( |
function ifUIElementsExist( El, does, doesnt ) { |
||
if ( Array.isArray( |
if ( Array.isArray( El ) ) { |
||
El.forEach( ( Element ) => { |
|||
ifUIElementsExist( |
ifUIElementsExist( Element, does, doesnt ) |
||
} ); |
} ); |
||
return; |
return; |
||
} |
} |
||
if ( |
if ( El ) { |
||
return ( typeof does !== 'undefined' ? does( |
return ( typeof does !== 'undefined' ? does( El ) : true ); |
||
} |
} |
||
Строка 642: | Строка 686: | ||
// Normalise prefix data |
// Normalise prefix data |
||
function |
function normalisePrefix( lang, title ) { |
||
var normalPrefix = _normalisedPrefixes[ lang ]; |
var normalPrefix = _normalisedPrefixes[ lang ]; |
||
var normalTitle = title; |
var normalTitle = title; |
||
if ( normalPrefix === 'en' ) { |
if ( normalPrefix === 'en' || normalPrefix === _c.wgContentLanguage ) { |
||
var prefixMatch = _regularPrefix.exec( title ); |
var prefixMatch = _regularPrefix.exec( title ); |
||
if ( prefixMatch !== null ) { |
if ( prefixMatch !== null ) { |
||
Строка 681: | Строка 725: | ||
// Normalise prefixes |
// Normalise prefixes |
||
if ( _normalisedPrefixKeys.includes( lang ) ) { |
if ( _normalisedPrefixKeys.includes( lang ) ) { |
||
var normalisedData = |
var normalisedData = normalisePrefix( lang, title ); |
||
lang = normalisedData.lang; |
lang = normalisedData.lang; |
||
title = normalisedData.title; |
title = normalisedData.title; |
||
Строка 762: | Строка 806: | ||
// Clear all button |
// Clear all button |
||
_ui.ClearAll = |
_ui.ClearAll = () => { |
||
var Btn = new OO.ui.ButtonInputWidget( { |
var Btn = new OO.ui.ButtonInputWidget( { |
||
disabled: true, |
disabled: true, |
||
Строка 769: | Строка 813: | ||
// Set event listeners |
// Set event listeners |
||
Btn.$button.on( 'click', |
Btn.$button.on( 'click', () => { |
||
$( '.iwrm-clear-toggle' ).click(); |
$( '.iwrm-clear-toggle' ).click(); |
||
Btn.setLabel( Btn.getLabel() === _locale.clearAll ? _locale.clearAllRestore : _locale.clearAll ); |
Btn.setLabel( Btn.getLabel() === _locale.clearAll ? _locale.clearAllRestore : _locale.clearAll ); |
||
Строка 778: | Строка 822: | ||
// Diff area |
// Diff area |
||
_ui.Diff = function( |
_ui.Diff = function( Diff ) { |
||
// HTML parts for diff layout |
// HTML parts for diff layout |
||
var diffLayout = '<table class="diff"><col class="diff-marker"><col class="diff-content"><col class="diff-marker"><col class="diff-content">'; |
var diffLayout = '<table class="diff"><col class="diff-marker"><col class="diff-content"><col class="diff-marker"><col class="diff-content">'; |
||
// Render an error if nothing was provided |
// Render an error if nothing was provided |
||
if ( ! |
if ( !Diff ) { |
||
var $ErrorText = $( '<div>' ).addClass( 'error iwrm-error' ).text( _locale.errors.noLinks ); |
var $ErrorText = $( '<div>' ).addClass( 'error iwrm-error' ).text( _locale.errors.noLinks ); |
||
Строка 797: | Строка 841: | ||
// Render the diff |
// Render the diff |
||
var $Diff = $( diffLayout ); |
var $Diff = $( diffLayout ); |
||
$Diff.append( |
$Diff.append( Diff ); |
||
return $Diff; |
return $Diff; |
||
Строка 803: | Строка 847: | ||
// Main dropdown |
// Main dropdown |
||
_ui.Dropdown = |
_ui.Dropdown = () => { |
||
_FirstHeading.find( 'span' ).text( IWRM.data.title ); |
_FirstHeading.find( 'span' ).text( IWRM.data.title ); |
||
_ui.PageLinks( IWRM.data.title ); |
_ui.PageLinks( IWRM.data.title ); |
||
Строка 857: | Строка 901: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Summary, |
IWRM.ui.Summary, |
||
( El ) => { |
|||
El.setValue( IWRM.prefs.summary ); |
|||
} |
} |
||
); |
); |
||
// Render new diff |
// Render new diff |
||
_ui.fn.renderDiff( title ).then( |
_ui.fn.renderDiff( title ).then( () => { |
||
// If there was a no links window, remove the previous item |
// If there was a no links window, remove the previous item |
||
if ( hadNoDiff ) { |
if ( hadNoDiff ) { |
||
Строка 884: | Строка 928: | ||
// Editing interface |
// Editing interface |
||
_ui.EditingInterface = |
_ui.EditingInterface = ( $Container, $Bar ) => { |
||
var $ChangedLines = $Container.find( '.diff-deletedline' ); |
var $ChangedLines = $Container.find( '.diff-deletedline' ); |
||
var promises = []; |
var promises = []; |
||
$ChangedLines.each( |
$ChangedLines.each( ( index, el ) => { |
||
promises.push( |
promises.push( |
||
_ui.fn.modifyLine( index, el, $ChangedLines.length, $Container, $Bar ) |
_ui.fn.modifyLine( index, el, $ChangedLines.length, $Container, $Bar ) |
||
Строка 894: | Строка 938: | ||
} ); |
} ); |
||
Promise.all( promises ).then( |
Promise.all( promises ).then( () => { |
||
// Remove any stray lines |
// Remove any stray lines |
||
$( '.iwrm-diff' ).find( '.diff-addedline' ).parent().remove(); |
$( '.iwrm-diff' ).find( '.diff-addedline' ).parent().remove(); |
||
Строка 900: | Строка 944: | ||
// Focus on first element |
// Focus on first element |
||
var FirstInput = document.querySelector( '.iwrm-line input' ); |
var FirstInput = document.querySelector( '.iwrm-line input' ); |
||
if ( FirstInput !== null ) { |
|||
FirstInput.focus(); |
|||
FirstInput.focus(); |
|||
} |
|||
mw.hook( 'iwrm.content' ).fire( $Container ); |
mw.hook( 'iwrm.content' ).fire( $Container ); |
||
Строка 907: | Строка 953: | ||
// Footer |
// Footer |
||
_ui.Footer = |
_ui.Footer = () => { |
||
// Summary field |
// Summary field |
||
IWRM.ui.Summary = new OO.ui.TextInputWidget( { |
IWRM.ui.Summary = new OO.ui.TextInputWidget( { |
||
Строка 921: | Строка 967: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Submit, |
IWRM.ui.Submit, |
||
function( |
function( Element ) { |
||
Element.$button.click(); |
|||
} |
} |
||
); |
); |
||
Строка 965: | Строка 1011: | ||
// Input field |
// Input field |
||
_ui.InputField = |
_ui.InputField = ( obj, modified, entity, isTemplate ) => { |
||
var Input = new OO.ui.TextInputWidget( { |
|||
var isTemplate = true; |
|||
placeholder: obj.fullText, |
|||
var modified = getTemplate( obj ); |
|||
value: modified, |
|||
title: _locale.notice.moveUpDown |
|||
} ); |
|||
Input.$input.data( 'old', obj.fullText ); |
|||
// Wikify the data (two times for link text transformations) |
|||
// Success |
|||
Wikify( Input.$input[ 0 ] ); |
|||
function resolvedWithSuccess( data ) { |
|||
Wikify( Input.$input[ 0 ] ); |
|||
var entity; |
|||
var isMissing = data && data.entities && data.entities[ '-1' ]; |
|||
if ( !isMissing ) { |
|||
entity = data && data.entities; |
|||
if ( entity ) { |
|||
var keys = Object.keys( entity ); |
|||
entity = entity[ keys[ 0 ] ]; |
|||
} |
|||
// Render and add an input field |
|||
// Change the output if a local page is available |
|||
var Field = new OO.ui.FieldLayout( Input, { |
|||
var data_sl = entity.sitelinks && entity.sitelinks[ _c.wgDBname ]; |
|||
align: 'top', |
|||
if ( data_sl ) { |
|||
label: null, |
|||
modified = getLink( obj, data_sl.title ); |
|||
content: [ _ui.InputLinks( Input, obj.tmpl.lang, obj.tmpl.origTitle, entity ) ] |
|||
isTemplate = false; |
|||
} ); |
|||
} |
|||
// Check if article exists in the project |
|||
if ( isTemplate && obj.lang ) { |
|||
var Input = new OO.ui.TextInputWidget( { |
|||
_ui.fn.checkArticle( modified, Field ); |
|||
placeholder: obj.fullText, |
|||
} |
|||
value: modified, |
|||
title: _locale.notice.moveUpDown |
|||
} ); |
|||
Input.$input.data( 'old', obj.fullText ); |
|||
var timer; |
|||
// Wikify the data (two times for link text transformations) |
|||
Input.$input.on( 'blur', function() { |
|||
var value = this.value.trim(); |
|||
Wikify( Input.$input[ 0 ] ); |
|||
clearTimeout( timer ); |
|||
// Render and add an input field |
|||
_ui.fn.checkArticle( value, Field ); |
|||
var Field = new OO.ui.FieldLayout( Input, { |
|||
} ); |
|||
align: 'top', |
|||
Input.$input.on( 'input', function() { |
|||
label: null, |
|||
var value = this.value.trim(); |
|||
content: [ _ui.InputLinks( Input, obj.tmpl.lang, obj.tmpl.origTitle, entity ) ] |
|||
} ); |
|||
Field.setErrors( [] ); |
|||
// Check if article exists in the project |
|||
clearTimeout( timer ); |
|||
if ( isTemplate && obj.lang ) { |
|||
timer = setTimeout( () => { |
|||
_ui.fn.checkArticle( modified, Field ); |
|||
} |
|||
var timer; |
|||
Input.$input.on( 'blur', function() { |
|||
var value = this.value.trim(); |
|||
clearTimeout( timer ); |
|||
_ui.fn.checkArticle( value, Field ); |
_ui.fn.checkArticle( value, Field ); |
||
} ); |
}, 500 ); |
||
} ); |
|||
Input.$input.on( 'input', function() { |
|||
var value = this.value.trim(); |
|||
// Keyboard shortcuts |
|||
Field.setErrors( [] ); |
|||
Input.$input.on( 'keydown', function( e ) { |
|||
clearTimeout( timer ); |
|||
_ui.fn.onInputKeyDown( e ); |
|||
timer = setTimeout( function() { |
|||
_ui.fn.checkArticle( value, Field ); |
|||
}, 500 ); |
|||
} ); |
|||
// Return to initial value on Esc |
|||
// Keyboard shortcuts |
|||
if ( e.keyCode === 27 ) { |
|||
e.preventDefault(); |
|||
this.value = this.defaultValue; |
|||
_ui.fn.checkArticle( this.value, Field ); |
|||
} |
|||
} ); |
|||
// Move cursor if value starts with our template or a link |
|||
Input.$input.on( 'focus', function() { |
|||
if ( e.keyCode === 27 ) { |
|||
var value = this.value.trim(); |
|||
e.preventDefault(); |
|||
var start = -1; |
|||
this.value = this.defaultValue; |
|||
var end; |
|||
_ui.fn.checkArticle( this.value, Field ); |
|||
if ( value.startsWith( _tmplStart ) ) { |
|||
} |
|||
start = _tmplStart.length - 1; |
|||
} ); |
|||
} |
|||
// |
// Select link text if it’s a link |
||
var endIndex = value.indexOf( _linkEnd ); |
|||
Input.$input.on( 'focus', function() { |
|||
if ( endIndex !== -1 ) { |
|||
var value = this.value.trim(); |
|||
var |
var pipeIndex = value.indexOf( _linkPipe ); |
||
start = pipeIndex === -1 ? endIndex : pipeIndex + 1; |
|||
var end; |
|||
end = endIndex; |
|||
if ( value.startsWith( _tmplStart ) ) { |
|||
} |
|||
start = _tmplStart.length - 1; |
|||
} |
|||
if ( start !== -1 ) { |
|||
this.setSelectionRange( start, end || start ); |
|||
var endIndex = value.indexOf( _linkEnd ); |
|||
} |
|||
if ( endIndex !== -1 ) { |
|||
} ); |
|||
var pipeIndex = value.indexOf( _linkPipe ); |
|||
start = pipeIndex === -1 ? endIndex : pipeIndex + 1; |
|||
end = endIndex; |
|||
} |
|||
return Field; |
|||
if ( start !== -1 ) { |
|||
this.setSelectionRange( start, end || start ); |
|||
} |
|||
} ); |
|||
return $.Deferred().resolve( Field.$element ); |
|||
} |
|||
// Failure |
|||
function resolvedWithFailure() { |
|||
IWRM.data.changeCount--; |
|||
// Add an error message |
|||
return $.Deferred().resolve( '<div class="iwrm-error">$1</div>'.replace( |
|||
'$1', |
|||
_locale.errors.badInterwiki.replace( '$1', obj.tmpl.lang ) |
|||
) ); |
|||
} |
|||
// Normalise language prefix for Wikidata |
|||
var wdLang = obj.tmpl.lang.split( '-' ).join( '_' ); |
|||
if ( wdLang === 'be_tarask' ) { |
|||
wdLang = 'be_x_old'; |
|||
} |
|||
// Return a promise |
|||
return IWRM.api.checkSitelinks( |
|||
wdLang, |
|||
obj.tmpl.origTitle |
|||
).then( resolvedWithSuccess, resolvedWithFailure ); |
|||
} |
} |
||
// Message with input links |
// Message with input links |
||
_ui.InputLinks = |
_ui.InputLinks = ( $Input, lang, title, entity ) => { |
||
var $LinksList = $( '<ul>' ); |
var $LinksList = $( '<ul>' ); |
||
Строка 1117: | Строка 1116: | ||
// Add a link to local page if it is available |
// Add a link to local page if it is available |
||
var thiswiki = |
var thiswiki = OO.getProp( entity, 'sitelinks', _c.wgDBname ); |
||
if ( thiswiki ) { |
if ( thiswiki ) { |
||
var $PageLink = $( '<a>' ) |
var $PageLink = $( '<a>' ) |
||
Строка 1155: | Строка 1154: | ||
if ( [ 'Enter', 'Space' ].includes( e.code ) ) { |
if ( [ 'Enter', 'Space' ].includes( e.code ) ) { |
||
e.preventDefault(); |
e.preventDefault(); |
||
this.click(); |
|||
} |
} |
||
} ) |
} ) |
||
Строка 1165: | Строка 1164: | ||
// Load more button |
// Load more button |
||
_ui.LoadMore = |
_ui.LoadMore = () => { |
||
var Btn = new OO.ui.ButtonInputWidget( { |
var Btn = new OO.ui.ButtonInputWidget( { |
||
flags: [ 'primary', 'progressive' ], |
flags: [ 'primary', 'progressive' ], |
||
Строка 1179: | Строка 1178: | ||
// Nearby page button (previous / next) |
// Nearby page button (previous / next) |
||
_ui.NearbyPage = |
_ui.NearbyPage = ( type ) => { |
||
var Btn = new OO.ui.ButtonInputWidget( { |
var Btn = new OO.ui.ButtonInputWidget( { |
||
disabled: true, |
disabled: true, |
||
Строка 1201: | Строка 1200: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Dropdown, |
IWRM.ui.Dropdown, |
||
( El ) => { |
|||
El.setValue( getPageTitle( index ) ); |
|||
} |
} |
||
); |
); |
||
Строка 1214: | Строка 1213: | ||
// Page links (tabs and edit section) |
// Page links (tabs and edit section) |
||
_ui.PageLinks = |
_ui.PageLinks = ( title ) => { |
||
var $Element = $( '.mw-editsection' ); |
var $Element = $( '.mw-editsection' ); |
||
var accessKeyHtml = ( _isSiteWide ? '' : ' accesskey="c"'); |
var accessKeyHtml = ( _isSiteWide ? '' : ' accesskey="c"'); |
||
Строка 1296: | Строка 1295: | ||
// Random page button |
// Random page button |
||
_ui.RandomPage = |
_ui.RandomPage = () => { |
||
var Btn = new OO.ui.ButtonInputWidget( { |
var Btn = new OO.ui.ButtonInputWidget( { |
||
disabled: true, |
disabled: true, |
||
Строка 1309: | Строка 1308: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Dropdown, |
IWRM.ui.Dropdown, |
||
( El ) => { |
|||
El.setValue( getPageTitle( rand ) ); |
|||
} |
} |
||
); |
); |
||
Строка 1322: | Строка 1321: | ||
// SiteSub |
// SiteSub |
||
_ui.SiteSub = |
_ui.SiteSub = ( count ) => { |
||
if ( typeof count === 'undefined' ) { |
if ( typeof count === 'undefined' ) { |
||
count = IWRM.data.changeCount; |
count = IWRM.data.changeCount; |
||
Строка 1359: | Строка 1358: | ||
// Skip a page |
// Skip a page |
||
_ui.SkipPage = |
_ui.SkipPage = () => { |
||
var Btn = new OO.ui.ButtonInputWidget( { |
var Btn = new OO.ui.ButtonInputWidget( { |
||
disabled: true, |
disabled: true, |
||
Строка 1370: | Строка 1369: | ||
// Set event listeners |
// Set event listeners |
||
Btn.$button.on( 'click', |
Btn.$button.on( 'click', () => { |
||
_ui.fn.removeArticle( IWRM.data.title ); |
_ui.fn.removeArticle( IWRM.data.title ); |
||
} ); |
} ); |
||
Строка 1378: | Строка 1377: | ||
// Submit changes |
// Submit changes |
||
_ui.Submit = |
_ui.Submit = () => { |
||
var Btn = new OO.ui.ButtonInputWidget( { |
var Btn = new OO.ui.ButtonInputWidget( { |
||
disabled: true, |
disabled: true, |
||
Строка 1408: | Строка 1407: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Summary, |
IWRM.ui.Summary, |
||
( El ) => { |
|||
summary = |
summary = El.getValue().trim(); |
||
} |
} |
||
); |
); |
||
Строка 1428: | Строка 1427: | ||
// A paragraph editing toggle |
// A paragraph editing toggle |
||
_ui.Toggle = |
_ui.Toggle = ( Textarea, $FauxLine ) => { |
||
var Toggle = new OO.ui.ToggleSwitchWidget(); |
var Toggle = new OO.ui.ToggleSwitchWidget(); |
||
Строка 1437: | Строка 1436: | ||
var textareaValue = Textarea.$input.data( 'old' ); |
var textareaValue = Textarea.$input.data( 'old' ); |
||
$FauxLine.find( '.iwrm-line input' ).each( |
$FauxLine.find( '.iwrm-line input' ).each( ( index, el ) => { |
||
$( el ).attr( 'disabled', value ); |
$( el ).attr( 'disabled', value ); |
||
if ( value === true ) { |
if ( value === true ) { |
||
Строка 1464: | Строка 1463: | ||
// Upper toolbar |
// Upper toolbar |
||
_ui.Toolbar = |
_ui.Toolbar = () => { |
||
// Should be loaded first and foremost |
// Should be loaded first and foremost |
||
IWRM.ui.Dropdown = _ui.Dropdown(); |
IWRM.ui.Dropdown = _ui.Dropdown(); |
||
Строка 1491: | Строка 1490: | ||
// Check if article exists |
// Check if article exists |
||
_ui.fn.checkArticle = |
_ui.fn.checkArticle = ( value, Field ) => { |
||
var match = _regularTmpl.exec( value ); |
var match = _regularTmpl.exec( value ); |
||
var title = match && match[ 1 ]; |
var title = match && match[ 1 ]; |
||
Строка 1498: | Строка 1497: | ||
function resolvedWithSuccess( data ) { |
function resolvedWithSuccess( data ) { |
||
var entity; |
var entity; |
||
var isMissing = |
var isMissing = OO.getProp( data, 'entities', '-1' ); |
||
if ( !isMissing ) { |
if ( !isMissing ) { |
||
Строка 1514: | Строка 1513: | ||
if ( title ) { |
if ( title ) { |
||
// Normalise language prefix for Wikidata |
|||
var wdLang = _c.wgContentLanguage.split( '-' ).join( '_' ); |
|||
if ( wdLang === 'be_tarask' ) { |
|||
wdLang = 'be_x_old'; |
|||
} |
|||
IWRM.api.checkSitelinks( |
IWRM.api.checkSitelinks( |
||
_c.wgContentLanguage, |
|||
wdLang, |
|||
title |
title |
||
).done( resolvedWithSuccess ); |
).done( resolvedWithSuccess ); |
||
Строка 1529: | Строка 1522: | ||
// Check nearby pages on validity |
// Check nearby pages on validity |
||
_ui.fn.checkBtns = |
_ui.fn.checkBtns = () => { |
||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.PreviousPage, |
IWRM.ui.PreviousPage, |
||
( El ) => { |
|||
El.setDisabled( IWRM.data.index === 0 ); |
|||
} |
} |
||
); |
); |
||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.NextPage, |
IWRM.ui.NextPage, |
||
( El ) => { |
|||
El.setDisabled( IWRM.data.index === IWRM.list.length - 1 ); |
|||
} |
} |
||
); |
); |
||
Строка 1546: | Строка 1539: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.RandomPage, |
IWRM.ui.RandomPage, |
||
( El ) => { |
|||
El.setDisabled( IWRM.list.length === 1 ); |
|||
} |
} |
||
); |
); |
||
Строка 1553: | Строка 1546: | ||
// Enable and disable action buttons |
// Enable and disable action buttons |
||
_ui.fn.disableActions = |
_ui.fn.disableActions = ( value, submitValue ) => { |
||
// Disable according to value |
// Disable according to value |
||
const toggleAction = ( El ) => { |
|||
El.setDisabled( value ); |
|||
} |
} |
||
Строка 1585: | Строка 1578: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Submit, |
IWRM.ui.Submit, |
||
( El ) => { |
|||
var val = typeof submitValue === 'undefined' ? value : submitValue; |
var val = typeof submitValue === 'undefined' ? value : submitValue; |
||
El.setDisabled( val ); |
|||
} |
} |
||
); |
); |
||
Строка 1593: | Строка 1586: | ||
// Get more data |
// Get more data |
||
_ui.fn.getMoreData = |
_ui.fn.getMoreData = () => { |
||
// Disable Load more button |
// Disable Load more button |
||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.LoadMore, |
IWRM.ui.LoadMore, |
||
( El ) => { |
|||
El.setDisabled( true ); |
|||
} |
} |
||
); |
); |
||
IWRM.api.getSearchData( |
IWRM.api.getSearchData().then( () => { |
||
IWRM.offset += IWRM.prefs.limit; |
|||
// Update data in dropdown |
// Update data in dropdown |
||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Dropdown, |
IWRM.ui.Dropdown, |
||
( El ) => { |
|||
El.setOptions( IWRM.list ); |
|||
} |
} |
||
); |
); |
||
Строка 1616: | Строка 1607: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.LoadMore, |
IWRM.ui.LoadMore, |
||
( El ) => { |
|||
El.setDisabled( false ); |
|||
} |
} |
||
); |
); |
||
Строка 1627: | Строка 1618: | ||
// Modify a changed line |
// Modify a changed line |
||
_ui.fn.modifyLine = |
_ui.fn.modifyLine = ( index, El, length, $Container, $Bar ) => { |
||
var $ |
var $El = $( El ); |
||
var $Parent = $El.parent(); |
|||
// Find a future container for inputs |
// Find a future container for inputs |
||
Строка 1651: | Строка 1643: | ||
// Render input fields for each change |
// Render input fields for each change |
||
var text = $ |
var text = $El.text(); |
||
var InputFields = []; |
var InputFields = []; |
||
replaceLinks( text, |
replaceLinks( text, ( obj, text ) => { |
||
var index = text.indexOf( obj.fullText ); |
var index = text.indexOf( obj.fullText ); |
||
InputFields[ index ] = _ui. |
InputFields[ index ] = _ui.fn.renderInput( obj ); |
||
IWRM.data.changeCount++; |
IWRM.data.changeCount++; |
||
Строка 1664: | Строка 1656: | ||
// Append them all at once synchronously |
// Append them all at once synchronously |
||
var promises = Promise.all( InputFields ).then( |
var promises = Promise.all( InputFields ).then( ( Element ) => { |
||
$InputHolder.append( Element ); |
$InputHolder.append( Element ); |
||
} ).then( |
} ).then( () => { |
||
// Remove progress bar and show the diff |
// Remove progress bar and show the diff |
||
if ( index === length - 1 ) { |
if ( index === length - 1 ) { |
||
Строка 1705: | Строка 1697: | ||
return promises; |
return promises; |
||
} |
|||
// Render an input |
|||
_ui.fn.renderInput = ( obj ) => { |
|||
var modified = getTemplate( obj ); |
|||
// Success |
|||
function resolvedWithSuccess( data, isTemplate ) { |
|||
var entity; |
|||
var isMissing = OO.getProp( data, 'entities', '-1' ); |
|||
// For returning a link to the same language |
|||
var title = obj.tmpl.origTitle; |
|||
if ( !isMissing ) { |
|||
entity = OO.getProp( data, 'entities' ); |
|||
if ( entity ) { |
|||
var keys = Object.keys( entity ); |
|||
entity = entity[ keys[ 0 ] ]; |
|||
} |
|||
// Change the output if a local page is available |
|||
var data_sl = OO.getProp( entity, 'sitelinks', _c.wgDBname ); |
|||
if ( data_sl ) { |
|||
title = data_sl.title; |
|||
isTemplate = false; |
|||
} |
|||
} |
|||
if ( isTemplate === false ) { |
|||
modified = getLink( obj, title ); |
|||
} |
|||
var InputField = _ui.InputField( obj, modified, entity, isTemplate ); |
|||
return $.Deferred().resolve( InputField.$element ); |
|||
} |
|||
// Failure |
|||
function resolvedWithFailure() { |
|||
IWRM.data.changeCount--; |
|||
// Add an error message |
|||
return $.Deferred().resolve( '<div class="iwrm-error">$1</div>'.replace( |
|||
'$1', |
|||
_locale.errors.badInterwiki.replace( '$1', obj.tmpl.lang ) |
|||
) ); |
|||
} |
|||
// Render a link on links from the same wiki |
|||
if ( obj.tmpl.lang === _c.wgContentLanguage ) { |
|||
const fakeData = { entities: { '-1': { data: 'fake' } } }; |
|||
return resolvedWithSuccess( fakeData, false ); |
|||
} |
|||
// Return a promise |
|||
return IWRM.api.checkSitelinks( |
|||
obj.tmpl.lang, |
|||
obj.tmpl.origTitle |
|||
).then( resolvedWithSuccess, resolvedWithFailure ); |
|||
} |
} |
||
Строка 1714: | Строка 1766: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Submit, |
IWRM.ui.Submit, |
||
( El ) => { |
|||
El.$button.click(); |
|||
_notification = _ui.fn.notify( null, null, { |
_notification = _ui.fn.notify( null, null, { |
||
text: _locale.errors.saving.replace( '$1', IWRM.data.title ), |
text: _locale.errors.saving.replace( '$1', IWRM.data.title ), |
||
Строка 1765: | Строка 1817: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
inputs[ nextIndex ], |
inputs[ nextIndex ], |
||
( El ) => { |
|||
El.focus(); |
|||
}, |
}, |
||
notifyIfNoNextInput |
notifyIfNoNextInput |
||
Строка 1774: | Строка 1826: | ||
// Parse changes in the diff view |
// Parse changes in the diff view |
||
_ui.fn.parseChanges = |
_ui.fn.parseChanges = ( content ) => { |
||
var result = content; |
var result = content; |
||
var hasErrors = false; |
var hasErrors = false; |
||
Строка 1781: | Строка 1833: | ||
+ '.iwrm-line input:not(:disabled)' |
+ '.iwrm-line input:not(:disabled)' |
||
); |
); |
||
$Changes.each( |
$Changes.each( ( index, el ) => { |
||
var old = $( el ).data( 'old' ); |
var old = $( el ).data( 'old' ); |
||
var value = $( el ).val(); |
var value = $( el ).val(); |
||
Строка 1819: | Строка 1871: | ||
// Remove an article from the lists |
// Remove an article from the lists |
||
_ui.fn.removeArticle = |
_ui.fn.removeArticle = ( title ) => { |
||
// Calculate the index and update the lists |
// Calculate the index and update the lists |
||
var index = getPageIndex( title ); |
var index = getPageIndex( title ); |
||
Строка 1832: | Строка 1884: | ||
ifUIElementsExist( |
ifUIElementsExist( |
||
IWRM.ui.Dropdown, |
IWRM.ui.Dropdown, |
||
( El ) => { |
|||
El.setOptionsData( IWRM.list ); |
|||
El.setValue( getPageTitle( newIndex ) ); |
|||
IWRM.data.index = newIndex; |
IWRM.data.index = newIndex; |
||
} |
} |
||
Строка 1841: | Строка 1893: | ||
// Render a diff |
// Render a diff |
||
_ui.fn.renderDiff = |
_ui.fn.renderDiff = ( title ) => { |
||
var $Container = $( '.iwrm-diff' ); |
var $Container = $( '.iwrm-diff' ); |
||
var $DiffBar = new OO.ui.ProgressBarWidget( { |
var $DiffBar = new OO.ui.ProgressBarWidget( { |
||
Строка 1851: | Строка 1903: | ||
$Container.html( $DiffBar.$element ); |
$Container.html( $DiffBar.$element ); |
||
return IWRM.api.getFullText( title ).then( |
return IWRM.api.getFullText( title ).then( () => { |
||
return IWRM.api.loadDiff( title ).then( |
return IWRM.api.loadDiff( title ).then( ( data ) => { |
||
// Insert the diff or the lack of it |
// Insert the diff or the lack of it |
||
var DiffResult = data |
var DiffResult = OO.getProp( data, 'compare', 'body' ); |
||
var Diff = _ui.Diff( DiffResult ); |
var Diff = _ui.Diff( DiffResult ); |
||
$Container.append( Diff ); |
$Container.append( Diff ); |
||
Строка 1870: | Строка 1922: | ||
IWRM.data.modifiedText = ''; |
IWRM.data.modifiedText = ''; |
||
} ); |
} ); |
||
} ).catch( |
} ).catch( () => { |
||
// Remove progress bar and show the message |
// Remove progress bar and show the message |
||
$DiffBar.$element.remove(); |
$DiffBar.$element.remove(); |
||
Строка 1878: | Строка 1930: | ||
// Show a notification |
// Show a notification |
||
_ui.fn.notify = |
_ui.fn.notify = ( error, data, options ) => { |
||
var text = options.text; |
var text = options ? options.text : ''; |
||
var replace = options.replace; |
var replace = options ? options.replace : ''; |
||
// Add a default text |
// Add a default text |
||
Строка 1908: | Строка 1960: | ||
autoHide: ( error ? false : true ), |
autoHide: ( error ? false : true ), |
||
title: ( error ? _locale.errors.title : '' ), |
title: ( error ? _locale.errors.title : '' ), |
||
tag: options.tag |
tag: ( options && options.tag ? options.tag : null ) |
||
} ); |
} ); |
||
} |
} |
||
Строка 1915: | Строка 1967: | ||
* Initialising a portlet |
* Initialising a portlet |
||
*/ |
*/ |
||
IWRM.InitPortlet = |
IWRM.InitPortlet = () => { |
||
var namespaces = [ |
|||
if ( _c.wgNamespaceNumber !== 0 && _c.wgTitle !== 'Песочница' || mw.config.get( 'wgIsMainPage' ) ) { |
|||
0, |
|||
14, // Category |
|||
] |
|||
if ( !namespaces.includes( _c.wgNamespaceNumber ) && _c.wgTitle !== 'Песочница' || mw.config.get( 'wgIsMainPage' ) ) { |
|||
return; |
return; |
||
} |
} |
||
Строка 1924: | Строка 1980: | ||
} |
} |
||
window.addEventListener( 'hashchange', |
window.addEventListener( 'hashchange', () => { |
||
IWRM.Init(); |
IWRM.Init(); |
||
} ); |
} ); |
||
var $iwLinks = $( '.mw-parser-output a.extiw' ); |
var $iwLinks = $( '.mw-parser-output a.extiw' ); |
||
if ( $iwLinks.length === 0 ) { |
if ( _c.wgNamespaceNumber === 0 && $iwLinks.length === 0 ) { |
||
return; |
return; |
||
} |
} |
||
mw.loader.using( 'mediawiki.util', |
mw.loader.using( 'mediawiki.util', () => { |
||
var portlet = mw.util.addPortletLink( |
var portlet = mw.util.addPortletLink( |
||
'p-tb', |
'p-tb', |
||
Строка 1947: | Строка 2003: | ||
* Initialising |
* Initialising |
||
*/ |
*/ |
||
IWRM.Init = |
IWRM.Init = ( customList ) => { |
||
// Check hash and user groups |
// Check hash and user groups |
||
var from = window.location.hash; |
var from = window.location.hash; |
||
Строка 1975: | Строка 2031: | ||
_ContentText.empty(); |
_ContentText.empty(); |
||
_Indicators.empty(); |
_Indicators.empty(); |
||
$( '.iwrm' ).removeClass( IWRM.prefs.loaded ); |
|||
mw.loader.using( _deps ).done( |
mw.loader.using( _deps ).done( () => { |
||
_api = new mw.Api(); |
_api = new mw.Api(); |
||
_wikidataApi = new mw.ForeignApi( 'https://www.wikidata.org/ruwiki/w/api.php' ); |
_wikidataApi = new mw.ForeignApi( 'https://www.wikidata.org/ruwiki/w/api.php' ); |
||
Строка 1991: | Строка 2048: | ||
} ); |
} ); |
||
IWRM.api.getSearchData().then( |
IWRM.api.getSearchData( customList ).then( () => { |
||
// Render toolbar once |
// Render toolbar once |
||
_ContentText.append( _ui.Toolbar().$element ); |
_ContentText.append( _ui.Toolbar().$element ); |
||
Строка 2001: | Строка 2058: | ||
_ContentText.append( _ui.Footer().$element ); |
_ContentText.append( _ui.Footer().$element ); |
||
// Remove progress bar and show the page |
|||
// Actually render diff |
|||
_ui.fn.renderDiff( IWRM.data.title ).then( |
_ui.fn.renderDiff( IWRM.data.title ).then( () => { |
||
// Remove progress bar and show the page |
|||
$ContentBar.$element.remove(); |
$ContentBar.$element.remove(); |
||
_ContentText.addClass( IWRM.prefs.loaded ); |
_ContentText.addClass( IWRM.prefs.loaded ); |
||
Строка 2010: | Строка 2066: | ||
// Confirm before leaving with a hash |
// Confirm before leaving with a hash |
||
mw.confirmCloseWindow( { |
mw.confirmCloseWindow( { |
||
test: |
test: () => { |
||
return window.location.hash.startsWith( IWRM.prefs.hash ); |
return window.location.hash.startsWith( IWRM.prefs.hash ); |
||
} |
} |
||
Строка 2016: | Строка 2072: | ||
// Remove notifications on saving |
// Remove notifications on saving |
||
mw.hook( 'postEdit' ).add( |
mw.hook( 'postEdit' ).add( () => { |
||
if ( _notification ) { |
if ( _notification ) { |
||
_notification = _notification.close(); |
_notification = _notification.close(); |
||
Строка 2022: | Строка 2078: | ||
} ); |
} ); |
||
} ); |
} ); |
||
} ).fail( |
} ).fail( ( error, data ) => { |
||
_ui.fn.notify( error, data, { replace: _locale.requests.init } ); |
_ui.fn.notify( error, data, { replace: _locale.requests.init } ); |
||
} ); |
} ); |
Текущая версия от 16:31, 11 ноября 2024
/* <nowiki>
* IWRM.js
* See [[Проект:Check Wikipedia/Замена прямых интервики-ссылок]]
* Local and global variables are lowerCamelCase
* Selectors and DOM nodes are CamelCase
* Local variables start with _
*/
( function() {
if ( !window.IWRM ) window.IWRM = {};
/*
* Global settings and variables
*/
IWRM.prefs = {
name: 'IWRM',
tag: 'iwrm',
hash: '#/iwrm/',
loaded: 'is-loaded',
limit: 50,
summary: '[[ПРО:CW|CheckWiki:]] замена прямых интервики-ссылок'
}
// API requests
IWRM.api = {};
// Current article data
IWRM.data = {
index: 0,
title: '',
text: '',
modifiedText: '',
changeCount: 0
}
// Current article list
IWRM.list = [];
// Current search offset
IWRM.offset = 0;
// Storage for referenced UI elements
IWRM.ui = {};
/*
* Local settings and variables
*/
var _c = mw.config.get( [
'wgAction',
'wgCanonicalSpecialPageName',
'wgCommentCodePointLimit',
'wgContentLanguage',
'wgDBname',
'wgNamespaceNumber',
'wgPageName',
'wgTitle',
'wgUserGroups',
'wgUserName',
] );
// Special:BlankPage, category or individual page
var _isSiteWide = true;
var _isCategory = _c.wgNamespaceNumber === 14;
if ( _c.wgCanonicalSpecialPageName !== 'Blankpage' ) {
_isSiteWide = false;
}
// Is user allowed to make edits
var _isAllowed = (
_c.wgUserName &&
(
_c.wgUserGroups.includes( 'bot' ) ||
_c.wgUserGroups.includes( 'autoreview' ) ||
_c.wgUserGroups.includes( 'editor' ) ||
_c.wgUserGroups.includes( 'sysop' )
)
);
// API handler container
var _api = null;
// Wikidata API handler container
var _wikidataApi = null;
// Counter for successful savings
var _counter = 0;
// Active notification container for closing
var _notification;
// Dependencies
var _deps = [
'ext.gadget.wikificator',
'mediawiki.action.view.postEdit',
'mediawiki.api',
'mediawiki.confirmCloseWindow',
'mediawiki.ForeignApi',
'mediawiki.diff.styles',
'mediawiki.notification',
'mediawiki.util',
'mediawiki.widgets.visibleLengthLimit',
'oojs',
'oojs-ui'
];
// Normalised prefixes
var _normalisedPrefixes = {
'w': 'en',
'be-x-old': 'be-tarask',
'cz': 'cs',
'jp': 'ja',
'nan': 'zh-min-nan',
'nb': 'no',
'yue': 'zh-yue',
'zh-tw': 'zh'
};
var _normalisedPrefixKeys = Object.keys( _normalisedPrefixes );
// Skipped interwiki links
var _otherProjects = [
'b', 'wikibooks',
'c', 'commons',
'd', 'wikidata',
'm', 'meta',
'mw',
'n', 'wikinews',
'q', 'wikiquote',
's', 'wikisource',
'species', 'wikispecies',
'v', 'wikiversity',
'voy', 'wikivoyage',
'wikt', 'wiktionary',
'wmf', 'wikimedia',
'category', 'file', 'image', 'media', 'wikipedia',
'betawiki', 'doi', 'translatewiki'
];
// Expression for interwiki links: [[$1:$2]]
var _regularExp = /\[{2}:? *([a-z-]+) *: *([^\[\]\|\n]+)(?:\|([^\|\n]*?))?\]{2}/gi;
// Expression for interwiki links in language: [[$1]] ({{lang-$2|$3}}$4
var _regularExpLangs = /\[{2}([^\[\]\n]+?)\]{2} \(\{\{[Ll]ang-([a-zA-Z-]+)\|(\[{2}:?[^\]]*?\]{2})\}\}(\)*)/g;
// Expression for prefixes: [[:$1:]]
var _regularPrefix = / *:? *([a-z-]+) *: */gi;
// Expression for templates: {{iw}}, {{нп5}}, {{не переведено 5}}
var _regularTmpl = /\{\{(?:subst:|подст:|safesubst:)?(?:iw|нп\d+|не переведено \d+)\|(.*?)[\|\}]/gi;
// Expression for bad text
var _regularBadText = /[\[\]\{\}<>]/;
// Search auto update limit
var _searchAutoUpdate = 10;
// Search API high limit
var _searchHighLimit = 500;
// Search request for interwiki links
var _searchRequest = 'insource:/\\[{2}:[a-z-]{2,}:/';
// Default link without/with {{lang}} syntax
var _link = '[[$1$2]]';
var _linkIsLang = '[[$1$2]] ({{lang-$3|$4}}$5';
var _linkTextOnly = '$2';
// Default template without/with {{lang}} syntax
var _tmpl = '{{iw||$3|$1|$2}}';
var _tmplIsLang = '{{iw|$4|$3|$1|$2}} ({{lang-$5|$6}}$7';
// Default link and template syntax
var _linkPipe = '|';
var _linkEnd = ']]';
var _tmplStart = '{{iw||';
// Talk namespace prefix
var _talkNs = 'Обсуждение:';
// UI functions
var _ui = {};
_ui.fn = {};
IWRM._ui = _ui;
// Most used selectors
var _ContentText = $( '#mw-content-text' );
var _FirstHeading = $( '#firstHeading' );
var _SiteSub = $( '#siteSub' );
var _ContentSub = $( '#contentSub' );
var _Indicators = $( '.mw-indicators' );
// Locale
var _locale = {
title: 'Замена $1прямых интервики-ссылок',
titleOne: 'Замена $1прямой интервики-ссылки',
errors: {
title: 'Скрипт мог перестать работать',
text: 'Произошла ошибка. ',
unidentified: 'Запрос $1 не был выполнен по неизвестной причине.',
notAllowed: 'Чтобы не распатрулировать статьи, скрипт можно использовать только автопатрулируемым, патрулирующим и администраторам.',
noArticle: 'Страница «$1» не найдена. Вероятно, до неё добрались удалисты, переименисты или коммунисты.',
noLinks: 'Не обнаружено прямых интервики-ссылок. Пропустите страницу.',
unchangedCode: 'Вы не заполнили первый параметр в одном из шаблонов или не заполнили текст ссылки. Изменения не были записаны, проверьте все поля ввода.',
saving: 'Страница «$1» сохраняется…',
altOnlyLink: 'Это единственная ссылка в данной статье — к другим перейти невозможно.',
altNoNext: 'Это последняя ссылка в данной статье — можно перейти только к предыдущей.',
noNetwork: 'Запрос $1 не дошёл из-за неполадок с сетью. Проверьте подключение к Интернету.',
noText: 'В запросе $1 не был передан итоговый текст.',
noTitle: 'В запросе $1 не был передан заголовок необходимой статьи.',
articleExists: '$1: $2страница уже существует$3.',
badInterwiki: '<strong>API Викиданных не может найти раздел Википедии ([[$1:…]]) для одной из ссылок.</strong> Она не будет отредактирована.'
},
requests: {
init: 'по загрузке модулей',
getFullText: 'для получения текста статьи',
getRateLimits: 'для выяснения максимального количества доступных вам данных',
getSearchData: 'для получения списка статей для обработки',
loadDiff: 'для получения разницы версий',
submitChanges: 'для записи изменений'
},
loading: 'Поиск в $1 займёт некоторое время, но это того стоит.',
loadingTitle: 'Загружается…',
loadMore: 'Загрузить ещё',
nearbyPage: {
previous: 'Предыдущая',
next: 'Следующая'
},
randomPage: 'Случайная статья',
notice: {
moveUpDown: 'Перемещаться по полям можно через [Alt+J] (предыдущее) / [Alt+K] (следующее)',
editSection: 'Править статью вручную (откроется в новой вкладке)',
viewArticle: 'Перейти в статью (откроется в новой вкладке)',
viewTalk: 'Перейти в обсуждение (откроется в новой вкладке)',
interwiki: 'Интервики-ссылка (откроется в новой вкладке)',
thiswiki: 'Статья в этом разделе (откроется в новой вкладке)',
wikidata: 'Элемент Викиданных (откроется в новой вкладке)',
thiswikiArticle: 'есть статья',
clearInput: 'очистить текст',
clearInputRestore: 'вернуть очищенное',
},
editParagraph: 'Править весь абзац',
editSection: 'править вручную',
viewArticle: 'просмотр статьи',
mainTab: 'Статья',
talkTab: 'Обсуждение',
summary: 'Описание изменений',
warrant: 'Вы сами отвечаете за правки, сделанные с помощью скрипта. <br><small>Удалите всё содержимое нужного поля ввода, чтобы не обрабатывать связанную с ним ссылку.</small>',
submit: 'Записать страницу',
clearAll: 'Очистить текст везде',
clearAllRestore: 'Вернуть очищенное везде',
skipPage: 'Пропустить страницу'
}
/*
* API requests
*/
// Check sitelinks
IWRM.api.checkSitelinks = ( lang, title ) => {
function getDBname( lang ) {
if ( lang === 'be-tarask' ) {
return 'be_x_old' + 'wiki';
}
return lang.split( '-' ).join( '_' ) + 'wiki';
}
return _wikidataApi.get( {
action: 'wbgetentities',
sites: getDBname( lang ),
sitefilter: _c.wgDBname,
titles: title,
props: 'sitelinks',
normalize: true,
formatversion: 2
} );
}
// Get full article text
IWRM.api.getFullText = ( title ) => {
if ( !title ) {
_ui.fn.notify( 'script', null, {
replace: _locale.requests.getFullText,
text: _locale.errors.noTitle
} );
return false;
}
// Success
function resolvedWithSuccess( data ) {
var revision = OO.getProp( data, 'query', 'pages', 0, 'revisions', 0 );
if ( revision ) {
IWRM.data.text = revision.content;
// Modify the text prematurely to show the changes
IWRM.data.modifiedText = replaceLinks( revision.content, ( obj, text ) => {
var modified = getTemplate( obj );
return text.replace( obj.fullText, modified );
} );
return;
}
var isMissing = OO.getProp( data, 'query', 'pages', 0, 'missing' );
if ( isMissing ) {
_ui.fn.notify( 'missingtitle', null, { replace: title } );
}
return;
}
// Failure
function resolvedWithFailure( error, data ) {
_ui.fn.notify( error, data, { replace: _locale.requests.getFullText } );
}
// Return a promise
return _api.get( {
action: 'query',
titles: title,
prop: 'revisions',
rvprop: 'content',
formatversion: 2
} ).done( resolvedWithSuccess ).fail( resolvedWithFailure );
}
// Send a request to CheckWiki server
IWRM.api.submitCheckWiki = ( title ) => {
return $.get( 'https://checkwiki.toolforge.org/cgi-bin/checkwiki.cgi', {
project: _c.wgDBname,
view: 'detail',
id: 68,
title: title
} );
}
// Get rate limits for the account
IWRM.api.getRateLimits = () => {
// Success
function resolvedWithSuccess( data ) {
var data_rl = OO.getProp( data, 'query', 'userinfo', 'ratelimits' );
if ( data_rl ) {
var limitsList = Object.keys( data_rl );
if ( limitsList.length === 0 ) {
IWRM.prefs.limit = _searchHighLimit;
return IWRM.prefs.limit;
}
}
return IWRM.prefs.limit;
}
// Failure
function resolvedWithFailure( error, data ) {
_ui.fn.notify( error, data, { replace: _locale.requests.getRateLimits } );
return IWRM.prefs.limit;
}
// Return a promise
return _api.get( {
action: 'query',
meta: 'userinfo',
uiprop: 'ratelimits',
formatversion: 2
} ).then( resolvedWithSuccess ).fail( resolvedWithFailure );
}
// Get search request data
IWRM.api.getSearchData = ( customList ) => {
var didJustLoad = IWRM.data.title === '';
var hasCustomList = typeof customList !== 'undefined';
var searchOptions = {
action: 'query',
list: 'search',
srprop: '',
srwhat: 'text',
srsearch: _searchRequest,
srsort: 'last_edit_desc',
sroffset: IWRM.offset,
srlimit: IWRM.prefs.limit,
formatversion: 2
}
// Success
function resolvedWithSuccess( data ) {
var data_sr = OO.getProp( data, 'query', 'search' );
var offset = OO.getProp( data, 'continue', 'sroffset' );
if ( data_sr ) {
if ( offset ) {
IWRM.offset = offset;
}
for ( var i = 0; i < data_sr.length; i++ ) {
var title = data_sr[ i ].title;
IWRM.list.push( {
data: title
} );
}
if ( didJustLoad ) {
IWRM.data.title = data_sr[ 0 ].title;
}
}
}
// Failure
function resolvedWithFailure( error, data ) {
_ui.fn.notify( error, data, { replace: _locale.requests.getSearchData } );
}
// API call or a promise for custom list
function doApiCall( searchOptions ) {
if ( customList ) {
IWRM.list = customList.map( el => {
return { data: el };
} );
IWRM.data.title = customList[ 0 ];
// Do a pseudo-call to something
return mw.loader.using( 'mediawiki.util', () => {
return IWRM.list;
} );
}
return _api.get( searchOptions )
.done( resolvedWithSuccess )
.fail( resolvedWithFailure );
}
// Add different options for non site-wide search
if ( !_isSiteWide ) {
if ( _isCategory ) {
// Request the pages in category
searchOptions.srsearch = `${ searchOptions.srsearch } incategory:"${ _c.wgTitle }"`;
} else if ( didJustLoad ) {
// Request the current page
searchOptions.srsearch = _c.wgPageName;
searchOptions.srwhat = 'nearmatch';
}
}
// Return a promise
if ( didJustLoad ) {
return IWRM.api.getRateLimits().then( ( limit ) => {
searchOptions.srlimit = limit;
return doApiCall( searchOptions );
} );
}
return doApiCall( searchOptions );
}
// Request diff with changes
IWRM.api.loadDiff = ( title ) => {
if ( !title ) {
_ui.fn.notify( 'script', null, {
replace: _locale.requests.loadDiff,
text: _locale.errors.noTitle
} );
return false;
}
// Failure
function resolvedWithFailure( error, data ) {
_ui.fn.notify( error, data, { replace: _locale.requests.loadDiff } );
}
// Return a promise
return _api.post( {
action: 'compare',
fromtitle: title,
totext: IWRM.data.modifiedText,
formatversion: 2
} ).fail( resolvedWithFailure );
}
// Submit changes
IWRM.api.submitChanges = ( title, modified, summary ) => {
if ( !title ) {
_ui.fn.notify( 'script', null, {
replace: _locale.requests.submitChanges,
text: _locale.errors.noTitle,
tag: 'iwrm-submit'
} );
return false;
}
if ( !modified ) {
_ui.fn.notify( 'script', null, {
replace: _locale.requests.submitChanges,
text: _locale.errors.noText,
tag: 'iwrm-submit'
} );
return false;
}
// Ensure that summary is present
if ( !summary ) {
summary = '';
}
// Success
function resolvedWithSuccess( data ) {
// Move counter up
_counter += 1;
// Send a request to CheckWiki server
IWRM.api.submitCheckWiki( data.title || title );
// Remove data for this article
_ui.fn.removeArticle( title );
// Show post-edit confirmation
mw.config.set( {
wgCurRevisionId: data.newrevid,
wgRevisionId: data.newrevid,
} );
mw.hook( 'postEdit' ).fire();
}
// Failure
function resolvedWithFailure( error, data ) {
if ( error !== 'editconflict' ) {
_ui.fn.notify( error, data, {
replace: _locale.requests.submitChanges,
tag: 'iwrm-submit'
} );
return;
}
// Try to submit once more for edit conflicts
return doApiCall();
}
// Generic function for API call
function doApiCall() {
return _api.edit(
title,
( revision ) => {
var text = _ui.fn.parseChanges( revision.content );
if ( text === false ) {
text = revision.content;
}
return {
assertuser: _c.wgUserName,
text: text,
summary: summary,
minor: true,
watchlist: 'nochange',
tags: IWRM.prefs.tag
}
}
).done( resolvedWithSuccess ).fail( resolvedWithFailure );
}
// Return a promise
return doApiCall();
}
/*
* Local functions
*/
// Get modified link text
function getLink( obj, title ) {
var text = obj.tmpl.text;
if ( !text ) {
text = '';
// Use link in text if there is no shown text but titles differ
if ( obj.tmpl.title && obj.tmpl.title.toLowerCase() !== title.toLowerCase() ) {
text = obj.tmpl.title;
}
}
var result = ( obj.lang ? _linkIsLang : _link );
if ( title === IWRM.data.title ) {
result = _linkTextOnly;
if ( !text ) {
text = title;
}
} else {
// Add the missing pipe to text if there is any
if ( text ) {
text = '|' + text;
}
}
result = result.replace( '$1', title ).replace( '$2', text );
if ( obj.lang ) {
// Have original title as text if there is none
var langText = obj.lang.text;
if ( !langText ) {
langText = obj.tmpl.origTitle;
}
result = result.replace(
'$3', obj.lang.lang
).replace(
'$4', langText.split( '_' ).join( ' ' )
).replace(
'$5', obj.lang.after
);
}
return result;
}
// Get modified template text
function getTemplate( obj ) {
var result = ( obj.lang ? _tmplIsLang : _tmpl );
result = result.replace(
'$1', obj.tmpl.lang
).replace(
'$2', obj.tmpl.origTitle.split( '_' ).join( ' ' )
).replace(
'$3', obj.tmpl.text.split( '_' ).join( ' ' )
);
if ( obj.lang ) {
result = result.replace(
'$4', obj.tmpl.title.split( '_' ).join( ' ' )
).replace(
'$5', obj.lang.lang
).replace(
'$6', obj.lang.text.split( '_' ).join( ' ' )
).replace(
'$7', obj.lang.after
);
}
return result;
}
// Get page index from a list, see getSearchData
function getPageIndex( title ) {
return IWRM.list.findIndex( ( page ) => {
return page.data === title;
} );
}
// Get page item from a list, see getSearchData
function getPageTitle( index ) {
return IWRM.list[ index ] ? IWRM.list[ index ].data : null;
}
// Check if OOUI element exists
function ifUIElementsExist( El, does, doesnt ) {
if ( Array.isArray( El ) ) {
El.forEach( ( Element ) => {
ifUIElementsExist( Element, does, doesnt )
} );
return;
}
if ( El ) {
return ( typeof does !== 'undefined' ? does( El ) : true );
}
return ( typeof doesnt !== 'undefined' ? doesnt() : false );
}
// Normalise prefix data
function normalisePrefix( lang, title ) {
var normalPrefix = _normalisedPrefixes[ lang ];
var normalTitle = title;
if ( normalPrefix === 'en' || normalPrefix === _c.wgContentLanguage ) {
var prefixMatch = _regularPrefix.exec( title );
if ( prefixMatch !== null ) {
normalPrefix = prefixMatch[ 1 ].toLowerCase();
normalTitle = title.replace( _regularPrefix, '' );
}
}
return {
lang: normalPrefix,
title: normalTitle
}
}
// Get link data from a link
function getLinkData( match ) {
if ( match === null ) {
return null;
}
var lang = match[ 1 ].toLowerCase();
var title = match[ 2 ];
var text = ( match[ 3 ] ? match[ 3 ] : '' );
// Skip other projects
if ( lang && _otherProjects.includes( lang ) ) {
return null;
}
// Trim data
lang = lang.trim();
title = title.trim();
text = text.trim();
// Normalise prefixes
if ( _normalisedPrefixKeys.includes( lang ) ) {
var normalisedData = normalisePrefix( lang, title );
lang = normalisedData.lang;
title = normalisedData.title;
}
// Remove incorrect text
if ( _regularBadText.exec( text ) ) {
text = '';
}
return {
lang: lang,
title: title,
text: text
}
}
// Replace all links to templates
function replaceLinks( text, callback ) {
if ( !text ) {
return false;
}
// Replace links with {{lang}} template around them
_regularExpLangs.lastIndex = 0;
var match = _regularExpLangs.exec( text );
while ( match !== null ) {
var link = ( match[ 1 ] ? match[ 1 ].split( '|' ) : [ '' ] );
var langLang = match[ 2 ].toLowerCase();
var langText = ( match[ 3 ] ? match[ 3 ] : '' );
var after = ( match[ 4 ] ? match[ 4 ] : '' );
_regularExp.lastIndex = 0;
var textMatch = _regularExp.exec( langText );
var linkData = getLinkData( textMatch );
if ( linkData !== null ) {
text = callback( {
fullText: match[ 0 ],
lang: {
after: after,
lang: langLang,
text: linkData.text
},
tmpl: {
lang: linkData.lang,
origTitle: linkData.title,
title: link[ 0 ],
text: ( link[ 1 ] ? link[ 1 ] : '' )
}
}, text );
}
match = _regularExpLangs.exec( text );
}
// Replace regular links afterwards
_regularExp.lastIndex = 0;
match = _regularExp.exec( text );
while ( match !== null ) {
var linkData = getLinkData( match );
if ( linkData !== null ) {
text = callback( {
fullText: match[ 0 ],
tmpl: {
lang: linkData.lang,
origTitle: linkData.title,
text: linkData.text
}
}, text );
}
match = _regularExp.exec( text );
}
return text;
}
/*
* Front-end
*/
// Clear all button
_ui.ClearAll = () => {
var Btn = new OO.ui.ButtonInputWidget( {
disabled: true,
label: _locale.clearAll
} );
// Set event listeners
Btn.$button.on( 'click', () => {
$( '.iwrm-clear-toggle' ).click();
Btn.setLabel( Btn.getLabel() === _locale.clearAll ? _locale.clearAllRestore : _locale.clearAll );
} );
return Btn;
}
// Diff area
_ui.Diff = function( Diff ) {
// HTML parts for diff layout
var diffLayout = '<table class="diff"><col class="diff-marker"><col class="diff-content"><col class="diff-marker"><col class="diff-content">';
// Render an error if nothing was provided
if ( !Diff ) {
var $ErrorText = $( '<div>' ).addClass( 'error iwrm-error' ).text( _locale.errors.noLinks );
// Enable action buttons
_ui.fn.disableActions( false, true );
// Check buttons on validity
_ui.fn.checkBtns();
return $ErrorText;
}
// Render the diff
var $Diff = $( diffLayout );
$Diff.append( Diff );
return $Diff;
}
// Main dropdown
_ui.Dropdown = () => {
_FirstHeading.find( 'span' ).text( IWRM.data.title );
_ui.PageLinks( IWRM.data.title );
// Create the dropdown
var Dropdown = new OO.ui.DropdownInputWidget( {
disabled: true,
options: IWRM.list
} );
// Change event callback
function onDropdownChange( value ) {
// Disable action buttons
_ui.fn.disableActions( true );
// Had no diff
var hadNoDiff = IWRM.data.changeCount === 0;
// Modify the title
var oldTitle = IWRM.data.title;
IWRM.data.title = value;
if ( IWRM.data.title !== oldTitle ) {
var title = IWRM.data.title;
// Update counter
var Counter = _FirstHeading.find( '.iwrm-counter' );
if ( Counter.data( 'count' ) !== _counter ) {
if ( Counter.hasClass( 'is-zero' ) && _counter !== 0 ) {
Counter.removeClass( 'is-zero' );
Counter.text( '−' + _counter );
}
Counter.data( 'count', _counter );
if ( _counter !== 0 ) {
Counter.text( '−' + _counter );
}
if ( _counter > _searchAutoUpdate ) {
Counter.addClass( 'is-big' );
}
}
// Update index
IWRM.data.index = getPageIndex( title );
// Update interface
_FirstHeading.find( 'span' ).text( title );
_ui.PageLinks( title );
_ui.SiteSub( 0 );
// Change summary back to default
ifUIElementsExist(
IWRM.ui.Summary,
( El ) => {
El.setValue( IWRM.prefs.summary );
}
);
// Render new diff
_ui.fn.renderDiff( title ).then( () => {
// If there was a no links window, remove the previous item
if ( hadNoDiff ) {
_ui.fn.removeArticle( oldTitle );
}
// Load more data if needed
if ( _searchAutoUpdate !== -1 && IWRM.list.length < _searchAutoUpdate + 1 ) {
_ui.fn.getMoreData();
}
} );
}
}
// Set event listeners
Dropdown.on( 'change', onDropdownChange );
return Dropdown;
}
// Editing interface
_ui.EditingInterface = ( $Container, $Bar ) => {
var $ChangedLines = $Container.find( '.diff-deletedline' );
var promises = [];
$ChangedLines.each( ( index, el ) => {
promises.push(
_ui.fn.modifyLine( index, el, $ChangedLines.length, $Container, $Bar )
);
} );
Promise.all( promises ).then( () => {
// Remove any stray lines
$( '.iwrm-diff' ).find( '.diff-addedline' ).parent().remove();
// Focus on first element
var FirstInput = document.querySelector( '.iwrm-line input' );
if ( FirstInput !== null ) {
FirstInput.focus();
}
mw.hook( 'iwrm.content' ).fire( $Container );
} );
}
// Footer
_ui.Footer = () => {
// Summary field
IWRM.ui.Summary = new OO.ui.TextInputWidget( {
value: IWRM.prefs.summary,
title: _locale.summary,
accessKey: 'b'
} );
// Submit on Enter
IWRM.ui.Summary.$input.on( 'keydown', function( e ) {
if ( e.keyCode === 13 ) {
e.preventDefault();
ifUIElementsExist(
IWRM.ui.Submit,
function( Element ) {
Element.$button.click();
}
);
}
} );
// Show byte limit
var currentLimit = _c.wgCommentCodePointLimit - IWRM.prefs.summary.length;
IWRM.ui.Summary.$input.codePointLimit( currentLimit );
mw.widgets.visibleCodePointLimit( IWRM.ui.Summary, currentLimit );
// UI buttons
IWRM.ui.Submit = _ui.Submit();
IWRM.ui.ClearAll = _ui.ClearAll();
IWRM.ui.SkipPage = _ui.SkipPage();
return new OO.ui.FieldsetLayout( {
label: null,
items: [
new OO.ui.FieldLayout(
IWRM.ui.Summary,
{
align: 'top',
label: _locale.summary,
errors: [ new OO.ui.HtmlSnippet( _locale.warrant ) ]
}
),
new OO.ui.FieldLayout( new OO.ui.Widget( {
content: [
new OO.ui.HorizontalLayout( {
items: [
IWRM.ui.Submit,
IWRM.ui.ClearAll,
IWRM.ui.SkipPage
]
} )
]
} ) )
]
} );
}
// Input field
_ui.InputField = ( obj, modified, entity, isTemplate ) => {
var Input = new OO.ui.TextInputWidget( {
placeholder: obj.fullText,
value: modified,
title: _locale.notice.moveUpDown
} );
Input.$input.data( 'old', obj.fullText );
// Wikify the data (two times for link text transformations)
Wikify( Input.$input[ 0 ] );
Wikify( Input.$input[ 0 ] );
// Render and add an input field
var Field = new OO.ui.FieldLayout( Input, {
align: 'top',
label: null,
content: [ _ui.InputLinks( Input, obj.tmpl.lang, obj.tmpl.origTitle, entity ) ]
} );
// Check if article exists in the project
if ( isTemplate && obj.lang ) {
_ui.fn.checkArticle( modified, Field );
}
var timer;
Input.$input.on( 'blur', function() {
var value = this.value.trim();
clearTimeout( timer );
_ui.fn.checkArticle( value, Field );
} );
Input.$input.on( 'input', function() {
var value = this.value.trim();
Field.setErrors( [] );
clearTimeout( timer );
timer = setTimeout( () => {
_ui.fn.checkArticle( value, Field );
}, 500 );
} );
// Keyboard shortcuts
Input.$input.on( 'keydown', function( e ) {
_ui.fn.onInputKeyDown( e );
// Return to initial value on Esc
if ( e.keyCode === 27 ) {
e.preventDefault();
this.value = this.defaultValue;
_ui.fn.checkArticle( this.value, Field );
}
} );
// Move cursor if value starts with our template or a link
Input.$input.on( 'focus', function() {
var value = this.value.trim();
var start = -1;
var end;
if ( value.startsWith( _tmplStart ) ) {
start = _tmplStart.length - 1;
}
// Select link text if it’s a link
var endIndex = value.indexOf( _linkEnd );
if ( endIndex !== -1 ) {
var pipeIndex = value.indexOf( _linkPipe );
start = pipeIndex === -1 ? endIndex : pipeIndex + 1;
end = endIndex;
}
if ( start !== -1 ) {
this.setSelectionRange( start, end || start );
}
} );
return Field;
}
// Message with input links
_ui.InputLinks = ( $Input, lang, title, entity ) => {
var $LinksList = $( '<ul>' );
var $MainLink = $( '<a class="extiw">' )
.attr(
'href',
'https://$1.wikipedia.org'.replace( '$1', lang ) + mw.util.getUrl( title )
)
.attr( 'title', _locale.notice.interwiki )
.attr( '_target', 'blank' )
.text( '$1.wikipedia.org'.replace( '$1', lang ) );
$LinksList.append( $( '<li> ').append( $MainLink ) );
if ( entity ) {
var $WikidataLink = $( '<a class="extiw">' )
.attr(
'href',
'https://www.wikidata.org' + mw.util.getUrl( entity.id )
)
.attr( 'title', _locale.notice.wikidata )
.attr( '_target', 'blank' )
.text( 'wikidata.org' );
$LinksList.append( $( '<li> ').append( $WikidataLink ) );
// Add a link to local page if it is available
var thiswiki = OO.getProp( entity, 'sitelinks', _c.wgDBname );
if ( thiswiki ) {
var $PageLink = $( '<a>' )
.attr(
'href',
mw.util.getUrl( thiswiki.title )
)
.attr( 'title', _locale.notice.thiswiki )
.attr( '_target', 'blank' )
.text( _locale.notice.thiswikiArticle );
$LinksList.append( $( '<li> ').append( $PageLink ) );
}
}
// Add a link to clear/restore the input
var oldValue = $Input.$input.attr( 'value' );
var $ClearInputLink = $( '<a>' )
.attr( 'role', 'button' )
.attr( 'href', IWRM.prefs.hash + '#' )
.addClass( 'iwrm-clear-toggle' )
.text( _locale.notice.clearInput );
$ClearInputLink.on( 'click', function( e ) {
e.preventDefault();
var value = $Input.getValue();
if ( value !== '' ) {
oldValue = value;
$Input.setValue( '' );
$( this ).text( _locale.notice.clearInputRestore );
} else {
$Input.setValue( oldValue );
$( this ).text( _locale.notice.clearInput );
}
} );
$ClearInputLink.on( 'keydown', function( e ) {
if ( [ 'Enter', 'Space' ].includes( e.code ) ) {
e.preventDefault();
this.click();
}
} )
$LinksList.append( $( '<li> ').append( $ClearInputLink ) );
return $( '<div class="hlist hlist-items-nowrap">' ).append( $LinksList );
}
// Load more button
_ui.LoadMore = () => {
var Btn = new OO.ui.ButtonInputWidget( {
flags: [ 'primary', 'progressive' ],
icon: 'add',
label: _locale.loadMore
} );
// Set event listeners
Btn.$button.on( 'click', _ui.fn.getMoreData );
return Btn;
}
// Nearby page button (previous / next)
_ui.NearbyPage = ( type ) => {
var Btn = new OO.ui.ButtonInputWidget( {
disabled: true,
flags: [ 'progressive' ],
icon: type,
label: _locale.nearbyPage[ type ],
title: _locale.nearbyPage[ type ],
accessKey: type === 'previous' ? 'a' : 'd'
} );
// Click event callback
function onNearbyClick() {
var index = IWRM.data.index;
if ( type === 'previous' ) {
index = ( index - 1 < 0 ? 0 : index - 1 );
} else {
index = ( index + 1 > IWRM.list.length - 1 ? IWRM.list.length - 1 : index + 1 );
}
// Set different value
ifUIElementsExist(
IWRM.ui.Dropdown,
( El ) => {
El.setValue( getPageTitle( index ) );
}
);
}
// Set event listeners
Btn.$button.on( 'click', onNearbyClick );
return Btn;
}
// Page links (tabs and edit section)
_ui.PageLinks = ( title ) => {
var $Element = $( '.mw-editsection' );
var accessKeyHtml = ( _isSiteWide ? '' : ' accesskey="c"');
var Html = '<span class="mw-editsection-bracket">[</span>' +
'<a href="' + mw.util.getUrl( title ) + '" title="$1" target="_blank"' + accessKeyHtml + '>$2</a>' +
'<span style="margin:0 0.25em;">|</span>' +
'<a href="' + mw.util.getUrl( title, { action: 'edit' } ) + '" title="$3" target="_blank" accesskey="e">$4</a>' +
'<span class="mw-editsection-bracket">]</span>';
var htmlOpen = '<span class="mw-editsection mw-content-ltr iwrm-editsection" style="float: right;">';
var htmlClose = '</span>';
// Do the replacements
Html = Html
.replace( '$1', _locale.notice.viewArticle )
.replace( '$2', _locale.viewArticle )
.replace( '$3', _locale.notice.editSection )
.replace( '$4', _locale.editSection );
// Generic function to create tabs
function createPageTab( data ) {
var MainTab = mw.util.addPortletLink(
_c.skin === 'vector-2022' ? 'p-associated-pages' : 'p-namespaces',
data.href,
data.title,
data.id,
data.hoverInfo,
data.accessKey
);
return $( MainTab ).find( 'a' ).attr( 'target', '_blank' );
}
// Render two tabs if we are on the special page
if ( _isSiteWide ) {
var mainTabData = {
id: 'ca-iwrm-main',
title: _locale.mainTab,
href: mw.util.getUrl( title ),
hoverInfo: _locale.notice.viewArticle,
accessKey: 'c'
}
var talkTabData = {
id: 'ca-iwrm-talk',
title: _locale.talkTab,
href: mw.util.getUrl( _talkNs + title ),
hoverInfo: _locale.notice.viewTalk,
accessKey: 't'
}
var $MainTab = $( '#' + mainTabData.id );
var $TalkTab = $( '#' + talkTabData.id );
// Create article tab or set a new URL
if ( $MainTab.length ) {
$MainTab.find( 'a' ).attr( 'href', mainTabData.href );
} else {
$MainTab = createPageTab( mainTabData );
}
// Create talk tab or set a new URL
if ( $TalkTab.length ) {
$TalkTab.find( 'a' ).attr( 'href', talkTabData.href );
} else {
$TalkTab = createPageTab( talkTabData );
}
}
// Render the result
if ( $Element.length ) {
$Element.addClass( 'iwrm-editsection' );
$Element.html( Html );
return;
}
if ( _SiteSub.length ) {
Html = htmlOpen + Html + htmlClose;
_SiteSub.before( Html );
}
}
// Random page button
_ui.RandomPage = () => {
var Btn = new OO.ui.ButtonInputWidget( {
disabled: true,
icon: 'die',
label: _locale.randomPage
} );
// Click event callback
function onRandomClick() {
var rand = Math.floor( Math.random() * ( IWRM.list.length - 1 - 0 + 1 ) ) + 0;
ifUIElementsExist(
IWRM.ui.Dropdown,
( El ) => {
El.setValue( getPageTitle( rand ) );
}
);
}
// Set event listeners
Btn.$button.on( 'click', onRandomClick );
return Btn;
}
// SiteSub
_ui.SiteSub = ( count ) => {
if ( typeof count === 'undefined' ) {
count = IWRM.data.changeCount;
}
var text = _locale.title;
if ( count === 0 ) {
count = '';
}
if ( count === 1 ) {
text = _locale.titleOne;
}
if ( count ) {
count += ' ';
}
text = text.replace( '$1', count );
// If ContentSub exists
if ( _ContentSub.length ) {
_ContentSub.empty();
}
// If SiteSub exists
if ( _SiteSub.length ) {
_SiteSub.text( text );
return;
}
// If SiteSub doesn’t exist
_ContentText.parent().prepend(
$( '<div id="siteSub"></div>' ).text( text )
);
_SiteSub = $( '#siteSub' );
return;
}
// Skip a page
_ui.SkipPage = () => {
var Btn = new OO.ui.ButtonInputWidget( {
disabled: true,
flags: [ 'destructive' ],
framed: false,
label: _locale.skipPage,
title: _locale.skipPage,
accessKey: 'i'
} );
// Set event listeners
Btn.$button.on( 'click', () => {
_ui.fn.removeArticle( IWRM.data.title );
} );
return Btn;
}
// Submit changes
_ui.Submit = () => {
var Btn = new OO.ui.ButtonInputWidget( {
disabled: true,
flags: [ 'primary', 'progressive' ],
label: _locale.submit,
title: _locale.submit,
accessKey: 's'
} );
// Click event callback
function onSubmitClick() {
// Disable action buttons
_ui.fn.disableActions( true );
var modifiedText = _ui.fn.parseChanges( IWRM.data.text );
// Show an error if user tries to submit a template with unchanged code
if ( modifiedText === false ) {
_ui.fn.notify( null, null, {
text: _locale.errors.unchangedCode,
tag: 'iwrm-unchanged'
} );
_ui.fn.disableActions( false );
return;
}
// Change summary
var summary = IWRM.prefs.summary;
ifUIElementsExist(
IWRM.ui.Summary,
( El ) => {
summary = El.getValue().trim();
}
);
// Submit
IWRM.api.submitChanges(
IWRM.data.title,
modifiedText,
summary
);
}
// Set event listeners
Btn.$button.on( 'click', onSubmitClick );
return Btn;
}
// A paragraph editing toggle
_ui.Toggle = ( Textarea, $FauxLine ) => {
var Toggle = new OO.ui.ToggleSwitchWidget();
// Change event callback
function onToggleChange( value ) {
$FauxLine.toggleClass( 'is-editing-paragraph' );
Textarea.setDisabled( !value );
var textareaValue = Textarea.$input.data( 'old' );
$FauxLine.find( '.iwrm-line input' ).each( ( index, el ) => {
$( el ).attr( 'disabled', value );
if ( value === true ) {
var old = $( el ).data( 'old' );
var elValue = $( el ).val().trim();
if ( elValue !== '' ) {
textareaValue = textareaValue.replace( old, elValue );
}
Textarea.focus();
} else if ( index === 0 ) {
$( el ).focus();
}
} );
if ( value === true ) {
Textarea.setValue( textareaValue );
}
}
// Set event listeners
Toggle.on( 'change', onToggleChange );
return Toggle;
}
// Upper toolbar
_ui.Toolbar = () => {
// Should be loaded first and foremost
IWRM.ui.Dropdown = _ui.Dropdown();
// Other buttons
IWRM.ui.LoadMore = _ui.LoadMore();
IWRM.ui.PreviousPage = _ui.NearbyPage( 'previous' );
IWRM.ui.NextPage = _ui.NearbyPage( 'next' );
IWRM.ui.RandomPage = _ui.RandomPage();
return new OO.ui.Widget( {
classes: [ 'iwrm-toolbar' ],
content: [
new OO.ui.HorizontalLayout( {
items: [
IWRM.ui.LoadMore,
IWRM.ui.PreviousPage,
IWRM.ui.Dropdown,
IWRM.ui.NextPage,
IWRM.ui.RandomPage
]
} )
]
} );
}
// Check if article exists
_ui.fn.checkArticle = ( value, Field ) => {
var match = _regularTmpl.exec( value );
var title = match && match[ 1 ];
// Success
function resolvedWithSuccess( data ) {
var entity;
var isMissing = OO.getProp( data, 'entities', '-1' );
if ( !isMissing ) {
var link = '<a href="' + mw.util.getUrl( title ) + '" title="$1" target="_blank">'.replace( '$1', _locale.notice.thiswiki );
var error = _locale.errors.articleExists.replace( '$2', link ).replace( '$3', '</a>' ).replace( '$1', title );
Field.setErrors( [
new OO.ui.HtmlSnippet( error )
] );
return;
}
Field.setErrors( [] );
}
if ( title ) {
IWRM.api.checkSitelinks(
_c.wgContentLanguage,
title
).done( resolvedWithSuccess );
return;
}
}
// Check nearby pages on validity
_ui.fn.checkBtns = () => {
ifUIElementsExist(
IWRM.ui.PreviousPage,
( El ) => {
El.setDisabled( IWRM.data.index === 0 );
}
);
ifUIElementsExist(
IWRM.ui.NextPage,
( El ) => {
El.setDisabled( IWRM.data.index === IWRM.list.length - 1 );
}
);
// Random page button
ifUIElementsExist(
IWRM.ui.RandomPage,
( El ) => {
El.setDisabled( IWRM.list.length === 1 );
}
);
}
// Enable and disable action buttons
_ui.fn.disableActions = ( value, submitValue ) => {
// Disable according to value
const toggleAction = ( El ) => {
El.setDisabled( value );
}
// Nearby pages buttons
if ( value === true ) {
ifUIElementsExist(
[
IWRM.ui.PreviousPage,
IWRM.ui.NextPage,
IWRM.ui.RandomPage
],
toggleAction
);
} else {
_ui.fn.checkBtns();
}
// Dropdown and footer buttons
ifUIElementsExist(
[
IWRM.ui.Dropdown,
IWRM.ui.ClearAll,
IWRM.ui.SkipPage
],
toggleAction
);
ifUIElementsExist(
IWRM.ui.Submit,
( El ) => {
var val = typeof submitValue === 'undefined' ? value : submitValue;
El.setDisabled( val );
}
);
}
// Get more data
_ui.fn.getMoreData = () => {
// Disable Load more button
ifUIElementsExist(
IWRM.ui.LoadMore,
( El ) => {
El.setDisabled( true );
}
);
IWRM.api.getSearchData().then( () => {
// Update data in dropdown
ifUIElementsExist(
IWRM.ui.Dropdown,
( El ) => {
El.setOptions( IWRM.list );
}
);
// Enable Load more button and update text
ifUIElementsExist(
IWRM.ui.LoadMore,
( El ) => {
El.setDisabled( false );
}
);
// Check buttons on validity
_ui.fn.checkBtns();
} );
}
// Modify a changed line
_ui.fn.modifyLine = ( index, El, length, $Container, $Bar ) => {
var $El = $( El );
var $Parent = $El.parent();
// Find a future container for inputs
var $FauxLine = $Parent.find( '.diff-addedline' );
if ( $FauxLine.length === 0 ) {
$FauxLine = $Parent.find( '.diff-empty' );
if ( $FauxLine.length > 0 ) {
$FauxLine.removeAttr( 'colspan' );
$FauxLine.before( '<td class="diff-marker"></td>' );
$Parent.next().remove();
}
}
// Modify input container
$FauxLine.removeClass( 'diff-addedline' ).removeClass( 'diff-empty' ).addClass( 'iwrm-fauxline' );
var Html = '<div class="iwrm-paragraph"></div><div class="iwrm-line"></div><div class="iwrm-toggle"></div>';
$FauxLine.html( Html );
var $InputHolder = $FauxLine.find( '.iwrm-line' );
// Render input fields for each change
var text = $El.text();
var InputFields = [];
replaceLinks( text, ( obj, text ) => {
var index = text.indexOf( obj.fullText );
InputFields[ index ] = _ui.fn.renderInput( obj );
IWRM.data.changeCount++;
// Return the dummy modified text to go to next input
var modified = getTemplate( obj );
return text.replace( obj.fullText, modified );
} );
// Append them all at once synchronously
var promises = Promise.all( InputFields ).then( ( Element ) => {
$InputHolder.append( Element );
} ).then( () => {
// Remove progress bar and show the diff
if ( index === length - 1 ) {
$Bar.$element.remove();
$Container.addClass( IWRM.prefs.loaded );
// Check buttons on validity
_ui.fn.checkBtns();
// Enable action buttons
_ui.fn.disableActions( false );
// Update link count
_ui.SiteSub();
}
} );
// Render a hidden textarea
var Textarea = new OO.ui.MultilineTextInputWidget( {
autosize: true,
disabled: true,
title: _locale.notice.moveUpDown
} );
Textarea.$input.data( 'old', text );
$FauxLine.find( '.iwrm-paragraph' ).append( Textarea.$element );
// Keyboard shortcuts
Textarea.$input.on( 'keydown', _ui.fn.onInputKeyDown );
// Render a toggle for changing states
var toggle = new OO.ui.FieldLayout(
_ui.Toggle( Textarea, $FauxLine ),
{
label: _locale.editParagraph
}
);
$FauxLine.find( '.iwrm-toggle' ).append( toggle.$element );
return promises;
}
// Render an input
_ui.fn.renderInput = ( obj ) => {
var modified = getTemplate( obj );
// Success
function resolvedWithSuccess( data, isTemplate ) {
var entity;
var isMissing = OO.getProp( data, 'entities', '-1' );
// For returning a link to the same language
var title = obj.tmpl.origTitle;
if ( !isMissing ) {
entity = OO.getProp( data, 'entities' );
if ( entity ) {
var keys = Object.keys( entity );
entity = entity[ keys[ 0 ] ];
}
// Change the output if a local page is available
var data_sl = OO.getProp( entity, 'sitelinks', _c.wgDBname );
if ( data_sl ) {
title = data_sl.title;
isTemplate = false;
}
}
if ( isTemplate === false ) {
modified = getLink( obj, title );
}
var InputField = _ui.InputField( obj, modified, entity, isTemplate );
return $.Deferred().resolve( InputField.$element );
}
// Failure
function resolvedWithFailure() {
IWRM.data.changeCount--;
// Add an error message
return $.Deferred().resolve( '<div class="iwrm-error">$1</div>'.replace(
'$1',
_locale.errors.badInterwiki.replace( '$1', obj.tmpl.lang )
) );
}
// Render a link on links from the same wiki
if ( obj.tmpl.lang === _c.wgContentLanguage ) {
const fakeData = { entities: { '-1': { data: 'fake' } } };
return resolvedWithSuccess( fakeData, false );
}
// Return a promise
return IWRM.api.checkSitelinks(
obj.tmpl.lang,
obj.tmpl.origTitle
).then( resolvedWithSuccess, resolvedWithFailure );
}
// React to keydown events in the same fashion
_ui.fn.onInputKeyDown = function( e ) {
// Submit on (Ctrl|Alt)+Enter or (Ctrl|Alt)+S
if ( ( e.ctrlKey || e.altKey ) && !e.shiftKey && ( e.code === 'Enter' || e.code === 'KeyS' ) ) {
e.preventDefault();
ifUIElementsExist(
IWRM.ui.Submit,
( El ) => {
El.$button.click();
_notification = _ui.fn.notify( null, null, {
text: _locale.errors.saving.replace( '$1', IWRM.data.title ),
tag: 'iwrm-keydown'
} );
}
);
}
// (Ctrl|Alt)+(Shift+|)(J|K) (Alt+J/Alt+K|Ctrl+J/Alt+K) to move between fields
var goToPrev = e.code === 'KeyJ';
var goToNext = e.code === 'KeyK';
if ( ( e.ctrlKey || e.altKey ) && ( goToPrev || goToNext ) ) {
e.preventDefault();
var inputs = document.querySelectorAll( '.iwrm-diff input:not([disabled]), .iwrm-diff textarea:not([disabled])' );
if ( inputs.length <= 1 ) {
_notification = _ui.fn.notify( null, null, {
text: _locale.errors.altOnlyLink,
tag: 'iwrm-keydown'
} );
return;
}
var thisInput = e.target;
var index = 0;
var chosenIndex = null;
while ( index < inputs.length ) {
if ( inputs[ index ] === thisInput ) {
chosenIndex = index;
break;
}
index++;
}
function notifyIfNoNextInput() {
if ( goToPrev ) return;
_notification = _ui.fn.notify( null, null, {
text: _locale.errors.altNoNext,
tag: 'iwrm-keydown'
} );
}
if ( chosenIndex === null ) {
notifyIfNoNextInput();
return;
}
var nextIndex = goToNext ? chosenIndex + 1 : chosenIndex - 1;
ifUIElementsExist(
inputs[ nextIndex ],
( El ) => {
El.focus();
},
notifyIfNoNextInput
);
}
}
// Parse changes in the diff view
_ui.fn.parseChanges = ( content ) => {
var result = content;
var hasErrors = false;
var $Changes = $(
'.iwrm-paragraph textarea:not(:disabled), '
+ '.iwrm-line input:not(:disabled)'
);
$Changes.each( ( index, el ) => {
var old = $( el ).data( 'old' );
var value = $( el ).val();
// Do not trim textarea, since there can be meaningful spaces
if ( $( el ).prop( 'tagName' ) === 'INPUT' ) {
value = value.trim();
}
if ( value !== '' || $( el ).prop( 'tagName' ) === 'TEXTAREA' ) {
// Check if there is unchanged code anywhere
if ( value.includes( _tmplStart ) ) {
hasErrors = true;
return false;
}
var unmodified = result;
// Remove entire paragraph for textarea
if ( value === '' ) {
result = result.replace( old + '\n', value );
unmodified = result;
}
// Remove other text if no modifications were made
if ( unmodified === result ) {
result = result.replace( old, value );
}
}
} );
if ( result === content ) {
return false;
}
return hasErrors === true ? false : result;
}
// Remove an article from the lists
_ui.fn.removeArticle = ( title ) => {
// Calculate the index and update the lists
var index = getPageIndex( title );
if ( index === -1 ) {
return;
}
IWRM.list.splice( index, 1 );
// Set new data to dropdown and update the picked element
var newIndex = ( index + 1 > IWRM.list.length - 1 ? IWRM.list.length - 1 : index );
ifUIElementsExist(
IWRM.ui.Dropdown,
( El ) => {
El.setOptionsData( IWRM.list );
El.setValue( getPageTitle( newIndex ) );
IWRM.data.index = newIndex;
}
);
}
// Render a diff
_ui.fn.renderDiff = ( title ) => {
var $Container = $( '.iwrm-diff' );
var $DiffBar = new OO.ui.ProgressBarWidget( {
progress: false
} );
// Render a progress bar
$Container.removeClass( IWRM.prefs.loaded );
$Container.html( $DiffBar.$element );
return IWRM.api.getFullText( title ).then( () => {
return IWRM.api.loadDiff( title ).then( ( data ) => {
// Insert the diff or the lack of it
var DiffResult = OO.getProp( data, 'compare', 'body' );
var Diff = _ui.Diff( DiffResult );
$Container.append( Diff );
// Start rendering replacement interface
IWRM.data.changeCount = 0;
if ( DiffResult ) {
IWRM.ui.Diff = Diff;
_ui.EditingInterface( $Container, $DiffBar );
} else {
IWRM.ui.Diff = null;
throw new Error();
}
IWRM.data.modifiedText = '';
} );
} ).catch( () => {
// Remove progress bar and show the message
$DiffBar.$element.remove();
$Container.addClass( IWRM.prefs.loaded );
} );
}
// Show a notification
_ui.fn.notify = ( error, data, options ) => {
var text = options ? options.text : '';
var replace = options ? options.replace : '';
// Add a default text
if ( !text ) {
text = _locale.errors.unidentified;
}
if ( error === 'http' ) {
text = _locale.errors.noNetwork;
}
if ( error === 'missingtitle' ) {
text = _locale.errors.noArticle;
}
// Show up some additional text to keep text different
if ( replace ) {
text = text.replace( '$1', replace );
}
// Get reasoning for showing the popup
if ( error ) {
text = _locale.errors.text + text;
}
return mw.notification.notify( text, {
autoHide: ( error ? false : true ),
title: ( error ? _locale.errors.title : '' ),
tag: ( options && options.tag ? options.tag : null )
} );
}
/*
* Initialising a portlet
*/
IWRM.InitPortlet = () => {
var namespaces = [
0,
14, // Category
]
if ( !namespaces.includes( _c.wgNamespaceNumber ) && _c.wgTitle !== 'Песочница' || mw.config.get( 'wgIsMainPage' ) ) {
return;
}
if ( _c.wgAction !== 'view' ) {
return;
}
window.addEventListener( 'hashchange', () => {
IWRM.Init();
} );
var $iwLinks = $( '.mw-parser-output a.extiw' );
if ( _c.wgNamespaceNumber === 0 && $iwLinks.length === 0 ) {
return;
}
mw.loader.using( 'mediawiki.util', () => {
var portlet = mw.util.addPortletLink(
'p-tb',
IWRM.prefs.hash + _c.wgPageName,
_locale.title.replace( '$1', '' ),
't-iwrm',
IWRM.prefs.name
);
} );
}
/*
* Initialising
*/
IWRM.Init = ( customList ) => {
// Check hash and user groups
var from = window.location.hash;
if ( !from.startsWith( IWRM.prefs.hash ) ) {
if ( _isSiteWide ) {
console.warn( IWRM.prefs.name + ': please re-open the page with ' + IWRM.prefs.hash + ' if you intended to use the script' );
}
return;
}
if ( !_isAllowed ) {
_ui.fn.notify( 'script', null, { text: _locale.errors.notAllowed } );
return;
}
// Change page title
if ( _isSiteWide ) {
document.title = document.title.replace( _c.wgTitle, _locale.title.replace( '$1', '' ) );
}
_FirstHeading.text( '' );
_FirstHeading.append( $( '<small>', { class: 'iwrm-counter' + ' is-zero' } ).text( _counter ) );
_FirstHeading.append( $( '<span>' ).text( _locale.loadingTitle ) );
_ui.SiteSub( 0 );
// Start rendering the interface
_ContentText.addClass( 'mw-parser-output' ).addClass( 'iwrm' );
_ContentText.empty();
_Indicators.empty();
$( '.iwrm' ).removeClass( IWRM.prefs.loaded );
mw.loader.using( _deps ).done( () => {
_api = new mw.Api();
_wikidataApi = new mw.ForeignApi( 'https://www.wikidata.org/ruwiki/w/api.php' );
// Render a progress bar
var $ContentBar = new OO.ui.ProgressBarWidget( {
progress: false
} );
_ContentText.append( $ContentBar.$element );
_notification = _ui.fn.notify( null, null, {
text: _locale.loading,
replace: IWRM.prefs.name
} );
IWRM.api.getSearchData( customList ).then( () => {
// Render toolbar once
_ContentText.append( _ui.Toolbar().$element );
// Preload diff area
_ContentText.append( $( '<div>', { class: 'iwrm-diff' } ) );
// Render footer
_ContentText.append( _ui.Footer().$element );
// Remove progress bar and show the page
_ui.fn.renderDiff( IWRM.data.title ).then( () => {
$ContentBar.$element.remove();
_ContentText.addClass( IWRM.prefs.loaded );
} );
// Confirm before leaving with a hash
mw.confirmCloseWindow( {
test: () => {
return window.location.hash.startsWith( IWRM.prefs.hash );
}
} );
// Remove notifications on saving
mw.hook( 'postEdit' ).add( () => {
if ( _notification ) {
_notification = _notification.close();
}
} );
} );
} ).fail( ( error, data ) => {
_ui.fn.notify( error, data, { replace: _locale.requests.init } );
} );
}
/* </nowiki>
* Starting point
*/
IWRM.Init();
if ( !_isSiteWide ) {
IWRM.InitPortlet();
}
}() );