User:Chlod/Scripts/DuplicatedRefs.js
Appearance
< User:Chlod | Scripts
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. |
Documentation for this user script can be added at User:Chlod/Scripts/DuplicatedRefs. |
// DuplicatedRefs
// Author: Chlod
// Version: 2.0.1
// Highlight equivalent references which are duplicated in the same paragraph.
// See [[Special:Diff/1141909502#Ref duplication highlight tool]].
mw.loader.using( [ 'mediawiki.util' ], function () {
mw.util.addCSS( '.reference-localduplicated { background-color: #ffff00; }' );
mw.util.addPortletLink(
'p-tb', 'javascript:void', 'Highlight duplicate refs', 't-dupref',
'Highlight all references in the same paragraph which are not interrupted by other references in sequence.'
).addEventListener( 'click', function ( e ) {
e.preventDefault();
let currentReferenceBundle = [];
let lastReferenceBundle = null;
let $lastGrandfatherElement = null;
let duplicatesFound = 0;
var references = document.querySelectorAll( '.mw-body-content .mw-parser-output .reference' );
for ( var item of references ) {
const $item = $( item );
const $grandfatherElement = $item.parents( '.mw-parser-output >' );
const referenceId = $item.find( 'a' ).attr( 'href' );
if ( referenceId == null ) {
// Invalid reference? We'll skip it.
continue;
}
if ( $lastGrandfatherElement == null ) {
// This is only the case if this is the very first reference in the article.
// We'll assign the reference ID as well and move on.
$lastGrandfatherElement = $grandfatherElement;
currentReferenceBundle.push( $item );
continue;
}
if ( !$lastGrandfatherElement.is( $grandfatherElement ) ) {
// Different paragraph/section. Snip bundle.
$lastGrandfatherElement = $grandfatherElement;
lastReferenceBundle = null;
currentReferenceBundle = [];
}
// Checks for the next sibling. The next sibling may be a text node or a DOM element.
const hasNextReference = item.nextSibling instanceof HTMLElement &&
item.nextSibling.classList.contains( 'reference' );
currentReferenceBundle.push( $item );
if ( !hasNextReference ) {
// Next sibling is not a reference. Snip bundle and compare.
// Get the reference IDs and see if they are the same on both bundles.
const bundleReferenceIds = new Set(
currentReferenceBundle.map(
e => e.find( 'a' ).attr( 'href' )
)
);
if ( lastReferenceBundle != null ) {
const lastBundleReferenceIds = new Set(
lastReferenceBundle.map(
e => e.find( 'a' ).attr( 'href' )
)
);
// Check if the two sets have the same values.
// Unordered, and repeating values discarded.
const sameIds = bundleReferenceIds.size == lastBundleReferenceIds.size &&
[...bundleReferenceIds].every( value => lastBundleReferenceIds.has( value ) );
if ( sameIds ) {
// Same IDs. Highlight both bundles.
currentReferenceBundle.forEach( e => e.addClass( 'reference-localduplicated' ) );
lastReferenceBundle.forEach( e => e.addClass( 'reference-localduplicated' ) );
duplicatesFound += currentReferenceBundle.length;
}
}
// Also check for repeated reference within the same bundle.
if ( bundleReferenceIds.size !== currentReferenceBundle.length ) {
// Different IDs, but there might still be repeating references in the same bundle.
const foundReferenceIds = new Set();
const foundDuplicateReferenceIds = new Set();
currentReferenceBundle.forEach( e => {
const foundReferenceId = e.find( 'a' ).attr( 'href' );
if ( foundReferenceIds.has( foundReferenceId ) ) {
// Duplicate found!
foundDuplicateReferenceIds.add( foundReferenceId );
// Afterwards, mark the ID as found (or do nothing if it's already in).
}
// else: no duplicate found, just add it in.
foundReferenceIds.add( foundReferenceId );
} );
currentReferenceBundle.forEach( e => {
if ( foundDuplicateReferenceIds.has( e.find( 'a' ).attr( 'href' ) ) ) {
e.addClass( 'reference-localduplicated' );
}
} );
duplicatesFound += foundDuplicateReferenceIds.size;
}
lastReferenceBundle = currentReferenceBundle;
currentReferenceBundle = [];
} else {
// Next sibling is a reference. Move on.
continue;
}
}
if ( duplicatesFound > 0 ) {
const duplicateExample =
'<span class="reference-localduplicated">Duplicated references are highlighted like this.</span>';
mw.notify(
$.parseHTML(
'Found ' + duplicatesFound + ' duplicate reference pairs in the same paragraph. '
+ duplicateExample
),
{ title: 'DuplicatedRefs' }
);
} else {
mw.notify(
$.parseHTML(
'<b>Congratulations!</b> No duplicate reference pairs within the same paragraph found.'
),
{ title: 'DuplicatedRefs' }
);
}
} );
} );