Jump to content

User:Zackmann08/scripts/indent.js

From Wikipedia, the free encyclopedia
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.
;
// code copied from [[User:Frietjes/indent.js]]";

importScript('User:Zackmann08/scripts/wait.js');
const MAX_PARAM_LENGTH = 25
	
var long_labels = [ ];

// -------------------------------------------------------------------------------- //
function wpIndent(s)
{
	var alignflag = 0;
	// Mangle file/image link
	while(s.search(/(\[\[[\t _]*(?:image|file)[\t _]*:[^\[\]=]*)=/gi) >=0 ) {
		s = s.replace(/(\[\[[\t _]*(?:image|file)[\t _]*:[^\[\]=]*)=/gi, '$1<!-- FIX EQ MARKER -->');
	}
	// Mangle html tags
	while(s.search(/(<[\t \r\n]*[a-z]+[^<>]*)=/gi) >=0 ) {
		s = s.replace(/(<[\t \r\n]*[a-z]+[^<>]*)=/gi, '$1<!-- FIX EQ MARKER -->');
	}
	// Election box
	s = s.replace(/(\{\{[_ ]*[Ee]lection[_ ]*box[^{}\|\r\n]*[a-z])[\t ]*\|[\t ]*([\r\n][\t ]*\|)/g, '$1$2');
	// Mark some spacing
	s = s.replace(/(= ) (\|)/g, '$1<!-- SPACE MARKER -->$2');
	
	// Some fixes for the case where infoboxes have the pipes at the end of the line
	var old = '';
	while(s != old) {
		old = s;
		s = s.replace(/(\{\{[_\t\r\n ]*(?:[Ii]nfobox|[Ss]peciesbox|[Ff]ootball[_ ]*box)(?:\{\{(?:\{\{(?:\{\{[^{}]*\}\}|[^{}])*\}\}|[^{}])*\}\}|[^{}])*)(\|)[\t ]*([\r\n])/gim, '$1$3$2');
	}
	s = s.replace(/(\{\{[_\t\r\n ]*(?:[Ii]nfobox|[Ss]peciesbox|[Ff]ootball[_ ]*box)(?:\{\{(?:\{\{(?:\{\{[^{}]*\}\}|[^{}])*\}\}|[^{}])*\}\}|[^{}])*)[\t\r\n ]*\|[\t ]*([\r\n][\t ]*\}\})/gi, '$1$2');
	s = s.replace(/(\{\{[_\t\r\n ]*(?:[Ii]nfobox|[Ss]peciesbox|[Ff]ootball[_ ]*box)(?:\{\{(?:\{\{(?:\{\{[^{}]*\}\}|[^{}])*\}\}|[^{}])*\}\}|[^{}])*[\r\n])[\t\r\n ]*\|[\t ]*(\}\})/gi, '$1$2');
	
	// Space before end of line
	while(s.search(/([^=])[\t ]+[\r\n]/g) >= 0) {
		s = s.replace(/([^=])[\t ]+([\r\n])/g, '$1$2');
	}
	// Unroll infobox
	if(s.search(/\{\{[_\t\r\n ]*(?:[Ii]nfobox|[Ss]peciesbox|[Ff]ootball[_ ]*box)/g) >=0 ) {
		s = s.replace(/([^\t \r\n])([\t ]*\|[\t ]*[^=\|\[\]\{\]\t ][^=\|\[\]\{\]]*=)/g, '$1\n$2');
		s = s.replace(/(=)([\t ]*\|[\t ]*[^=\|\[\]\{\]\t ][^=\|\[\]\{\]]*=)/g, '$1\n$2');
		s = s.replace(/([^\t \r\n])[\t \r\n]*(\}\})/g, '$1\n$2'); // put trailing braces on a newline
	}

	// Add some markers
	s = s.replace(/(\|[ ]*(?:[a-z_ ]+)[\t ]*=[^\r\n]*(?:[\r\n][\t ]*♦§[0-9][0-9]*§♦[\t ]*|)*)[\r\n]([\t ]*\|[\t ]*(?:pattern|left|right|body|shorts|socks))/g, '$1<!-- UNGAP MARKER -->\n\n$2');
	s = s.replace(/(\|[ ]*(?:league|cup[0-9]+)(?:[ ]*result|)[\t ]*=[^\r\n]*(?:[\r\n][\t ]*♦§[0-9]+§♦[\t ]*|))[\r\n]([\t ]*\|[ ]*(?:[a-z]+ attendance|[a-z]+ topscorer|league scorer))/g, '$1<!-- UNGAP MARKER -->\n\n$2');
	s = s.replace(/[\r\n]([\t ]*\|[ ]*prevseason[ ]*=)/g, '<!-- UNGAP MARKER -->\n\n$1');
	s = s.replace(/[\t ]*[\r\n][\t ]*([\r\n][\t ]*\|[\t ]*[A-Za-z0-9_\-\t ]*=)/g, '\n<!--\n GAP MARKER\n -->$1');
	s = s.replace(/[\r\n][\t ]*[\r\n]/g, '\n<!-- GAP MARKER -->\n');
	
	// Labels that should not be indented
	for(var i=0; i < long_labels.length; i++) {
		var re = new RegExp('(\\|[\\t ]*)(' + long_labels[i] + ')[\\t ]*=[\\t ]*', 'g');
		s = s.replace(re, '$1_FIX_ = <!-- _THIS_LONG_LABEL_ $2 -->');
	}
	
	var longRegex = new RegExp('(\\|[\\t ]*)([\\w\/]{' + MAX_PARAM_LENGTH + ',})[\\t ]*=[\\t ]*', 'g');
	s = s.replace(longRegex, '$1_FIX_ = <!-- _THIS_LONG_LABEL_ $2 -->');

	// uniform pre-pipe indentation
	var c0 = s.split(/[\r\n]\|/g).length;
	var c1 = s.split(/[\r\n][\t ]\|/g).length;
	var c2 = s.split(/[\r\n][\t ][\t ][\t ]*\|/g).length;
	if (c0 >= c1 && c0 >= c2) {
		s = s.replace(/([\r\n])[\s]*(\|)/g, '$1$2');
	} else if (c1 >= c0 && c1 >= c2 ) {
		s = s.replace(/([\r\n])[\s]*(\|)/g, '$1 $2');
	} else {
		s = s.replace(/([\r\n])[\s]*(\|)/g, '$1  $2');
	}
	// uniform post-pipe indentation
	c0 = s.split(/[\r\n][\t ]*\|[^=\|\[\]\{\]\t ]/g).length;
	c1 = s.split(/[\r\n][\t ]*\|[\t ][\t ]*[^=\|\[\]\{\]\t ]/g).length;
	if (c1 >= c0) {
		s = s.replace(/([\r\n][\t ]*\|)[\t ]*([^=\|\[\]\{\]\t ])/g, '$1 $2');
	} else {
		s = s.replace(/([\r\n][\t ]*\|)[\t ]*([^=\|\[\]\{\]\t ])/g, '$1$2');
	}
	// flip space and =
	s = s.replace(/(\|[^=\|\[\]\{\]\t ][\t ]*)(=)([\t ][\t ][\t ]*)/g, '$1$3$2');
	var two_or_more_space_before_equal_count = s.split(/[^=\|\[\]\{\}][\s][\s][\s]*=/g).length - 1;
	if (two_or_more_space_before_equal_count > 1) {
		// Unalign
		s = s.replace(/[\s]*\|[\s]*[\r\n]([\s]*[^=\|\[\]\{\]]*?=)[\t ]*/g, '\n|$1 ');
		s = s.replace(/(\|[\s ]*[^=\|\[\]\{\]]*?)[\s]*=[\t ]*/g, '$1 = ');
		s = s.replace(/[\r\n]([\s]*[^\s\|])/g, '<!-- ♦§NEWLINE§♦ -->$1');
		s = s.replace(/<!--[\s]*<!-- ♦§NEWLINE§♦ -->[\s]*GAP MARKER[\s]*<!-- ♦§NEWLINE§♦ -->[\s]*-->/g, '<!--\n GAP MARKER\n -->');

		// Realign
		for(var i=40; i>1; i--) {
			var r1 = new RegExp('([\\r\\n][\\s]*\\|[^=\\|\\[\\]\\{\\}]{'+(i)+'}=[^\\r\\n]*[\\r\\n][\\s]*\\|[^=\\|\\[\\]\\{\\}]{0,'+(i-1)+'})(=)','g');
			while( s.search(r1) > -1) {
				s = s.replace(r1, '$1 $2');
			}
			var r2 = new RegExp('([\\r\\n][\\s]*\\|[^=\\|\\[\\]\\{\\}]{0,'+(i-1)+'})(=[^\\r\\n]*[\\r\\n][\\s]*\\|[^=\\|\\[\\]\\{\\}]{'+(i)+'}=)','g');
			while( s.search(r2) > -1) {
				s = s.replace(r2, '$1 $2');
			}
		}
	} else if (two_or_more_space_before_equal_count > 0) {
		s = s.replace(/([^=\|\[\]\{\}][\s])[\s][\s]*(=)/g, '$1$2');
	}
	s = s.replace(/<!-- ♦§NEWLINE§♦ -->/g, '\n');
	s = s.replace(/<!-- FIX EQ MARKER -->/g, '=');
	s = s.replace(/<!-- SPACE MARKER -->/g, ' ');
	s = s.replace(/<!--[\s]*GAP MARKER[\s]*-->/g, '');
	s = s.replace(/<!--[\s]*UNGAP MARKER[\s]*-->[\t ]*[\r\n][\t ]*[\r\n]*([\s]*)/g, '\n$1');
	s = s.replace(/_FIX_[\t ]*=[\t ]*<!--[\t ]* _THIS_LONG_LABEL_ [\t ]*([^<>]*?)[\t ]*-->[\t ]*/g, '$1 = ');
	s = s.replace(/([^\|\{\t ])[\t ]*(\|)[\t ]*([\r\n])([^\*:;\{\|])/g, '$1$3$2$4');
	s = s.replace(/(\{\{cite[^{}\r\n]*)[\t ][\t ]+([^{}\r\n]*\}\})/g, '$1 $2');

	return s;
}
// -------------------------------------------------------------------------------- //
function wpUnsmall(s)
{
	var strlist = s.split(/(\{\{[\t \r\n]*(?:[Ii]nfobox|[Ff]ootball[_ ]*box)(?:[^{}]|\{\{(?:(?:[^{}]|\{\{(?:[^{}]|\{\{(?:[^{}]|\{\{[^{}]*\}\})*\}\})*\}\})*|\{\{[^{}]*\}\})*\}\})*\}\})/);
	for (i = 0; i < strlist.length; i++) {
		s = strlist[i];
		if(s.search(/\{\{[_\t\r\n ]*(?:[Ii]nfobox|[Ff]ootball[_ ]*box)/g) >=0 ) {
			var stmp = '';
			while( stmp != s ) {
				stmp = s;
				s = s.replace(/(\|[ ]*name[ ]*=[^\[\]\|\{\}]*sm)(all)/gi, '$1<!--MARKER DO NOT FIX MARKER-->$2');
			}
			s = s.replace(/<[\t ]*\/?[\t ]*small[^<>]*>/gi, '');
			s = s.replace(/\{\{small(?:er|)[\t ]*\|[\t ]*1[\t ]*=[\t ]*((?:[^{}]|\{\{(?:(?:[^{}]|\{\{(?:[^{}]|\{\{(?:[^{}]|\{\{[^{}]*\}\})*\}\})*\}\})*|\{\{[^{}]*\}\})*\}\})*?)[\t ]*\}\}/gi, '$1');
			s = s.replace(/\{\{small(?:er|)[\t ]*\|((?:[^{}]|\{\{(?:(?:[^{}]|\{\{(?:[^{}]|\{\{[^{}]*\}\})*\}\})*|\{\{[^{}]*\}\})*\}\})*)\}\}/gi, '$1');
			s = s.replace(/<!--[ ]*MARKER DO NOT FIX MARKER[ ]*-->/g, '');
			strlist[i] = s;
		}
	}
	return strlist.join('');
}
// -------------------------------------------------------------------------------- //
function startIndent(mytxt) 
{
	// Regular expression which matchs a template arg
	var argexp = new RegExp("(\\|[\\s]*[^=\\|\\[\\]\\{\\}]*=[\\s]*)", "gm");
	// Internal for and while loop variables
	var i=0; var j=0; var loopcount=0;
	// Array used to hold the list of unnested templates
	var tlist = []; 
	// Mark dollar signs which are problematic for regexp
	mytxt = mytxt.replace(/\$/g, '♦§DOLLAR§♦');

	// Remove comments
	loopcount = 0;
	while( (mytxt.search(/<!--.*?-->/g) >= 0) && (loopcount < 20) ) {
		// Split into chunks, isolating the comments
		var strlist = mytxt.split(/(<!--.*?-->)/);
		// Loop through the chunks, removing the comments templates
		for (i = 0; i < strlist.length; i++) {
			if( strlist[i].search(/^<!--.*?-->$/) >= 0 ) {
				tlist.push(strlist[i]);
				strlist[i] = '♦§' + (tlist.length - 1) + '§♦';
			}
		}
		// Join the chunks back together for the next iteration
		mytxt = strlist.join('');
		loopcount++;
	}

	var istart = tlist.length;
	// Now start unnesting the templates
	loopcount = 0;
	while( (mytxt.search(/(?:\{\{|\}\})/g) >= 0) && (loopcount < 20) ) {
		// Split into chunks, isolating the unnested templates
		var strlist = mytxt.split(/(\{\{[^\{\}]*\}\})/);
		// Loop through the chunks, removing the unnested templates
		for (i = 0; i < strlist.length; i++) {
			if( strlist[i].search(/^\{\{[^\{\}]*\}\}$/) >= 0 ) {
				tlist.push(strlist[i]);
				strlist[i] = '♦§' + (tlist.length - 1) + '§♦';
			}
		}
		// Join the chunks back together for the next iteration
		mytxt = strlist.join('');
		loopcount++;
	}

	// Now start indenting the list of unnested templates
	for(i=istart; i < tlist.length; ++i) {
		tlist[i] = wpIndent(tlist[i]);
	}
	// Reassemble the article
	while(mytxt.search(/♦§[0-9][0-9]*§♦/g) >= 0) {
		var n = mytxt.match(/♦§[0-9][0-9]*§♦/)[0].replace(/♦§([0-9]*)§♦/, '$1');
		mytxt = mytxt.replace(new RegExp('♦§' + n + '§♦', 'g'), tlist[Number(n)]);
	}

	mytxt = mytxt.replace(/[\t ]*[\r\n]*[\t ]*[\r\n][\t ]*(\{\{)[\t ]*((?:[Ii]nfobox|[Ss]peciesbox))/g, '\n$1$2');
    mytxt = mytxt.replace(/(\{\{[^{}=\|\r\n]*)([\r\n])(\|)[\t ]*(\{\{)/g, '$1$3$2$4');
	// Unmark dollar signs which are problematic for regexp
	mytxt = mytxt.replace(/♦§DOLLAR§♦/g, '\$');
	
	return mytxt;
}

function indentInfobox(mytxt){
	mw.notify('Indenting Infobox!', {type: 'info'})

	const regex = /\|\s*((?:header|label|data|class|rowclass|below)\d*[a-d]?)\s*=/g

	var full_matches = [...mytxt.matchAll(regex)];

	var values = full_matches.map(match => match[1]);

	var longest = values.reduce((a, b) => (a.length >= b.length ? a : b));

	for (const val of values) {
		mytxt = mytxt.replace(new RegExp(`(\\s*)\\|\\s*${RegExp.escape(val)}\\s*=\\s*`), "$1| " + val.padEnd(longest.length+1) + '= ')
	}
	
	return mytxt;
}

jQuery(document).ready(function($) {

var myContent = $('#wpTextbox1').textSelection('getContents');
// var myContent = document.getElementsByName('wpTextbox1')[0];
// -------------------------------------------------------------------------------- //
var mysummary = "Cleaning up syntax using [[:en:User:Zackmann08/scripts/indent|indent.js]]";
// var mysummary = "";

if(typeof indenteditsummary == 'string') {mysummary = indenteditsummary;}
var linktext = "Indent";
// -------------------------------------------------------------------------------- //

// Overid default action of marking minor edit when clicking accesskey + I
$("#wpMinoredit").removeAttr("accesskey");

$(document).ready(function() {
	if(mw.config.get('wgNamespaceNumber') != -1 && myContent && (mw.config.get('wgPageName') != 'User:Zackmann08/scripts/JWB.js/loader')) {		
		$(document).keydown(function(event) {
			// Check if Control key is pressed (event.ctrlKey)
			// Check if Alt key is pressed (event.altKey)
			// Check if the 'I' key is pressed (event.which or event.keyCode for 'I' is 73)
			if (event.ctrlKey && event.altKey && event.code === 'KeyI') {
				// Prevent the default browser action for this key combination (e.g., opening developer tools)
				console.log('i shortcut triggered')
				event.preventDefault();
				wpIndentTemplates(0);
			}
		});
		mw.loader.using(['mediawiki.util']).done( function() {
			var portletlink = mw.util.addPortletLink('p-tb', '#', linktext, 't-indent', 'Indent text');
			$(portletlink).click(function(e) {
				e.preventDefault();
				wpIndentTemplates(0);
			});
		});
		(async () => {
			await waitForElement("wpMinoredit");
			document.getElementById("wpMinoredit").removeAttribute("accesskey");
		})();

		// Add button to bottom toolbar after the "Show changes" button
		mw.loader.using( [ 'oojs-ui-core' ] ).then( function () {
			if ( document.getElementById( 'wpDiff' ) ) {
				var diff = document.getElementById( 'wpDiffWidget' );
				try {
					var indentButton = new OO.ui.ButtonWidget();
					indentButton.setElementId( 'wpIndent' ).setIcon( 'indent' ).setLabel( 'Indent' );
					indentButton.on( 'click', function () {
						wpIndentTemplates(0);
					});
					indentButton.$element.insertBefore( $(diff.nextSibling) );
				} catch (err) {
					mw.notify('Failed to insert ButtonWidget: ' + err.message, {type: 'warn'});
					return;
				}
			}	
		});
	}
});

// -------------------------------------------------------------------------------- //
function wpIndentTemplates(debugflag)
{
	console.log('wpIndentTemplates RAN')
	// Copy the contents of the text window so we can modify it without problems
	const original = $('#wpTextbox1').textSelection('getContents');
	var mytxt = original
	if ((mw.config.get('wgCanonicalNamespace') == 'Template' && !mw.config.get('wgPageName').match(/(\/doc|\/testcase)/)) || mw.config.get('wgPageName') == 'User:Zackmann08/indent') {	
		mytxt = indentInfobox(mytxt);
	} else {
		mytxt = startIndent(mytxt);
	}
    // Remove small tags (if opt-in)
	if(typeof removesmalltags == 'string' && removesmalltags == 'yes') {
		var oldtxt = mytxt;
		mytxt = wpUnsmall(mytxt);
		if (oldtxt != mytxt) {
			mysummary = mysmallsummary;
		}
	}

	if (mytxt != original) {
		mw.notify('Indent complete!', {type: 'success'});
		// myContent.value = mytxt;
		$('#wpTextbox1').textSelection('setContents', mytxt);
		
		var editsummary = document.getElementsByName('wpSummary')[0];
		if(typeof editsummary == 'object') {
			if (editsummary.value.indexOf(mysummary) == -1) {
				if (editsummary.value.match(/[^\*\/\s][^\/\s]?\s*$/)) {
					editsummary.value += '; ' + mysummary;
				} else {
					editsummary.value += mysummary;
				}
			}
		}	
	} else {
		mw.notify('Nothing to indent...', {type: 'error'});
	}
}

mw.hook( 'wikiEditor.toolbarReady' ).add( function ( $textarea ) {
    $textarea.wikiEditor( 'addToToolbar', {
        section: 'main', // Options: 'main', 'advanced'
        group: 'insert', // Options: 'format', 'insert', etc.
        tools: {
            'indent-button': {
                type: 'button',
                label: 'Indent text', // Button text
                icon: '//upload.wikimedia.org/wikipedia/commons/a/a3/OOjs_UI_icon_indent-ltr.svg', // Icon URL
                action: {
                    type: 'callback',
                    execute: function ( context ) {
                        wpIndentTemplates(0);
                    }
                }
            }
        }
    } );
} );
// -------------------------------------------------------------------------------- //

});