Skip to content

Commit

Permalink
unblock-review: fix linter errors, refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
NovemLinguae committed Oct 31, 2024
1 parent 2d7685a commit fb7ff29
Showing 1 changed file with 152 additions and 141 deletions.
293 changes: 152 additions & 141 deletions unblock-review/unblock-review.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
// Fork of https://en.wikipedia.org/w/index.php?title=User:Enterprisey/unblock-review.js&oldid=1073685522
/*
Fork of https://en.wikipedia.org/w/index.php?title=User:Enterprisey/unblock-review.js&oldid=1073685522
TODO:
- get rid of goto jump (matchLoop label)
*/
/* global importStylesheet */

// <nowiki>
( function () {
const UNBLOCK_REQ_COLOR = 'rgb(235, 244, 255)';
const SIGNATURE = '~~' + '~~';
const DECLINE_REASON_HERE = '{' + '{subst:Decline reason here}}'; // broken up to prevent processing
const SIGNATURE = '~~~~';
const DECLINE_REASON_HERE = '{{subst:Decline reason here}}';
const ADVERT = ' ([[User:Enterprisey/unblock-review|unblock-review]])';

// Making this a function for unit test reasons.
function getInitialText( wikitext, appealReason ) {
// https://stackoverflow.com/a/6969486/3480193
function escapeRegExp( string ) {
return string.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ); // $& means the whole matched string
// $& means the whole matched string
return string.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
}

const regEx = new RegExp( escapeRegExp( appealReason ), 'g' );
Expand All @@ -32,15 +40,16 @@
}

let i = 0;
while ( wikitext[ startIdx ] != '{' && i < 50 ) {
while ( wikitext[ startIdx ] !== '{' && i < 50 ) {
startIdx--;
i++;
}
if ( i == 50 ) {
if ( i === 50 ) {
continue;
}

startIdx--; // templates start with two opening curly braces
// templates start with two opening curly braces
startIdx--;

const initialText = wikitext.substring( startIdx, textIdx );
return initialText;
Expand All @@ -49,152 +58,154 @@
throw new Error( 'Searching backwards failed!' );
}

if ( mw.config.get( 'wgNamespaceNumber' ) === 3 ) {
/**
* Is there a signature (four tildes) present in the given text,
* outside of a nowiki element?
*/
function hasSig( text ) {
// no literal signature?
if ( text.indexOf( SIGNATURE ) < 0 ) {
return false;
}
/**
* Is there a signature (four tildes) present in the given text,
* outside of a nowiki element?
*/
function hasSig( text ) {
// no literal signature?
if ( text.indexOf( SIGNATURE ) < 0 ) {
return false;
}

// if there's a literal signature and no nowiki elements,
// there must be a real signature
if ( text.indexOf( '<nowiki>' ) < 0 ) {
return true;
// if there's a literal signature and no nowiki elements,
// there must be a real signature
if ( text.indexOf( '<nowiki>' ) < 0 ) {
return true;
}

// Save all nowiki spans
const nowikiSpanStarts = []; // list of ignored span beginnings
const nowikiSpanLengths = []; // list of ignored span lengths
const NOWIKI_RE = /<nowiki>.*?<\/nowiki>/g;
let spanMatch;
do {
spanMatch = NOWIKI_RE.exec( text );
if ( spanMatch ) {
nowikiSpanStarts.push( spanMatch.index );
nowikiSpanLengths.push( spanMatch[ 0 ].length );
}
} while ( spanMatch );

// So that we don't check every ignore span every time
let nowikiSpanStartIdx = 0;

const SIG_RE = new RegExp( SIGNATURE, 'g' );
let sigMatch;

matchLoop:
do {
sigMatch = SIG_RE.exec( text );
if ( sigMatch ) {
// Check that we're not inside a nowiki
for ( let nwIdx = nowikiSpanStartIdx; nwIdx <
nowikiSpanStarts.length; nwIdx++ ) {
if ( sigMatch.index > nowikiSpanStarts[ nwIdx ] ) {
if ( sigMatch.index + sigMatch[ 0 ].length <=
nowikiSpanStarts[ nwIdx ] + nowikiSpanLengths[ nwIdx ] ) {

// Invalid sig
continue matchLoop;
} else {

// Save all nowiki spans
const nowikiSpanStarts = []; // list of ignored span beginnings
const nowikiSpanLengths = []; // list of ignored span lengths
const NOWIKI_RE = /<nowiki>.*?<\/nowiki>/g;
let spanMatch;
do {
spanMatch = NOWIKI_RE.exec( text );
if ( spanMatch ) {
nowikiSpanStarts.push( spanMatch.index );
nowikiSpanLengths.push( spanMatch[ 0 ].length );
}
} while ( spanMatch );

// So that we don't check every ignore span every time
let nowikiSpanStartIdx = 0;

const SIG_RE = new RegExp( SIGNATURE, 'g' );
let sigMatch;

matchLoop:
do {
sigMatch = SIG_RE.exec( text );
if ( sigMatch ) {
// Check that we're not inside a nowiki
for ( let nwIdx = nowikiSpanStartIdx; nwIdx <
nowikiSpanStarts.length; nwIdx++ ) {
if ( sigMatch.index > nowikiSpanStarts[ nwIdx ] ) {
if ( sigMatch.index + sigMatch[ 0 ].length <=
nowikiSpanStarts[ nwIdx ] + nowikiSpanLengths[ nwIdx ] ) {

// Invalid sig
continue matchLoop;
} else {

// We'll never encounter this span again, since
// headers only get later and later in the wikitext
nowikiSpanStartIdx = nwIdx;
}
// We'll never encounter this span again, since
// headers only get later and later in the wikitext
nowikiSpanStartIdx = nwIdx;
}
}

// We aren't inside a nowiki
return true;
}
} while ( sigMatch );
return false;
}

/**
* Given the div of an unblock request, set up the UI and event
* listeners.
*/
function setUpUi( unblockDiv ) {
const container = document.createElement( 'table' );
container.className = 'unblock-review';
const hrEl = unblockDiv.querySelector( 'hr' );
container.innerHTML = "<tr><td class='reason-container' rowspan='2'>" +
"<textarea class='unblock-review-reason mw-ui-input'" +
" placeholder='Reason for accepting/declining here'>" + DECLINE_REASON_HERE + '</textarea></td>' +
"<td><button class='unblock-review-accept mw-ui-button mw-ui-progressive'>Accept</button></td></tr>" +
"<tr><td><button class='unblock-review-decline mw-ui-button mw-ui-destructive'>Decline</button></td></tr>";
unblockDiv.insertBefore( container, hrEl.previousElementSibling );
const reasonArea = container.querySelector( 'textarea' );
$( container ).find( 'button' ).click( function () {
const action = $( this ).text().toLowerCase();
const appealReason = hrEl.nextElementSibling.nextElementSibling.childNodes[ 0 ].textContent;
$.getJSON(
mw.util.wikiScript( 'api' ),
{
format: 'json',
action: 'query',
prop: 'revisions',
rvprop: 'content',
rvlimit: 1,
titles: mw.config.get( 'wgPageName' )
}
).done( ( data ) => {
// Extract wikitext from API response
const pageId = Object.keys( data.query.pages )[ 0 ];
wikitext = data.query.pages[ pageId ].revisions[ 0 ][ '*' ];

const initialText = getInitialText( wikitext, appealReason );

// Build accept/decline reason
let reason = reasonArea.value;
if ( !reason.trim() ) {
reason = DECLINE_REASON_HERE + ' ' + SIGNATURE;
} else if ( !hasSig( reason ) ) {
reason = reason + ' ' + SIGNATURE;
// We aren't inside a nowiki
return true;
}
} while ( sigMatch );
return false;
}

/**
* Given the div of an unblock request, set up the UI and event listeners.
*/
function setUpUi( unblockDiv ) {
const container = document.createElement( 'table' );
container.className = 'unblock-review';
const hrEl = unblockDiv.querySelector( 'hr' );
container.innerHTML = "<tr><td class='reason-container' rowspan='2'>" +
"<textarea class='unblock-review-reason mw-ui-input'" +
" placeholder='Reason for accepting/declining here'>" + DECLINE_REASON_HERE + '</textarea></td>' +
"<td><button class='unblock-review-accept mw-ui-button mw-ui-progressive'>Accept</button></td></tr>" +
"<tr><td><button class='unblock-review-decline mw-ui-button mw-ui-destructive'>Decline</button></td></tr>";
unblockDiv.insertBefore( container, hrEl.previousElementSibling );
const reasonArea = container.querySelector( 'textarea' );
$( container ).find( 'button' ).on( 'click', function () {
const action = $( this ).text().toLowerCase();
const appealReason = hrEl.nextElementSibling.nextElementSibling.childNodes[ 0 ].textContent;
$.getJSON(
mw.util.wikiScript( 'api' ),
{
format: 'json',
action: 'query',
prop: 'revisions',
rvprop: 'content',
rvlimit: 1,
titles: mw.config.get( 'wgPageName' )
}
).done( ( data ) => {
// Extract wikitext from API response
const pageId = Object.keys( data.query.pages )[ 0 ];
let wikitext = data.query.pages[ pageId ].revisions[ 0 ][ '*' ];

const initialText = getInitialText( wikitext, appealReason );

// Build accept/decline reason
let reason = reasonArea.value;
if ( !reason.trim() ) {
reason = DECLINE_REASON_HERE + ' ' + SIGNATURE;
} else if ( !hasSig( reason ) ) {
reason = reason + ' ' + SIGNATURE;
}
wikitext = wikitext.replace( initialText + appealReason, '{' +
'{unblock reviewed|' + action + '=' + reason + '|1=' + appealReason );

const summary = ( action === 'accept' ? 'Accepting' : 'Declining' ) +
' unblock request' + ADVERT;

( new mw.Api() ).postWithToken( 'csrf', {
action: 'edit',
title: mw.config.get( 'wgPageName' ),
summary: summary,
text: wikitext
} ).done( ( data ) => {
if ( data && data.edit && data.edit.result && data.edit.result === 'Success' ) {
window.location.reload( true );
} else {
console.log( data );
}
wikitext = wikitext.replace( initialText + appealReason, '{' +
'{unblock reviewed|' + action + '=' + reason + '|1=' + appealReason );

const summary = ( action === 'accept' ? 'Accepting' : 'Declining' ) +
' unblock request' + ADVERT;

( new mw.Api() ).postWithToken( 'csrf', {
action: 'edit',
title: mw.config.get( 'wgPageName' ),
summary: summary,
text: wikitext
} ).done( ( data ) => {
if ( data && data.edit && data.edit.result && data.edit.result == 'Success' ) {
window.location.reload( true );
} else {
console.log( data );
}
} );
} );
} );
}
} );
}

$.when( $.ready, mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ) ).then( () => {
mw.util.addCSS(
'.unblock-review td { padding: 0 }' +
'td.reason-container { padding-right: 1em; width: 30em }' +
'.unblock-review-reason { height: 5em }' );
importStylesheet( 'User:Enterprisey/mw-ui-button.css' );
importStylesheet( 'User:Enterprisey/mw-ui-input.css' );
const userBlockBoxes = document.querySelectorAll( 'div.user-block' );
for ( let i = 0, n = userBlockBoxes.length; i < n; i++ ) {
if ( userBlockBoxes[ i ].style[ 'background-color' ] !== UNBLOCK_REQ_COLOR ) {
continue;
}
const userTalkNamespace = 3;
if ( mw.config.get( 'wgNamespaceNumber' ) !== userTalkNamespace ) {
return;
}

// We now have a pending unblock request - add UI
setUpUi( userBlockBoxes[ i ] );
$.when( $.ready, mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ) ).then( () => {
mw.util.addCSS(
'.unblock-review td { padding: 0 }' +
'td.reason-container { padding-right: 1em; width: 30em }' +
'.unblock-review-reason { height: 5em }' );
importStylesheet( 'User:Enterprisey/mw-ui-button.css' );
importStylesheet( 'User:Enterprisey/mw-ui-input.css' );
const userBlockBoxes = document.querySelectorAll( 'div.user-block' );
for ( let i = 0, n = userBlockBoxes.length; i < n; i++ ) {
if ( userBlockBoxes[ i ].style[ 'background-color' ] !== UNBLOCK_REQ_COLOR ) {
continue;
}
} );
}

// We now have a pending unblock request - add UI
setUpUi( userBlockBoxes[ i ] );
}
} );
}() );
// </nowiki>

0 comments on commit fb7ff29

Please sign in to comment.