Jump to content

User:Nardog/AutoSectionLink.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
['edit', 'submit'].includes(mw.config.get('wgAction')) &&
mw.config.get('wgArticleId') &&
mw.config.get('wgPageContentModel') === 'wikitext' &&
$(async () => {
	let form = document.getElementById('editform');
	if (!form) return;
	let formData = new FormData(form);
	let section = formData.get('wpSection');
	if (section === 'new') return;
	let widget = document.getElementById('wpSummaryWidget');
	if (!widget) return;
	let isOld = formData.get('altBaseRevId') > 0 ||
		(formData.get('baseRevId') || formData.get('parentRevId')) !== formData.get('editRevId');
	await mw.loader.using([
		'jquery.textSelection', 'mediawiki.util', 'mediawiki.api', 'oojs-ui-core',
		'oojs-ui.styles.icons-editing-core'
	]);
	let $textarea = $('#wpTextbox1');
	let input = OO.ui.infuse(widget);
	let button = new OO.ui.ButtonWidget({
		framed: false,
		icon: 'undo',
		classes: ['autosectionlink-button'],
		invisibleLabel: true,
		label: 'Restore previous section link'
	}).toggle().on('click', () => {
		let cache = button.getData();
		input.setValue(input.getValue().replace(
			/^(\/\*.*?\*\/)?\s*/,
			cache[0] ? '/* ' + cache[0] + ' */ ' : ''
		));
		updatePreview(cache[0]);
		cache.reverse();
	}).on('toggle', () => {
		input.$input.css('width', `calc(100% - ${button.$element.width()}px)`);
	});
	input.$input.after(button.$element);
	let update = mw.util.debounce($diff => {
		let lines = $textarea.textSelection('getContents').trimEnd().split('\n');
		let modLines = [...lines];
		let firstLineNum;
		if (isOld) {
			let i, lastLineNum;
			$diff.find('td:last-child').each(function () {
				if (this.classList.contains('diff-lineno')) {
					i = this.textContent.replace(/\D+/g, '') - 1;
				} else if (this.classList.contains('diff-context')) {
					i++;
				} else if (this.classList.contains('diff-addedline')) {
					i++;
					if (!firstLineNum) {
						firstLineNum = i;
					}
					lastLineNum = i;
				} else if (this.classList.contains('diff-empty')) {
					if (!firstLineNum) {
						firstLineNum = i === 0 ? 1 : i;
					}
					lastLineNum = i;
				}
			});
			modLines.length = lastLineNum || 0;
		} else {
			let origLines = $textarea.prop('defaultValue').trimEnd().split('\n');
			firstLineNum = lines.findIndex((line, i) => line !== origLines[i]) + 1;
			if (!firstLineNum) {
				firstLineNum = lines.length < origLines.length
					? lines.length
					: 1;
			}
			for (let i = 1, x = lines.length, y = origLines.length;
				(section ? i < x : i <= x) && lines[x - i] === origLines[y - i];
				i++
			) {
				modLines.pop();
			}
		}
		let re = /^(={1,6})\s*(.+?)\s*\1\s*(?:<!--.+-->\s*)?$/, lowest = 7;
		modLines.slice(firstLineNum).forEach(line => {
			let match = line.match(re);
			if (match?.[1].length < lowest) {
				lowest = match[1].length;
			}
		});
		let head;
		modLines.slice(0, firstLineNum).reverse().some(line => {
			let match = line.match(re);
			if (match?.[1].length < lowest) {
				head = match[2];
				return true;
			}
		});
		if (head) {
			head = head
				.replace(/'''(.+?)'''|\[\[:?(?:[^|\]]+\|)?([^\]]+)\]\]|<\/?(?:abbr|b|bdi|bdo|big|cite|code|data|del|dfn|em|font|i|ins|kbd|mark|nowiki|q|rb|ref|rp|rt|rtc|ruby|s|samp|small|span|strike|strong|sub|sup|templatestyles|time|translate|tt|u|var)(?:\s[^>]*)?>|<!--.*?-->|\[(?:https?:)?\/\/[^\s\[\]]+\s([^\]]+)\]/gi, '$1$2$3')
				.replace(/''(.+?)''/g, '$1')
				.trim();
		} else if (modLines.length && lowest === 7 && section < 1 && (
			section === '0' || lines.slice(modLines.length).some(line => re.test(line))
		)) {
			head = '';
		}
		let v = input.getValue();
		let match = v.match(/^\/\*\s*(.+?)\s*\*\/\s*/);
		let prev = match?.[1];
		if (prev === head) return;
		input.setValue((
			head || head === '' ? '/* ' + head + ' */ ' : ''
		) + (match ? v.slice(match[0].length) : v));
		button.setData([prev, head]).toggle(true);
		updatePreview(head);
	}, 500);
	let updatePreview = head => {
		let $comment = $('.mw-summary-preview > .comment');
		if (!$comment.length) return;
		let url;
		if (head) {
			url = mw.util.getUrl() + '#' + head.replace(/[\s_]+/g, '_');
		} else if (head === '') {
			head = mw.messages.get('autocomment-top', '(top)');
			url = mw.util.getUrl();
		}
		let paren = [...mw.messages.get('parentheses', '($1)')][0];
		let $nodes = $comment.contents();
		let $ac = $nodes.eq(1);
		if ($nodes[0]?.textContent === paren && $ac.is('.autocomment:first-child')) {
			if (head) {
				$ac.children('a').attr('href', url).children('bdi').text(head);
			} else {
				if ($nodes[2]?.nodeType === 3) {
					$nodes[2].textContent = $nodes[2].textContent.trimStart();
				}
				$ac.remove();
			}
		} else if (head) {
			let rtl = document.body.classList.contains('sitedir-rtl');
			$comment.prepend(
				paren,
				$('<span>').addClass('autocomment').append(
					$('<a>').attr({
						href: url,
						title: mw.config.get('wgPageName').replaceAll('_', ' ')
					}).text(rtl ? '←' : '→').append(
						$('<bdi>').attr('dir', rtl ? 'rtl' : 'ltr').text(head)
					),
					mw.messages.get('colon-separator', ': ')
				)
			);
			if ($nodes[0]?.nodeType === 3) {
				let text = $nodes[0].textContent;
				if (text.startsWith(paren)) {
					text = text.slice(paren.length);
				}
				$nodes[0].textContent = ' ' + text;
			}
		}
	};
	if (isOld) {
		mw.hook('wikipage.diff').add(update);
	} else {
		$textarea.on('input', update);
		mw.hook('ext.CodeMirror.input').add(update);
		update();
	}
	new mw.Api().loadMessagesIfMissing(['autocomment-top', 'colon-separator', 'parentheses']);
	mw.loader.addStyleTag('.autosectionlink-button{position:absolute;top:0;right:0;margin:0}');
});