Участник:Stjn/disambigAutoDesc.js

/**
 * disambigAutoDesc.js
 * <nowiki>
 * Добавляет кнопку, по которой для выделенного текста со ссылкой в [[]] добавляется описание из преамбулы страницы.
 * Для страниц значений заменяет выделенную ссылку на шаблон {{NL}}.
 * Работает только на страницах значений, в перенаправлениях и при создании страниц.
 */
mw.loader.using( [
	'mediawiki.util',
	'jquery.textSelection',
	'ext.gadget.registerTool',
], () => {
	if ( mw.config.get( 'wgNamespaceNumber' ) !== 0 ) return;

	// @ts-expect-error
	if ( typeof registerTool === 'undefined' ) return;

	const _linkRegex = /\[\[(.*?)[\|\]]+/;

	let _textbox = $( '#wpTextbox1' );

	const _templateNames = [
		'неоднозначность',
		'многозначность',
		'disambig',
		'disambiguation',
		'дизамбиг',
		'список значений',
		'militarydis',
		'военные части',
		'воинские формирования',
		'воинские части',
		'нисба',
		'одноимённые воинские части',
		'омонимы систематиков',
		'список однофамильцев',
		'список однофамильцев-тезок',
		'список однофамильцев-тёзок',
		'список полных тёзок',
		'список тёзок',
		'список тёзок-однофамильцев',
	];

	const notDisambigable = () => {
		// Для редактора-2017 проверяем HTML
		if ( document.querySelector( '#disambig' ) !== null ) return false;
		
		// Несуществующие страницы
		if ( mw.config.get( 'wgArticleId' ) === 0 ) return false;

		let text = _textbox.val()?.toString().toLowerCase();
		if ( !text ) return false;

		// Перенаправления
		if ( text.match( /^#.* \[\[/ ) !== null ) return false;

		return !_templateNames.some( t => text.includes( `{{${ t }` ) );
	}
	
	if ( notDisambigable() ) return;

	/**
	 * @param {string} msg
	 * @param {mw.notification.NotificationOptions} options
	 */
	const showMessage = ( msg, options ) => {
		let result = $( '<span>' ).html(
			msg.replace( _linkRegex, ( _, $1 ) => {
				var url = mw.util.getUrl( $1 );
				return `<a href="${ url }">[[${ $1 }]]</a>`;
			} )
		);

		return mw.notify( result, {
			tag: 'disambigAutoDesc',
			...options,
		} );
	}

	/**
	 * @param {string} msg
	 */
	const showError = ( msg ) => {
		return showMessage( msg, {
			type: 'error',
			autoHideSeconds: 'long',
		} );
	}

	const runAutoDesc = () => {
		_textbox = $( '#wpTextbox1' );
		
		const selection = _textbox.textSelection( 'getSelection' );
		if ( selection === null || selection === '' ) {
			showError( 'Для создания автоопределения необходимо выделить текст.' );
			return;
		}

		// Does not contain wikilink syntax
		const wikilink = selection.match( _linkRegex );
		if ( wikilink === null ) {
			showError( 'Для создания автоопределения необходимо выделить &#91;&#91;вики-ссылку&#93;&#93; в квадратных скобках.' );
			return;
		}

		const target = wikilink[ 1 ];
		fetch( `/api/rest_v1/page/summary/${ target }` ).then( res => res.json() ).then( data => {
			// Ошибки разного рода
			if ( ![ 'standard', 'disambiguation' ].includes( data.type ) ) {
				showError( `<b>Не вышло создать автоопределение [[${ target }]].</b> Скорее всего, страница не существует.` );
				return;
			}
			const title = data.title;

			let text = selection;
			let desc = null;
			if ( data.type === 'disambiguation' ) {
				// Заменить ссылку на {{NL}}
				text = text.replace( '[[', `{{NL|` )
					.replace( target, title )
					.replace( ']]', '}}' );
				
				text = text.replace( /{{NL\|(.*?) \(значения\)\|\1}}/, '{{NL|$1 (значения)}}' );
			} else {
				const dashes = `[–−—―]`;
				// Текст после тире вне (1900—2000)
				const dashRegex = new RegExp( `[^\\d]${ dashes }\\s*([^\\.\\n]+)`, 'm' );
				const endStopRegex = /\.*(\n?)$/;
				desc = data.extract.match( dashRegex );
				if ( desc !== null ) {
					desc = desc[ 1 ].trim();
					const newDesc = desc.trim();
					let result = text.trim().replace( endStopRegex, '$1' );
					if ( text.match( dashRegex ) === null ) {
						result = `${ result } — ${ newDesc }`;
					} else {
						// Заменить часть после последнего тире, а не все
						const textParts = text.split( new RegExp( `\\s+${ dashes }\\s+` ) );
						result = textParts[ 0 ];
						for (let i = 1; i < textParts.length; i++) {
							let part = textParts[ i ];
							if ( i === textParts.length - 1 ) {
								part = newDesc;
							}
							result = `${ result } — ${ part }`;
						}
					}
					
					// В выделении в Коудмирроре может быть перенос в конце
					if ( text.match( /\n+$/ ) ) {
						result = result + '\n';
					}

					text = result.replace( endStopRegex, '.$1' );
				}
			}

			// Добавить годы жизни для статей в формате ФИО для страниц с элементами Викиданных
			if ( data.wikibase_item && title.includes( ', ' ) && !text.includes( ']] (' ) ) {
				text = text.replace( /\]\]\s*—/, `]] ({{подст:годы жизни|${ data.wikibase_item }}}) —` );
			}

			if ( text === selection ) {
				showMessage( `<b>Не вышло создать автоопределение [[${ title }]].</b> Скорее всего, на странице отсутствует тире или текущее определение совпадает с новым.`, {
					type: 'warn',
				} );
				return;
			}

			// Уточнить ссылку, если отличается
			if ( data.type === 'standard' && target !== title ) {
				text = text.replace( target, title );
			}

			// Скрыть уточнение при наличии
			if ( title.includes( ' (' ) ) {
				text = text.replace( title + ']]', `${ title }|]]` );
			}

			_textbox.textSelection( 'encapsulateSelection', {
				peri: text,
				replace: true,
			} );

			// Перейти в конец добавленной строки
			let textboxVal = _textbox.val()?.toString();
			let linkIndex = textboxVal?.indexOf( title );
			const startPos = textboxVal ? textboxVal.indexOf( desc, linkIndex ) : -1;
			if ( desc && startPos !== -1 ) {
				_textbox.textSelection( 'setSelection', {
					start: startPos + desc.length,
					end: startPos + desc.length,
				} );
			}

			showMessage( `<b>Создано автоопределение [[${ title }]].</b> Пожалуйста, отредактируйте его в соответствии с правилами проекта. Помните, что все определения должны быть краткими.`, {
				type: 'success',
			} );
		} ).catch( err => {
			showError( `<b>При запросе автоопределения произошла техническая ошибка:</b><br>${ err }` );
		} );
	}
	
	// @ts-expect-error
	registerTool( {
		name: 'disambigAutoDesc',
		position: 1312,
		title: 'Автоопределение',
		label: 'Выделите текст со [[ссылкой]], чтобы добавить ей автоопределение из преамбулы',
		callback: runAutoDesc,
		classic: {
			icon: 'https://upload.wikimedia.org/wikipedia/commons/a/a9/Logo_disambig.svg',
		},
		visual: {
			icon: 'https://upload.wikimedia.org/wikipedia/commons/a/a9/Logo_disambig.svg',
			modes: [ 'source' ],
		},
	} );
} );
// </nowiki>
Prefix: a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9

Portal di Ensiklopedia Dunia

Kembali kehalaman sebelumnya