User:Zackmann08/scripts/HoldingCellHelper.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.
//<nowiki>

mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Zackmann08/scripts/HoldingCellHelper.js/style.css&action=raw&ctype=text/css', 'text/css')

jQuery(document).ready(function() {
	if (mw.config.get('wgPageName') == 'User:Zackmann08/holding' || mw.config.get('wgPageName') == 'Wikipedia:Templates_for_discussion/Holding_cell') {	
		// if (mw.user.getName() != 'Zackmann08') {
		// 	alert("HoldingCellHelper.js currently only works for its creator, Zackmann08 as I am working on an update. Please check back in a bit.")
		// 	return; 
		// }
		document.querySelectorAll('.Tfdl').forEach(span => {
			// get the ID of the current span (falls back to 'No ID' if empty)
			const idText = span.id || 'No ID';

			// insert a checkbox before each tfdl
			span.parentElement.insertAdjacentHTML('afterBegin', `<input type="checkbox" id="hch_${idText}" class='hch'>`);
			
			// find the last span containing the links
			const end = span.nextElementSibling.nextElementSibling.nextElementSibling;
  
			// if the <span> exists, insert the links at the end
			if (end) {
				var newhtml = `<span class='hch'> [ <a href="javascript:markForDeletion('${RegExp.escape(idText)}')">Del</a> ] [ <a href="javascript:markForDeletion('${RegExp.escape(idText)}', true)">RFD</a> ] </span>`
				end.insertAdjacentHTML('afterend', newhtml);
			}
		});
		document.querySelectorAll('.mw-heading').forEach(div => {
			var heading = div.querySelectorAll('h3, h4')[0].innerText
			if (heading == 'Closing discussions' || heading == 'Tools' || heading == 'Ready for deletion') { 
				return; 
			}
			// var newhtml = ` [<a href="javascript:blankSection('${heading}')">Blank section</a>] `;
			var newhtml = ' <span class="hch">[<a href="javascript:deleteChecked()">Del Checked</a>] ';
			newhtml += ' [<a href="javascript:deleteChecked(true)">RFD Checked</a>]</span> ';
			div.insertAdjacentHTML('beforeend', newhtml);
		});
		document.getElementById('Ready_for_deletion').parentElement.insertAdjacentHTML('beforeend', '  <span class="hch">[<a href="javascript:blankSection()">Blank section</a>]</span> ');
	}
});

async function blankSection() {
	const result = await confirmAction("Are you sure you want to blank the 'Ready for deletion' section?");
	if (!result) {
		mw.notify( "Aborting", {type: 'error'} );
		return;
	}
	var regex = new RegExp(/===\s*Ready for deletion\s*===[\w\W]*/)
	var replacement = "===Ready for deletion===\n{{anchor|d}}\nTemplates for which consensus to delete has been reached, and for which orphaning has been completed, can be listed here for an administrator to delete. Remove from this list when an item has been deleted.\n<!-- list begin (leave this here); use the format:\n* {{tfdl|template name|log date (YYYY Month D)|delete=1}}\nIf empty, add * ''None currently'' below this comment. -->\n\n* ''None currently''"
	mw.loader.using( "mediawiki.api", function () {
		var api = new mw.Api();
        api.get( {
            prop: 'revisions',
            rvprop: 'content',
            rvlimit: 1,
            titles: mw.config.get( "wgPageName" )
        } ).done( function ( data ) {
            if ( !data.query || !data.query.pages ) return;
            var pageid = Object.getOwnPropertyNames( data.query.pages )[0],
                text = data.query.pages[pageid].revisions[0]["*"];
            var new_text = text.replace(regex, replacement)
			if (new_text == text) {
				mw.notify( "Nothing to blank. Aborting", {type: 'error'} );
				return;
			}
            api.postWithEditToken( {
                action: "edit",
                title: mw.config.get( "wgPageName" ),
                summary: '/* Ready for deletion */ blanking section using [[User:Zackmann08/scripts/HoldingCellHelper]] - all templates/modules resolved',
                text: new_text
            } ).done ( function ( data ) {
                if ( data && data.edit && data.edit.result && data.edit.result == 'Success' ) {
                    mw.notify( "Section blanked!\nReloading in 2 seconds...", {type: 'success'} );
                    window.setTimeout( function () {
                        document.location.reload( true );
                    }, 2000 );
                }
            } );
        } );
    } );
}

async function confirmAction(message = 'Are you sure you want to do this?') {
	// 1. Setup the dialog and manager
	var messageDialog = new OO.ui.MessageDialog();
	var windowManager = new OO.ui.WindowManager();
	$( document.body ).append( windowManager.$element );
	windowManager.addWindows( [ messageDialog ] );
	
	// 2. Open the dialog with specific action buttons
	var instance = windowManager.openWindow( messageDialog, {
		title: 'Confirm Action',
		message: message,
		actions: [
			{ action: 'accept', label: 'Yes', flags: 'destructive' },
			{ action: 'reject', label: 'No', flags: 'safe' }
		]
	});
	function checkUserAction() {
		return new Promise((resolve) => {
			instance.closing.done(function (data) {
				if (data && data.action === 'accept') {
					resolve(true); 
				} else {
					resolve(false); 
				}
			});
		});
	}
	const result = await checkUserAction();
	return result;
}

async function markForDeletion(link, rfd = false){
	var message = rfd ? `Are you sure you want to mark ${link} for deletion?` : `Are you sure you want to DELETE ${link} from the page?`
	const result = await confirmAction(message);
	if (!result) {
		mw.notify( "Aborting", {type: 'error'} );
		return;
	}
	var notification = rfd ? `Marking for deletion: ${link}` : `Deleting: ${link}`;
	mw.notify(notification, {type: 'notice'});
	mw.loader.using( "mediawiki.api", function () {
		var api = new mw.Api();
        api.get( {
            prop: 'revisions',
            rvprop: 'content',
            rvlimit: 1,
            titles: mw.config.get( "wgPageName" )
        } ).done( function ( data ) {
            if ( !data.query || !data.query.pages ) return;
            var pageid = Object.getOwnPropertyNames( data.query.pages )[0],
                text = data.query.pages[pageid].revisions[0]["*"];
            var [new_text, heading] = parseHoldingCell(text, link, rfd);

			var summary = rfd ? `/* ${heading} */ marking ready for deletion using [[User:Zackmann08/scripts/HoldingCellHelper]]` : `/* ${heading} */ template done. Removing using [[User:Zackmann08/scripts/HoldingCellHelper]]`
            api.postWithEditToken( {
                action: "edit",
                title: mw.config.get( "wgPageName" ),
                summary: summary,
                text: new_text
            } ).done ( function ( data ) {
                if ( data && data.edit && data.edit.result && data.edit.result == 'Success' ) {
					var notification = rfd ? "Template moved to 'ready for deletion'" : "Template removed from page"
                    mw.notify( notification + "\nReloading in 2 seconds...", {type: 'success'} );
                    window.setTimeout( function () {
                        document.location.reload( true );
                    }, 2000 );
                }
            } );
        } );
    } );
}

function parseHoldingCell(text, template, rfd){
	var regex, tfdl, full_discussion;
	var original_text = text;
	if (template.match(/^Module:/)) {
		regex = new RegExp(`(\\*\\s*\\{\\{tfdl\\|${RegExp.escape(template.replace(/^Module:/, ""))}.*\\|ns=Module.*)\\n[\\W\\w]*?(?=(\\*\\s*\\{\\{tfdl|===))`);
		try {
			[full_discussion, tfdl] = text.match(regex)
		} catch (error) {
			mw.notify( "ERROR: could not find module match. " + error.message, {type: 'error'} );
			return;
		} 
	} else if (template.match(/^Template:/)) {
		regex = new RegExp(`(\\*\\s*\\{\\{tfdl\\|${RegExp.escape(template.replace(/^Template:/, ""))}.*)\\n[\\W\\w]*?(?=(\\*\\s*\\{\\{tfdl|===))`);
		try {
			[full_discussion, tfdl] = text.match(regex)
		} catch (error) {
			mw.notify( "ERROR: could not find template match. " + error.message, {type: 'error'} );
			return;
		} 
	} else {
		mw.notify( "ERROR: Unparsable tfdl type. Aborting", {type: 'error'} );
		return;
	}

	// test if tfdl is last in the section
	if (text.match(new RegExp(`\\-\\->\\s*\\n*${RegExp.escape(tfdl)}\\n*\\=\\=`))) {
		text = text.replace(full_discussion,"* ''None currently''\n\n");
	} else {
		// simply remove the tfdl from the page
		text = text.replace(full_discussion,'');
	}
	
	regex = new RegExp(`={3,4}([\\w\\s]+)={3,4}(?:(?!={3,4}([\\w\\s]+)={3,4})[\\s\\S])*?(\\*\\s*\\{\\{tfdl\\|${RegExp.escape(template.replace(/^(Module|Template):/, ""))})`)

	// find the name of the heading that this tfdl is under
	var heading;
	try {
		heading = original_text.match(regex)[1]
	} catch {
		mw.notify( "ERROR: Unable to get heading name", {type: 'error'} );
	}
	
	// If not marking for deletion, then return
	if (!rfd) {
		return [text, heading];
	}
	
	// test if nothing currently listed for deletion
	text = text.replace(/\n?\*\s*''\s*None currently\s*''\s*$\n*/, '')

	// change the TFDL to include '|delete=1' at the end
	tfdl = tfdl.replace(/\}\}/, '|delete=1}}')
	
	// insert the tfdl at the bottom of the page
	text = text + '\n' + tfdl
	
	return [text, heading];
}

async function deleteChecked(rfd = false){
	const checked = document.querySelectorAll('input.hch:checked');
	if (checked.length === 0 ){
		mw.notify( "ERROR: Nothing checked.", {type: 'error'} );
		return;
	}
	var message = rfd ? `Are you sure you want to mark all checked tfdls for deletion?` : `Are you sure you want to DELETE all checked tfdls from the page?`
	const result = await confirmAction(message);
	if (!result) {
		mw.notify( "Aborting", {type: 'error'} );
		return;
	}
	var notification = rfd ? 'Marking for deletion' : 'Deleting';
	mw.notify(notification, {type: 'notice'});
	mw.loader.using( "mediawiki.api", function () {
		var api = new mw.Api();
        api.get( {
            prop: 'revisions',
            rvprop: 'content',
            rvlimit: 1,
            titles: mw.config.get( "wgPageName" )
        } ).done( function ( data ) {
            if ( !data.query || !data.query.pages ) return;
            var pageid = Object.getOwnPropertyNames( data.query.pages )[0],
                text = data.query.pages[pageid].revisions[0]["*"];
			var heading;
			checked.forEach(input => {
				[text,heading] = parseHoldingCell(text, input.id.replace(/hch_/, ''), rfd);
			});
			
			var summary = rfd ? `/* ${heading} */ marking ${checked.length} tfdls ready for deletion using [[User:Zackmann08/scripts/HoldingCellHelper]]` : `/* ${heading} */ ${checked.length} tfdls done. Removing using [[User:Zackmann08/scripts/HoldingCellHelper]]`
           api.postWithEditToken( {
                action: "edit",
                title: mw.config.get( "wgPageName" ),
                summary: summary,
                text: text
            } ).done ( function ( data ) {
                if ( data && data.edit && data.edit.result && data.edit.result == 'Success' ) {
					var notification = rfd ? "Tfdls moved to 'ready for deletion'" : "Tfdls removed from page"
                    mw.notify( notification + "\nReloading in 2 seconds...", {type: 'success'} );
                    window.setTimeout( function () {
                        document.location.reload( true );
                    }, 2000 );
                }
            });
        });
    });
}

//</nowiki>