MediaWiki:Gadget-iwrm.js
Перейти к навигации
Перейти к поиску
JS-код ниже относится к гаджету «Интерфейс для замены прямых интервики-ссылок в полуавтоматическом режиме для проекта Check Wikipedia» (править описание). Связанный CSS-файл: MediaWiki:Gadget-iwrm.css. Его использует около 200 учётных записей.
После сохранения или недавних изменений очистите кэш браузера.
/* <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();
}
}() );