User:Aram/diff restorer.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | This user script seems to have a documentation page at User:Aram/diff restorer and an accompanying .css page at User:Aram/diff restorer.css. |
/**
* Description: Restore deleted lines easier while you are editing and viewing changes
* Documentation: [[User:Aram/diff restorer]]
* Version: 1.0.3
*/
'use strict';
mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Aram/diff_restorer.css&action=raw&ctype=text/css', 'text/css');
function getInsertPosition(element, content) {
const initialLineRow = element.closest('tr')
.prevAll('tr')
.find('td.diff-lineno:first');
if (!initialLineRow.length) return null;
const lineText = initialLineRow.text();
// Transform the text by replacing separators and converting digits
let normalizedText = lineText;
const separatorMap = mw.language.getSeparatorTransformTable();
Object.entries(separatorMap).forEach(([latin, local]) => {
normalizedText = normalizedText.replace(new RegExp(local, 'g'), latin);
});
const digitMap = mw.language.getDigitTransformTable();
digitMap.forEach((latin, index) => {
normalizedText = normalizedText.replace(new RegExp(latin, 'g'), index);
});
// Extract all numbers from the text
const numbers = normalizedText.match(/\d+/g);
if (!numbers || numbers.length === 0) return null;
// Use the last number when moving from bottom to top
const exactLineNumber = parseInt(numbers[numbers.length - 1]);
// Count non-deleted rows between current position and initial line
const prevRows = element.closest('tr')
.prevUntil(initialLineRow.closest('tr'))
.not(':has(td.diff-empty.diff-side-deleted)');
const lineNumber = exactLineNumber + prevRows.length;
const lines = content.split('\n');
// If line number exceeds content length, return end of content
if (lineNumber > lines.length) return content.length;
// Calculate insert position
return lines.slice(0, lineNumber - 1).join('\n').length + (lineNumber > 1 ? 1 : 0);
}
(function () {
function addRestoreButtons() {
$('table.diff tbody tr:not(.diff-title) .diff-side-deleted:not(.diff-context)').each(function () {
var deletedLine = $(this);
var addedLine = deletedLine.closest('tr').find('.diff-side-added');
var movedPara = deletedLine.closest('tr').find('.mw-diff-movedpara-left');
if (movedPara.length) {
var targetAnchor = movedPara.attr('href').substring(1);
addedLine = $('.diff-side-added').has('a[name="' + targetAnchor + '"]');
}
if (!addedLine.length) return;
var restoreBtn = $('<span>')
.addClass('restore-btn')
.attr('title', 'Restore this line')
.click(function () {
var editBox = $('#wpTextbox1');
if (!editBox.length || editBox.css('display') === 'none') {
mw.notify('Edit box not found. Please ensure you are in edit mode, or if you are, turn off code editor', { type: 'error', autoHide: false });
return;
}
var editContent = editBox.val();
var deletedText = deletedLine.text();
var success = false;
if (movedPara.length) {
var addedText = addedLine.text();
var currentPosition = editContent.indexOf(addedText);
if (currentPosition !== -1) {
var contentWithoutMoved = editContent.substring(0, currentPosition) +
editContent.substring(currentPosition + addedText.length +
(editContent[currentPosition + addedText.length] === '\n' ? 1 : 0));
var targetPosition = getInsertPosition(deletedLine, contentWithoutMoved);
if (targetPosition !== null) {
editBox.val(contentWithoutMoved.substring(0, targetPosition) +
deletedText + '\n' +
contentWithoutMoved.substring(targetPosition));
success = true;
}
}
} else if (addedLine.hasClass('diff-empty') || addedLine.find('br').length) {
var position = getInsertPosition(deletedLine, editContent);
if (position !== null) {
var newContent = addedLine.hasClass('diff-empty')
? editContent.substring(0, position) + deletedText + '\n' + editContent.substring(position)
: editContent.substring(0, position) + editContent.substring(position + 1);
editBox.val(newContent);
success = true;
}
} else if (editContent.includes(addedLine.text())) {
editBox.val(editContent.replace(addedLine.text(), deletedText));
success = true;
}
restoreBtn
.addClass(success ? 'restore-success' : 'restore-fail')
.off('click');
});
deletedLine.append(restoreBtn);
});
}
mw.hook('wikipage.diff').add(addRestoreButtons);
})();