User:Nardog/AutoSectionLink.js
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' &&
$(function autoSectionLink() {
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');
mw.loader.using([
'jquery.textSelection', 'mediawiki.util', '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').replace(/\s+$/, '').split('\n');
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;
}
});
lines.length = lastLineNum || 0;
} else {
let origLines = $textarea.prop('defaultValue').replace(/\s+$/, '').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++
) {
lines.pop();
}
}
let re = /^(={1,6})\s*(.+?)\s*\1\s*(?:<!--.+-->\s*)?$/, lowest = 7;
lines.slice(firstLineNum).forEach(line => {
let match = line.match(re);
if (match && match[1].length < lowest) {
lowest = match[1].length;
}
});
let head;
lines.slice(0, firstLineNum).reverse().some(line => {
let match = line.match(re);
if (match && match[1].length < lowest) {
head = match[2];
return true;
}
});
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() : '';
let match = input.getValue().match(/^(?:\/\*\s*(.*?)\s*\*\/)?\s*(.*?)$/);
let prev = match[1] || '';
if (prev === head) return;
if (section < 1 && lowest === 7 && !head && prev === 'top') return;
input.setValue((head ? '/* ' + head + ' */ ' : '') + match[2]);
button.setData([prev, head]).toggle(true);
updatePreview(head);
}, 500);
let updatePreview = head => {
let $preview = $('.mw-summary-preview > .comment > span[dir="auto"]');
if (!$preview.length) return;
let url = head && mw.util.getUrl() + '#' + head.replace(/ /g, '_');
let text = head && (document.dir === 'rtl' ? '←\u200F' : '→\u200E') + head;
let $ac = $preview.children('.autocomment:first-child');
if ($ac.length && !$ac[0].previousSibling) {
if (head) {
$ac.children('a').attr('href', url).text(text);
} else {
let node = $ac[0].nextSibling;
if (node && node.nodeType === 3) {
node.textContent = node.textContent.replace(/^\s+/, '');
}
$ac.remove();
}
} else if (head) {
$('<span>').addClass('autocomment').append(
$('<a>').attr({
href: url,
title: mw.config.get('wgPageName').replace(/_/g, ' ')
}).text(text),
mw.messages.get('colon-separator', ': ')
).prependTo($preview);
}
};
if (isOld) {
mw.hook('wikipage.diff').add(update);
} else {
$textarea.on('input', update);
mw.hook('ext.CodeMirror.switch').add((on, $codeMirror) => {
if (on && $codeMirror[0].CodeMirror) {
$codeMirror[0].CodeMirror.on('change', update);
}
});
mw.hook('ext.CodeMirror.input').add(update);
update();
}
});
mw.loader.addStyleTag('.autosectionlink-button{position:absolute;top:0;right:0;margin:0}');
});