Content deleted Content added
Polygnotus (talk | contribs) No edit summary Tag: Reverted |
Polygnotus (talk | contribs) No edit summary Tag: Reverted |
||
Line 28:
function addDuplicateCitationsTemplate() {
var pageTitle = mw.config.get('wgPageName');
let duplicateInfo = getDuplicateInfo();
api.get({
action: 'query',
prop: 'revisions',
titles: pageTitle,
rvprop: 'content',
rvslots: 'main',
formatversion: 2
}).then(function(data) {
var page = data.query.pages[0];
var content = page.revisions[0].slots.main.content;
// Add the template at the top of the page
var newContent = '{{Duplicate citations}}\n' + content;
// Create the edit summary
let summary = '+{{Duplicate citations}}';
if (duplicateInfo.length > 0) {
summary += ': ';
duplicateInfo.forEach((info, index) => {
summary += `${info.url} (refs: ${info.refs.join(', ')})`;
if (index < duplicateInfo.length - 1) {
summary += '; ';
}
});
}
// Make the edit
return api.postWithToken('csrf', {
action: 'edit',
title: pageTitle,
text: newContent,
summary: summary
});
}).then(function() {
mw.notify('Successfully added the Duplicate citations template!');
// Reload the page to show the changes
___location.reload();
}).catch(function(error) {
console.error('Error:', error);
mw.notify('Failed to add the template. See console for details.', {type: 'error'});
});
}
function getDuplicateInfo() {
const referenceSpans = document.querySelectorAll('span.reference-text');
const urlMap = new Map();
const duplicates = [];
referenceSpans.forEach((span) => {
const links = span.querySelectorAll('a');
const refNumber = span.closest('li')?.id.split('-').pop() || 'Unknown';
let validLink = null;
for (let link of links) {
const url = link.href;
const linkText = link.textContent.trim();
if (
linkText !== "Archived" &&
(!url.includes("wikipedia.org/wiki/") || url.includes("Special:BookSources")) &&
!url.includes("_(identifier)")
) {
validLink = link;
break;
}
}
if (validLink) {
const url = validLink.href;
if (urlMap.has(url)) {
urlMap.get(url).push(refNumber);
} else {
urlMap.set(url, [refNumber]);
}
}
});
urlMap.forEach((refs, url) => {
if (refs.length > 1) {
duplicates.push({ url, refs });
}
});
return duplicates;
}
function calculateSimilarity(str1, str2) {
const shorter = str1.length > str2.length ? str2 : str1;
const longerLength = longer.length;
if (longerLength === 0) {
return 1.0;
}
return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);
}
function editDistance(s1, s2) {
s2 = s2.toLowerCase();
const costs = new Array();
for (let i = 0; i <= s1.length; i++) {
let lastValue = i;
for (let j = 0; j <= s2.length; j++) {
if (i == 0)
costs[j] = j;
else {
if (j > 0) {
let newValue = costs[j - 1];
if (s1.charAt(i - 1) != s2.charAt(j - 1))
newValue = Math.min(Math.min(newValue, lastValue),
costs[j]) + 1;
costs[j - 1] = lastValue;
lastValue = newValue;
}
}
}
if (i > 0)
costs[s2.length] = lastValue;
}
return costs[s2.length];
}
function getAllVisibleText(element) {
return element.textContent.trim();
}
if (element.nodeType === Node.ELEMENT_NODE) {
if (window.getComputedStyle(element).display === 'none') {
return '';
}
return Array.from(element.childNodes)
.map(child => getAllVisibleText(child))
.join(' ')
.replace(/\s+/g, ' ')
.trim();
}
return '';
}
function extractVisibleText(htmlString) {
const doc = parser.parseFromString(htmlString, 'text/html');
return getAllVisibleText(doc.body);
}
Line 73 ⟶ 204:
referenceSpans.forEach((span) => {
const citeNote = span.closest('li')?.id;
const refNumber = citeNoteMap.get(citeNote) || 'Unknown';
const refText = extractVisibleText(span.outerHTML); // Extract visible text content
let validLink = null;
for (let link of links) {
const url = link.href;
const linkText = link.textContent.trim();
if (
linkText !== "Archived" &&
(!url.includes("wikipedia.org/wiki/") || url.includes("Special:BookSources")) &&
!url.includes("_(identifier)")
) {
validLink = link;
break;
}
}
if (validLink) {
const url = validLink.href;
totalLinks++;
if (urlMap.has(url)) {
if (duplicates.has(url)) {
duplicates.get(url).push({ refNumber, citeNote, refText });
} else {
duplicates.set(url, [urlMap.get(url), { refNumber, citeNote, refText }]);
}
} else {
urlMap.set(url, { refNumber, citeNote, refText });
}
// Check for similar content
let isDuplicate = false;
for (let [content, info] of contentMap) {
if (calculateSimilarity(refText, content) > 0.9) { // 90% similarity threshold
info.push({ refNumber, citeNote, refText, url });
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
contentMap.set(refText, [{ refNumber, citeNote, refText, url }]);
}
}
});
if (duplicates.size > 0 || Array.from(contentMap.values()).some(group => group.length > 1)) {
if (document.querySelector('table.box-Duplicated_citations') === null) {
const editSections = parentDiv.querySelectorAll('span.mw-editsection');
editSections.forEach(editSection => {
let spanBefore = document.createElement('span');
spanBefore.className = 'mw-editsection-bracket';
spanBefore.textContent = '[';
let addTemplateLink = document.createElement('a');
addTemplateLink.textContent = ' add {{duplicated citations}} ';
addTemplateLink.href = '#';
addTemplateLink.addEventListener('click', function(e) {
e.preventDefault();
addDuplicateCitationsTemplate();
});
let spanAfter = document.createElement('span');
spanAfter.className = 'mw-editsection-bracket';
spanAfter.textContent = ']';
editSection.appendChild(spanBefore);
editSection.appendChild(addTemplateLink);
editSection.appendChild(spanAfter);
});
}
Line 161 ⟶ 359:
function displayDuplicateInfo(duplicates, contentMap) {
let paragraphInfo = document.createElement('span');
let urlLink = document.createElement('a');
urlLink.href = url;
urlLink.textContent = url;
urlLink.target = "_blank";
urlLink.rel = "noopener noreferrer";
paragraphInfo.appendChild(document.createTextNode('Duplicate URL: '));
paragraphInfo.appendChild(urlLink);
paragraphInfo.appendChild(document.createTextNode(' in refs: '));
refInfo.forEach((ref, index) => {
if (ref.citeNote) {
let link = document.createElement('a');
link.href = `#${ref.citeNote}`;
link.textContent = ref.refNumber;
paragraphInfo.appendChild(link);
// Highlight only the specific duplicates on hover
link.addEventListener('mouseover', () => {
refInfo.forEach(duplicate => {
const citationElement = document.getElementById(duplicate.citeNote);
if (citationElement) {
if (duplicate.citeNote === ref.citeNote) {
citationElement.classList.add('duplicate-citation-hover');
} else {
citationElement.classList.add('duplicate-citation-highlight');
}
}
});
});
link.addEventListener('mouseout', () => {
refInfo.forEach(duplicate => {
const citationElement = document.getElementById(duplicate.citeNote);
if (citationElement) {
citationElement.classList.remove('duplicate-citation-hover');
citationElement.classList.remove('duplicate-citation-highlight');
}
});
});
// Highlight duplicates on click and allow navigation
link.addEventListener('click', () => {
// Remove previous click highlights
document.querySelectorAll('.duplicate-citation-clicked').forEach(el => {
el.classList.remove('duplicate-citation-clicked');
});
// Add new click highlights
refInfo.forEach(duplicate => {
const citationElement = document.getElementById(duplicate.citeNote);
if (citationElement) {
citationElement.classList.add('duplicate-citation-clicked');
}
});
// The default behavior (navigation) will now occur
});
} else {
paragraphInfo.appendChild(document.createTextNode(ref.refNumber));
}
// Calculate similarity with the next reference
if (index < refInfo.length - 1) {
const similarity = calculateSimilarity(ref.refText, refInfo[index + 1].refText);
const similarityPercentage = Math.round(similarity * 100);
paragraphInfo.appendChild(document.createTextNode(` (${similarityPercentage}%)`));
}
if (index < refInfo.length - 1) {
paragraphInfo.appendChild(document.createTextNode(', '));
}
});
paragraphInfo.appendChild(document.createElement('br'));
newParagraph.appendChild(paragraphInfo);
});
// Add information about similar content duplicates
contentMap.forEach((group, content) => {
if (group.length > 1) {
let paragraphInfo = document.createElement('span');
paragraphInfo.appendChild(document.createTextNode('Similar content in refs: '));
group.forEach((ref, index) => {
if (ref.citeNote) {
let link = document.createElement('a');
link.href = `#${ref.citeNote}`;
link.textContent = ref.refNumber;
paragraphInfo.appendChild(link);
// Highlight similar content on hover and click (same as URL duplicates)
link.addEventListener('mouseover', () => {
group.forEach(duplicate => {
const citationElement = document.getElementById(duplicate.citeNote);
if (citationElement) {
if (duplicate.citeNote === ref.citeNote) {
citationElement.classList.add('duplicate-citation-hover');
} else {
citationElement.classList.add('duplicate-citation-highlight');
}
}
});
});
link.addEventListener('mouseout', () => {
group.forEach(duplicate => {
const citationElement = document.getElementById(duplicate.citeNote);
if (citationElement) {
citationElement.classList.remove('duplicate-citation-hover');
citationElement.classList.remove('duplicate-citation-highlight');
}
});
});
link.addEventListener('click', () => {
document.querySelectorAll('.duplicate-citation-clicked').forEach(el => {
el.classList.remove('duplicate-citation-clicked');
});
group.forEach(duplicate => {
const citationElement = document.getElementById(duplicate.citeNote);
if (citationElement) {
citationElement.classList.add('duplicate-citation-clicked');
}
});
});
} else {
paragraphInfo.appendChild(document.createTextNode(ref.refNumber));
}
if (index < group.length - 1) {
paragraphInfo.appendChild(document.createTextNode(', '));
}
});
paragraphInfo.appendChild(document.createElement('br'));
newParagraph.appendChild(paragraphInfo);
}
});
}
|