User:Aidan9382/scripts/fixlint.js

/* General fixer for simple lint errors Currently attempts to fix: - Font tags (converts all parameters to their style= versions) - Strike tags (replaces them with ) and tt tags (replaces them with ) - Center tags (replaces with the center template for text and style="margin:1em auto" for wikitables)

Nothing else beyond some obselete tags is fixed as of now

A "Fix Lint" button will be added to the More tab - use this to apply fixes

For a variety of reasons, all edits should be supervised and checked after the script has ran I don't often use JS so expect a wild number of bad practices

Inspired by, and built with help from, User:ಮಲ್ನಾಡಾಚ್ ಕೊಂಕ್ಣೊ/center.js //

var ISDEV = (getURIArg('fixlintdev') == 'true'); var fontColours = ['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green', 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen']; //Compiled from https://www.w3schools.com/colors/colors_names.asp

function getURIArg(arg) { var re = RegExp('[&?]' + arg + '=([^&]*)'); var matches = re.exec(document.location); if (matches) { try { return decodeURI(matches[1]); } catch (e) { } }	return null; }

function ProcessTag(tag) { //Turns a tag into a more usable table, hopefully var tagData = {}; var tagArgs = {}; tagData.args = tagArgs; var fspace = tag.search(" "); if (fspace == -1) tagData.name = tag.substring(1,tag.length-1); else tagData.name = tag.substring(1,fspace); var tempData = tag; while (true) { var argName; var argData; var tagArg = RegExp("(\\w+) ?= ?(?:'([^']*)'|\"([^\"]*)\")").exec(tempData);		if (!tagArg) {			tagArg = RegExp("(\\w+) ?= ?([^ >]+)").exec(tempData);			if (!tagArg) {				break;			} else {				argName = tagArg[1].toLowerCase;				argData = tagArg[2];			}		} else {			argName = tagArg[1].toLowerCase;			argData = tagArg[2] || tagArg[3];		}		if (ISDEV)			console.log("tagArg",tempData,tagArg);		tagArgs[argName] = argData;		tempData = tempData.replace(tagArg[0],"");	}	return tagData; }

function StringifyTag(tagData) { //The inverse of the above var final = "<" + tagData.name; for (var argName in tagData.args) { var argData = tagData.args[argName]; if (argData != null) { final = final + " " + argName + "=\"" + argData + "\""; }	}	return final + ">"; }

//Add a tab to activate if(mw.config.get('wgArticleId') != 0 ) { section = getURIArg("section"); mw.util.addPortletLink(		'p-cactions', 		mw.util.getUrl(null,{action:'edit',section:section,fixlint:true}),		'Fix Lint',		'ca-fixlint',		'Fix basic Lint Errors'	); }

function notify(title,message,type,autohide,autohidetime) { if (mw.notification) { console.log("notify",title,message); autohide = (autohide==null && true) || autohide; autohidetime = (autohidetime==null && "short") || autohidetime; if (!mw.notification.autoHideSeconds[autohidetime]) mw.notification.autoHideSeconds[autohidetime] = autohidetime; //Enable custom arbitrary lengths mw.notification.notify(message,{autoHide:autohide,autoHideSeconds:autohidetime,title:title,type:type}); } }

function modifyPageContent(notifyOfEvents) { function notifyIfAllowed(title,message,type,autohide,autohidetime) { if (notifyOfEvents) { notify(title,message,type,autohide,autohidetime); }	}	var myContent = document.getElementById('wpTextbox1').value; var noIssues = true; //Remove unprocessed content (content inside nowiki or syntaxhighlight) //This content is never actively displayed and we should therefore never judge it	var removedText = {}; var tag; var i;	var unprocessedTags = ["[Ss][Yy][Nn][Tt][Aa][Xx][Hh][Ii][Gg][Hh][Ll][Ii][Gg][Hh][Tt]","[Nn][Oo][Ww][Ii][Kk][Ii]","[Pp][Rr][Ee]"]; for (tag in unprocessedTags) { tag = unprocessedTags[tag]; i = Object.keys(removedText).length; removedText[i] = []; if (ISDEV) console.log("Handling",tag,i,removedText[i]); while (true) { var foundTag = RegExp('<'+tag+'[^>]*>[\\s\\S]+?').exec(myContent); if (!foundTag) { break; } else { foundTag = foundTag[0]; removedText[i][removedText[i].length] = foundTag; myContent = myContent.replace(foundTag,"REMOVED_TAG_"+i+"_"+(removedText[i].length-1)); }		}	}	if (ISDEV) console.log("removed content:",removedText); // Fix font tags - START var fontTagBalance = myContent.split(/ 0) { notifyIfAllowed("Lint Fixer","There were "+fontTagBalance+" too many opening font tags","warn",false); noIssues = false; } else if (fontTagBalance < 0) { notifyIfAllowed("Lint Fixer","There were "+(-fontTagBalance)+" too many closing font tags","warn",false); noIssues = false; }	while (true) { var fontTag = RegExp('<[Ff][Oo][Nn][Tt][^>]*>').exec(myContent); if (!fontTag) { //Out of font tags, we are done here break; } else { fontTag = fontTag[0]; }		if (ISDEV) console.log("fontTag",fontTag); var tagData = ProcessTag(fontTag); var style; var fontColour = tagData.args.color; if (fontColour) { style = tagData.args.style || ""; if (style.length > 0 && style.substring(style.length-1) != ";") { style = style + ";"; }			if (fontColour.substring(0,1) != "#" && !isNaN(parseInt(fontColour,16)) && !fontColours.includes(fontColour.toLowerCase)) { fontColour = "#" + fontColour; }			style = style + "color:" + fontColour; tagData.args.style = style; tagData.args.color = null; }		var fontFace = tagData.args.face; if (fontFace) { style = tagData.args.style || ""; if (style.length > 0 && style.substring(style.length-1) != ";") { style = style + ";"; }			style = style + "font-family:" + fontFace; tagData.args.style = style; tagData.args.face = null; }		var fontSize = tagData.args.size; if (fontSize) { //This requires manual conversion, as the metric is a little different //Logic is based off of the exact px provided by mw:Help:Lint errors/obsolete-tag //Reinforced by the behaviour seen in Special:Permalink/1118333555 var sizes = {1:"x-small",2:"small",3:"medium",4:"large",5:"x-large",6:"xx-large",7:"xxx-large"}; style = tagData.args.style || ""; if (style.length > 0 && style.substring(style.length-1) != ";") { style = style + ";"; }			if (fontSize.substring(fontSize.length-2) == "px") { fontSize = fontSize.substring(0,fontSize.length-2); }			if (fontSize.substring(0,1) == "+") { fontSize = 3 + (parseInt(fontSize.substring(1))); } else if (fontSize.substring(0,1) == "-") { fontSize = 3 - (parseInt(fontSize.substring(1))); }			fontSize = Math.max(1,Math.min(7,fontSize)); //Limit 1 -> 7 if (!sizes[fontSize]) { notifyIfAllowed("Lint Fixer","Unable to recognise font size of "+fontSize+" ("+tagData.args.size+")","warn",false); noIssues = false; } else { style = style + "font-size:" + sizes[fontSize]; tagData.args.style = style; tagData.args.size = null; }		}

tagData.name = "span"; myContent = myContent.replace(fontTag,StringifyTag(tagData)); }	myContent = myContent.replaceAll(/<\/font>/gi," "); //Don't overengineer, this'll do	// Fix font tags - END // Fix strike tags - START myContent = myContent.replaceAll(/ /gi," "); //Simple approach does the job myContent = myContent.replaceAll(/<\/strike>/gi," "); // Fix strike tags - END // Fix tt tags - START myContent = myContent.replaceAll(//gi," "); myContent = myContent.replaceAll(/<\/tt>/gi," "); // Fix tt tags - END // Fix center tags - START while (true) { var centerTag = RegExp('<[Cc][Ee][Nn][Tt][Ee][Rr]>([\\s\\S]+?)').exec(myContent); var centerContent; if (!centerTag) { break; } else { centerContent = centerTag[1]; centerTag = centerTag[0]; }		if (centerContent.search("{\\|") > -1) { wikitableStyle = RegExp('(\n{\\|.*?)\n').exec(centerContent)[1]; styleTag = RegExp("(style=['\"].*?);?['\"]").exec(wikitableStyle); if (styleTag) { //These are messy lines, but it's supervised so it's good enough centerContent = centerContent.replace(wikitableStyle,wikitableStyle.replace(styleTag[0],styleTag[1]+";margin:1em auto\""));			} else {				centerContent = centerContent.replace(wikitableStyle,wikitableStyle+" style=\"margin:1em auto\"");			}			if (centerContent.substring(0,1) == "\n") {				centerContent = centerContent.substring(1);			}			if (centerContent.substring(centerContent.length-1) == "\n") {				centerContent = centerContent.substring(0,centerContent.length-1);			}			myContent = myContent.replace(centerTag,centerContent);		} else {			if (centerContent.search("=") > -1 && centerContent.search("{") == -1) { //Definite fix needed				myContent = myContent.replace(centerTag,""+centerContent+"");			} else { //May still need fix, but who knows :)				myContent = myContent.replace(centerTag,""+centerContent+"");			}		}	}	// Fix center tags - END	//Bring back unprocessed content in the reverse order to avoid bad nesting fails for (i=Object.keys(removedText).length-1; i>=0; i--) { var tags = removedText[i]; for (i2=0; i2<tags.length; i2++) { myContent = myContent.replace("REMOVED_TAG_"+i+"_"+i2,tags[i2]); }	}	return {myContent:myContent, wasChanged:document.getElementById('wpTextbox1').value != myContent, noIssues:noIssues}; }

if(mw.config.get('wgAction') == 'edit') { if (getURIArg('fixlint') == 'true' || ISDEV) { data = modifyPageContent(true); document.getElementById('wpTextbox1').value = data.myContent; if (data.wasChanged) { document.getElementById('wpSummary').value = '→Fix Lint Errors'; document.getElementById('wpMinoredit').checked = true; //document.getElementById('wpWatchthis').checked = true; //document.getElementById('wpWatchlistExpiry').selectedIndex = 2; if (!ISDEV && data.noIssues) document.forms.editform.wpDiff.click; else if (!data.noIssues) notify("Lint Fixer","There were some potential issues found during fixing","warn",false); } else { notify("Lint Fixer","No lint errors were found","success"); }	} else { data = modifyPageContent(false); if (data.wasChanged) { if (data.noIssues) { notify("Lint Fixer","Fixable lint errors were found on this page"); } else { notify("Lint Fixer","Fixable lint errors were found on this page (supervision required)","warn"); }		} else { //notify("Lint Fixer","No lint errors were found","success"); }	} }

//