User:MJL/ARA.js

// // Everything is encapsulated in a private namespace object---``JJJ'': var JJJ = JJJ || {};

$(document).ready(function {	//initialize Constants	JJJ.Constants = getARAConstants;

//only execute on the edit page if (!JJJ.Constants.IS_EDIT_PAGE || JJJ.Constants.IS_JS_PAGE || JJJ.Constants.ARTICLE_TEXT_BOX_ELEMENT == null) return; //init functions and rules JJJ.Functions = getARAFunctions; JJJ.Rules    = getARARules; //init UI	$('#editform').prepend(JJJ.Constants.SUGGESTION_BOX_DIV); $('#wpSummaryLabel').prepend(JJJ.Constants.ADD_TO_SUMMARY_DIV); JJJ.Functions.scan;                                  //init scan now JJJ.Functions.observeWikiText(JJJ.Constants.delayScan); // ... and every time the user pauses typing });

function getARAConstants {	var ARA_Constants = ARA_Constants || {};

//article text box element ARA_Constants.ARTICLE_TEXT_BOX_ELEMENT = $("#wpTextbox1"); //are we on an Edit page? ARA_Constants.IS_EDIT_PAGE = mw.config.get('wgAction') === 'edit' || mw.config.get('wgAction') === 'submit'; //are we on a JS page? ARA_Constants.IS_JS_PAGE = mw.config.get('wgRelevantPageName').endsWith('.js'); //the ARA Suggestion box, which appears above the editing section ARA_Constants.SUGGESTION_BOX_DIV = $(' ', {'id':'suggestionBox.ARA', 'style':'border:dashed #ccc 1px;color:#888;'}); //the Add to Summary box, which appears near the edit summary ARA_Constants.ADD_TO_SUMMARY_DIV = $(' ', {'id':'addToSummaryBox.ARA', 'style':'border:dashed #ccc 1px;color:#888;display:none;'}); ARA_Constants.DEFAULT_MAX_SUGGESTIONS = 8; ARA_Constants.maxSuggestions = ARA_Constants.DEFAULT_MAX_SUGGESTIONS; ARA_Constants.suggestions; // : Suggestion[] ARA_Constants.appliedSuggestions = {}; // : Map ARA_Constants.scannedText = null; // remember what we scan, to check if it is	                      // still the same when we try to fix it	ARA_Constants.BIG_THRESHOLD = 100 * 1024; ARA_Constants.isBigScanConfirmed = false; // is the warning about a big article confirmed ARA_Constants.isTalkPageScanConfirmed = false; ARA_Constants.scanTimeoutId = null; // a timeout is set after a keystroke and before // a scan, this variable tracks its id	return ARA_Constants; }

function getARAFunctions {	var ARA_Functions = ARA_Functions || {};

// Browsers offer means to highlight text between two given offsets (``start	// and ``end) in a textarea, but some of them do not automatically scroll to it. // This function is an attempt to simulate cross-browser selection and scrolling. ARA_Functions.setSelectionRange = function (ta, start, end) { // Initialise static variables used within this function var _static = arguments.callee; // this is the Function we are in. It will be used as a poor man's function-local static scope. if (ta.setSelectionRange) { // Guess the vertical scroll offset by creating a			// separate hidden clone of the original textarea, filling it with the text // before ``start'' and computing its height. if (_static.NEWLINES == null) { _static.NEWLINES = '\n'; // 64 of them should be enough. for (var i = 0; i < 6; i++) { _static.NEWLINES += _static.NEWLINES; }			}			if (_static.helperTextarea == null) { _static.helperTextarea = document.createElement('TEXTAREA'); _static.helperTextarea.style.display = 'none'; document.body.appendChild(_static.helperTextarea); }			var hta = _static.helperTextarea; hta.style.display = ''; hta.style.width = ta.clientWidth + 'px'; hta.style.height = ta.clientHeight + 'px'; hta.value = _static.NEWLINES.substring(0, ta.rows) + ta.value.substring(0, start); var yOffset = hta.scrollHeight; hta.style.display = 'none'; ta.focus; ta.setSelectionRange(start, end); if (yOffset > ta.clientHeight) { yOffset -= Math.floor(ta.clientHeight / 2); ta.scrollTop = yOffset; // Opera does not support setting the scrollTop property if (ta.scrollTop != yOffset) { // todo: Warn the user or apply a workaround }			} else { ta.scrollTop = 0; }		} else { // IE incorrectly counts '\r\n' as a signle character start -= ta.value.substring(0, start).split('\r').length - 1; end -= ta.value.substring(0, end).split('\r').length - 1; var range = ta.createTextRange; range.collapse(true); range.moveStart('character', start); range.moveEnd('character', end - start); range.select; }	};	// getPosition(e), observe(e, x, f), stopObserving(e, x, f), // and stopEvent(event) are inspired by the prototype.js framework // http://prototypejs.org/ ARA_Functions.getPosition = function (e) { var x = 0; var y = 0; do { x += e.offsetLeft || 0; y += e.offsetTop || 0; e = e.offsetParent; } while (e); return {x: x, y: y}; };	ARA_Functions.observe = function (e, eventName, f) { if (e.addEventListener) { e.addEventListener(eventName, f, false); } else { e.attachEvent('on' + eventName, f); }	};	ARA_Functions.stopObserving = function (e, eventName, f) { if (e.removeEventListener) { e.removeEventListener(eventName, f, false); } else { e.detachEvent('on' + eventName, f); }	};	ARA_Functions.stopEvent = function (event) { if (event.preventDefault) { event.preventDefault; event.stopPropagation; } else { event.returnValue = false; event.cancelBubble = true; }	};	// ARA_Functions.anchor is a shortcut to creating a link as a DOM node: ARA_Functions.anchor = function (text, href, title) { var e = document.createElement('A'); e.href = href; e.appendChild(document.createTextNode(text)); e.title = title || ''; return e;	}; // ARA_Functions.link produces the HTML for a link to a Wikipedia article as a string. // It is convenient to embed in a help popup. ARA_Functions.hlink = function (toWhat, text) { var wgServer = window.wgServer || 'http://en.wikipedia.org'; var wgArticlePath = window.wgArticlePath || '/wiki/$1'; var url = (wgServer + wgArticlePath).replace('$1', toWhat); return '' + (text || toWhat) + ''; };	// === Helpers a la functional programming === // A higher-order function---produces a cached version of a one-arg function. ARA_Functions.makeCached = function (f) { var cache = {}; // a closure; the cache is private for f		return function (x) { return (cache[x] != null) ? cache[x] : (cache[x] = f(x)); };	};	// === Editor compatibility layer === // Controlling access to wpTextbox1 helps abstract out compatibility // with editors like wikEd (http://en.wikipedia.org/wiki/User:Cacycle/wikEd) ARA_Functions.getWikiText = function { if (window.wikEdUseWikEd) { var obj = {sel: WikEdGetSelection}; WikEdParseDOM(obj, wikEdFrameBody); return obj.plain; }		return JJJ.Constants.ARTICLE_TEXT_BOX_ELEMENT.val; };	ARA_Functions.setWikiText = function (s) { if (window.wikEdUseWikEd) { // todo: wikEd compatibility alert(JJJ.Functions._('Changing text in wikEd is not yet supported.')); return; };		JJJ.Constants.ARTICLE_TEXT_BOX_ELEMENT.val(s); };	ARA_Functions.focusWikiText = function { if (window.wikEdUseWikEd) { wikEdFrameWindow.focus; return; }		JJJ.Constants.ARTICLE_TEXT_BOX_ELEMENT.focus; };	ARA_Functions.selectWikiText = function (start, end) { if (window.wikEdUseWikEd) { var obj = x = {sel: WikEdGetSelection, changed: {}}; WikEdParseDOM(obj, wikEdFrameBody); var i = 0; while ((obj.plainStart[i + 1] != null) && (obj.plainStart[i + 1] <= start)) { i++; }			var j = i;			while ((obj.plainStart[j + 1] != null) && (obj.plainStart[j + 1] <= end)) { j++; }			obj.changed.range = document.createRange; obj.changed.range.setStart(obj.plainNode[i], start - obj.plainStart[i]); obj.changed.range.setEnd(obj.plainNode[j], end - obj.plainStart[j]); WikEdRemoveAllRanges(obj.sel); obj.sel.addRange(obj.changed.range); return; }		ARA_Functions.setSelectionRange(document.getElementById(JJJ.Constants.ARTICLE_TEXT_BOX_ELEMENT.prop('id')), start, end); };	ARA_Functions.observeWikiText = function (callback) { // todo: wikEd compatibility ARA_Functions.observe(document.getElementById(JJJ.Constants.ARTICLE_TEXT_BOX_ELEMENT.prop('id')), 'keyup', JJJ.Functions.delayScan); };	// === Interaction with the user === // ARA_Functions.scan analyses the text and handles how the proposals are reflected in the UI. ARA_Functions.scan = function (force) {		JJJ.Constants.scanTimeoutId = null; //get article text var s = JJJ.Functions.getWikiText; //determine if we actually need to scan if ((s === JJJ.Constants.scannedText) && !force) return; // Nothing to do, we've already scanned the very same text JJJ.Constants.scannedText = s;		//remove all current suggestions JJJ.Constants.SUGGESTION_BOX_DIV.empty; // Warn about scanning a big article if ((s.length > JJJ.Constants.BIG_THRESHOLD) && !JJJ.Constants.isBigScanConfirmed) { JJJ.Constants.SUGGESTION_BOX_DIV.append(document.createTextNode( JJJ.Functions._('This article is rather long. ARA may consume a lot of '					+ 'RAM and CPU resources while trying to parse the text.  You could limit '					+ 'your edit to a single section, or ') ));			JJJ.Constants.SUGGESTION_BOX_DIV.append(JJJ.Functions.anchor( JJJ.Functions._('scan the text anyway.'), 'javascript: JJJ.Constants.isBigScanConfirmed = true; JJJ.Functions.scan(true); void(0);', JJJ.Functions._('Ignore this warning.') ));			return; }		// Warn about scanning a talk page if ((  mw.config.get('wgCanonicalNamespace') === 'Talk' || mw.config.get('wgCanonicalNamespace') === 'User_talk' || mw.config.get('wgCanonicalNamespace') === 'Project_talk' || mw.config.get('wgCanonicalNamespace') === 'MediaWiki_talk' )		   && !JJJ.Constants.isTalkPageScanConfirmed		) {			JJJ.Constants.SUGGESTION_BOX_DIV.append(document.createTextNode( JJJ.Functions._('ARA is disabled on talk pages, because ' +					'it might suggest changing other users\' comments. That would be ' +					'something against talk page conventions.  If you promise to be ' +					'careful, you can ') ));			JJJ.Constants.SUGGESTION_BOX_DIV.append(JJJ.Functions.anchor( JJJ.Functions._('scan the text anyway.'), 'javascript: JJJ.Constants.isTalkPageScanConfirmed = true; JJJ.Functions.scan(true); void(0);', JJJ.Functions._('Ignore this warning.') ));			return; }		//get suggestions JJJ.Constants.suggestions = JJJ.Functions.getSuggestions(s); //if there aren't any suggestions, say so		if (JJJ.Constants.suggestions.length === 0) {			JJJ.Constants.SUGGESTION_BOX_DIV.append(document.createTextNode( JJJ.Functions._('OK \u2014 ARA found no referencing issues.') // U+2014 is an mdash ));			return; }		var nSuggestions = Math.min(JJJ.Constants.maxSuggestions, JJJ.Constants.suggestions.length); JJJ.Constants.SUGGESTION_BOX_DIV.append(document.createTextNode( (JJJ.Constants.suggestions.length == 1) ? JJJ.Functions._('1 suggestion: ') : JJJ.Functions._('$1 suggestions: ', JJJ.Constants.suggestions.length) ));		for (var i = 0; i < nSuggestions; i++) { var suggestion = JJJ.Constants.suggestions[i]; var eA = JJJ.Functions.anchor(					suggestion.name,					'javascript:JJJ.Functions.showSuggestion(' + i + '); void(0);',					suggestion.description			); suggestion.element = eA; JJJ.Constants.SUGGESTION_BOX_DIV.append(eA); if (suggestion.replacement != null) {				var eSup = document.createElement('SUP'); JJJ.Constants.SUGGESTION_BOX_DIV.append(eSup); var sup1 = suggestion.sup1 != null ? suggestion.sup1 : 'fix'; eSup.appendChild (					JJJ.Functions.anchor ( JJJ.Functions._(sup1), 'javascript:JJJ.Functions.fixSuggestion(' + i + '); void(0);' )				);				//sometimes, suggestions may have more than one fix link if (suggestion.replacement2 != null) {					var sup2 = suggestion.sup2 != null ? suggestion.sup2 : 'fix2'; eSup.appendChild(document.createTextNode(' | ')); eSup.appendChild(						JJJ.Functions.anchor( JJJ.Functions._(sup2), 'javascript:JJJ.Functions.fixSuggestion2(' + i + '); void(0);' )					);				}				if (suggestion.replacement3 != null) {					var sup3 = suggestion.sup3 != null ? suggestion.sup3 : 'fix3'; eSup.appendChild(document.createTextNode(' | ')); eSup.appendChild(						JJJ.Functions.anchor( JJJ.Functions._(sup3), 'javascript:JJJ.Functions.fixSuggestion3(' + i + '); void(0);' )					);				}			}			JJJ.Constants.SUGGESTION_BOX_DIV.append(document.createTextNode(' ')); }		if (JJJ.Constants.suggestions.length > JJJ.Constants.maxSuggestions) { JJJ.Constants.SUGGESTION_BOX_DIV.append(JJJ.Functions.anchor( '...', 'javascript: JJJ.Constants.maxSuggestions = 1000; JJJ.Functions.scan(true); void(0);', JJJ.Functions._('Show All') ));		}	};	// getSuggestions returns the raw data used by scan. // It is convenient for unit testing. ARA_Functions.getSuggestions = function (s) { var suggestions = []; var missingRefGroupSuggestions = []; //we want to keep track of the ones we already have so we don't push the same message twice for (var i = 0; i < JJJ.Rules.length; i++) //for each rule {			var a = JJJ.Rules[i](s); //execute rule for (var j = 0; j < a.length; j++) //for each suggestion pushed by the rule {				var returned_suggestion = a[j]; //if the suggestion is not a missing reference groups suggestion, or it is and we didn't already push this one if (!returned_suggestion.name.includes("missing reference groups") || !missingRefGroupSuggestions.includes(returned_suggestion.name)) {					suggestions.push(returned_suggestion); //add suggestion to list of suggestions missingRefGroupSuggestions.push(returned_suggestion.name); //add suggestion to list of suggestions we already have }			}		}		suggestions.sort(function (x, y) {			return (x.start < y.start) ? -1 :			      (x.start > y.start) ? 1 :			       (x.end < y.end) ? -1 :			       (x.end > y.end) ? 1 : 0;		}); return suggestions; };	// === Internationalisation === // ARA_Functions._ is a gettext-style internationalisation helper // (http://en.wikipedia.org/wiki/gettext) // If no translation is found for the parameter, it is returned as is. // Additionally, subsequent parameters are substituted for $1, $2, and so on. ARA_Functions._ = function (s) { if (JJJ.Constants.translation && JJJ.Constants.translation[s]) { s = JJJ.Constants.translation[s]; }		var index = 1; while (arguments[index]) { s = s.replace('$' + index, arguments[index]); // todo: replace all? index++; }		return s;	}; // showSuggestion handles clicks on the suggestions above the edit area // This does one of two things: // * on first click---highlight the corresponding text in the textarea // * on a second click, no later than a fixed number milliseconds after the // 		first one---show the help popup ARA_Functions.showSuggestion = function (k) { if (JJJ.Functions.getWikiText != JJJ.Constants.scannedText) { // The text has changed - just do another scan and don't change selection JJJ.Functions.scan; return; }		var suggestion = JJJ.Constants.suggestions[k]; var now = new Date.getTime; if ((suggestion.help != null) && (JJJ.Constants.lastShownSuggestionIndex === k) && (now - JJJ.Constants.lastShownSuggestionTime < 1000)) { // Show help var p = JJJ.Functions.getPosition(suggestion.element); var POPUP_WIDTH = 300; var eDiv = document.createElement('DIV'); eDiv.innerHTML = suggestion.help; eDiv.style.position = 'absolute'; eDiv.style.left = Math.max(0, Math.min(p.x, document.body.clientWidth - POPUP_WIDTH)) + 'px'; eDiv.style.top = (p.y + suggestion.element.offsetHeight) + 'px'; eDiv.style.border = 'solid ThreeDShadow 1px'; eDiv.style.backgroundColor = 'InfoBackground'; eDiv.style.fontSize = '12px'; eDiv.style.color = 'InfoText'; eDiv.style.width = POPUP_WIDTH + 'px'; eDiv.style.padding = '0.3em'; eDiv.style.zIndex = 10; document.body.appendChild(eDiv); JJJ.Functions.observe(document.body, 'click', function (event) {				event = event || window.event;				var target = event.target || event.srcElement;				var e = target;				while (e != null) {					if (e == eDiv) {						return;					}					e = e.parentNode;				}				document.body.removeChild(eDiv);				JJJ.Functions.stopObserving(document.body, 'click', arguments.callee);			}); JJJ.Functions.focusWikiText; return; }		JJJ.Constants.lastShownSuggestionIndex = k;		JJJ.Constants.lastShownSuggestionTime = now; JJJ.Functions.selectWikiText(suggestion.start, suggestion.end); };	// Usually, there is a ``fix'' link next to each suggestion. It is handled by: ARA_Functions.fixSuggestion = function(k) {		var s = JJJ.Functions.getWikiText; if (s != JJJ.Constants.scannedText) { JJJ.Functions.scan; return; }		var suggestion = JJJ.Constants.suggestions[k]; // the issue is not automatically fixable, return if (suggestion.replacement == null) { return; }		JJJ.Functions.setWikiText(				s.substring(0, suggestion.start)				+ suggestion.replacement				+ s.substring(suggestion.end)		); JJJ.Functions.selectWikiText(				suggestion.start,				suggestion.start + suggestion.replacement.length		); // Propose an edit summary unless it's a new section var editform = document.getElementById('editform'); if (!editform['wpSection'] || (editform['wpSection'].value != 'new')) { if (JJJ.Constants.appliedSuggestions[suggestion.name] == null) { JJJ.Constants.appliedSuggestions[suggestion.name] = 1; } else { JJJ.Constants.appliedSuggestions[suggestion.name]++; }			var a = []; for (var i in JJJ.Constants.appliedSuggestions) { a.push(i); }			a.sort(function (x, y) {				return (JJJ.Constants.appliedSuggestions[x] > JJJ.Constants.appliedSuggestions[y]) ? -1 :					  (JJJ.Constants.appliedSuggestions[x] < JJJ.Constants.appliedSuggestions[y]) ? 1 :					   (x < y) ? -1 : (x > y) ? 1 : 0;			}); var s = ''; for (var i = 0; i < a.length; i++) { var count = JJJ.Constants.appliedSuggestions[a[i]]; s += ', ' + ((count == 1) ? a[i] : (count + 'x ' + a[i])); }			// Cut off the leading ``,  and add ``using ARA s = JJJ.Functions._(					'fixed $1 using ARA-JJJ',					s.substring(2)	                       ); if (s == "fixed missing Name in Persondata using ARA-JJJ") s = "added missing Name in Persondata using ARA-JJJ"; else if (s == "fixed empty Persondata template using ARA-JJJ") s = "expanded empty Persondata template using ARA-JJJ"; else if (s.indexOf("references in Portal page]] using ARA-JJJ") > -1) s = "removed references from Portal page using ARA-JJJ"; else if (s == "fixed in Portal page using ARA-JJJ") s = "removed from Portal page using ARA-JJJ"; // Render in DOM JJJ.Constants.ADD_TO_SUMMARY_DIV.empty; JJJ.Constants.ADD_TO_SUMMARY_DIV.show; JJJ.Constants.ADD_TO_SUMMARY_DIV.append(JJJ.Functions.anchor( JJJ.Functions._('Add to summary'), 'javascript:JJJ.Functions.addToSummary(unescape("' + escape(s) + '"));', JJJ.Functions._('Append the proposed summary to the input field below') ));			JJJ.Constants.ADD_TO_SUMMARY_DIV.append(document.createTextNode(': "' + s + '"')); }		// Re-scan immediately JJJ.Functions.scan; };	// if a suggestion has two 'fix' options, the second option will be handled here ARA_Functions.fixSuggestion2 = function (k) {		var s = JJJ.Functions.getWikiText; if (s != JJJ.Constants.scannedText) { JJJ.Functions.scan; return; }		var suggestion = JJJ.Constants.suggestions[k];

// the issue is not automatically fixable, return if (suggestion.replacement2 == null) { return; }		//otherwise, if we are executing JS, do so		else if (suggestion.replacement2.includes("javascript:")) {			eval(suggestion.replacement2); return; }		JJJ.Functions.setWikiText(				s.substring(0, suggestion.start2)				+ suggestion.replacement2				+ s.substring(suggestion.end2)		); JJJ.Functions.selectWikiText(				suggestion.start2,				suggestion.start2 + suggestion.replacement2.length		); // Propose an edit summary unless it's a new section var editform = document.getElementById('editform'); if (!editform['wpSection'] || (editform['wpSection'].value != 'new')) { if (JJJ.Constants.appliedSuggestions[suggestion.name] == null) { JJJ.Constants.appliedSuggestions[suggestion.name] = 1; } else { JJJ.Constants.appliedSuggestions[suggestion.name]++; }			var a = []; for (var i in JJJ.Constants.appliedSuggestions) { a.push(i); }			a.sort(function (x, y) {				return (JJJ.Constants.appliedSuggestions[x] > JJJ.Constants.appliedSuggestions[y]) ? -1 :					  (JJJ.Constants.appliedSuggestions[x] < JJJ.Constants.appliedSuggestions[y]) ? 1 :					   (x < y) ? -1 : (x > y) ? 1 : 0;			}); var s = ''; for (var i = 0; i < a.length; i++) { var count = JJJ.Constants.appliedSuggestions[a[i]]; s += ', ' + ((count == 1) ? a[i] : (count + 'x ' + a[i])); }			// Cut off the leading ``,  and add ``using ARA s = JJJ.Functions._(					'fixed $1 using ARA-JJJ',					s.substring(2)	                       ); if (s == "fixed missing Name in Persondata using ARA-JJJ") s = "added missing Name in Persondata using ARA-JJJ"; else if (s == "fixed empty Persondata template using ARA-JJJ") s = "expanded empty Persondata template using ARA-JJJ"; else if (s.indexOf("references in Portal page]] using ARA-JJJ") > -1) s = "removed references from Portal page using ARA-JJJ"; else if (s == "fixed in Portal page using ARA-JJJ") s = "removed from Portal page using ARA-JJJ"; // Render in DOM JJJ.Constants.ADD_TO_SUMMARY_DIV.empty; JJJ.Constants.ADD_TO_SUMMARY_DIV.show; JJJ.Constants.ADD_TO_SUMMARY_DIV.append(JJJ.Functions.anchor( JJJ.Functions._('Add to summary'), 'javascript:JJJ.Functions.addToSummary(unescape("' + escape(s) + '"));', JJJ.Functions._('Append the proposed summary to the input field below') ));			JJJ.Constants.ADD_TO_SUMMARY_DIV.append(document.createTextNode(': "' + s + '"')); }		// Re-scan immediately JJJ.Functions.scan; };	ARA_Functions.fixSuggestion3 = function (k) {		var s = JJJ.Functions.getWikiText; if (s != JJJ.Constants.scannedText) { JJJ.Functions.scan; return; }		var suggestion = JJJ.Constants.suggestions[k]; if (suggestion.replacement3 == null) { // the issue is not automatically fixable return; }		JJJ.Functions.setWikiText(				s.substring(0, suggestion.start3)				+ suggestion.replacement3				+ s.substring(suggestion.end3)		); JJJ.Functions.selectWikiText(				suggestion.start3,				suggestion.start3 + suggestion.replacement3.length		); // Propose an edit summary unless it's a new section var editform = document.getElementById('editform'); if (!editform['wpSection'] || (editform['wpSection'].value != 'new')) { if (JJJ.Constants.appliedSuggestions[suggestion.name] == null) { JJJ.Constants.appliedSuggestions[suggestion.name] = 1; } else { JJJ.Constants.appliedSuggestions[suggestion.name]++; }			var a = []; for (var i in JJJ.Constants.appliedSuggestions) { a.push(i); }			a.sort(function (x, y) {				return (JJJ.Constants.appliedSuggestions[x] > JJJ.Constants.appliedSuggestions[y]) ? -1 :					  (JJJ.Constants.appliedSuggestions[x] < JJJ.Constants.appliedSuggestions[y]) ? 1 :					   (x < y) ? -1 : (x > y) ? 1 : 0;			}); var s = ''; for (var i = 0; i < a.length; i++) { var count = JJJ.Constants.appliedSuggestions[a[i]]; s += ', ' + ((count == 1) ? a[i] : (count + 'x ' + a[i])); }			// Cut off the leading ``,  and add ``using ARA s = JJJ.Functions._(					'fixed $1 using ARA-JJJ',					s.substring(2)	                       ); if (s == "fixed missing Name in Persondata using ARA-JJJ") s = "added missing Name in Persondata using ARA-JJJ"; else if (s == "fixed empty Persondata template using ARA-JJJ") s = "expanded empty Persondata template using ARA-JJJ"; else if (s.indexOf("references in Portal page]] using ARA-JJJ") > -1) s = "removed references from Portal page using ARA-JJJ"; else if (s == "fixed in Portal page using ARA-JJJ") s = "removed from Portal page using ARA-JJJ"; // Render in DOM JJJ.Constants.ADD_TO_SUMMARY_DIV.empty; JJJ.Constants.ADD_TO_SUMMARY_DIV.show; JJJ.Constants.ADD_TO_SUMMARY_DIV.append(JJJ.Functions.anchor( JJJ.Functions._('Add to summary'), 'javascript:JJJ.Functions.addToSummary(unescape("' + escape(s) + '"));', JJJ.Functions._('Append the proposed summary to the input field below') ));			JJJ.Constants.ADD_TO_SUMMARY_DIV.append(document.createTextNode(': "' + s + '"')); }		// Re-scan immediately JJJ.Functions.scan; };	// The mnemonics of the accepted suggestions are accumulated in JJJ.Constants.appliedSuggestions // and the user is presented with a sample edit summary. If she accepts it, // addToSummary gets called. ARA_Functions.addToSummary = function (summary) { var wpSummary = document.getElementById('wpSummary'); if (wpSummary.value != '') { summary = wpSummary.value + '; ' + summary; }		if ((wpSummary.maxLength > 0) && (summary.length > wpSummary.maxLength)) { alert(JJJ.Funtions._( 'Error: If the proposed text is added to the summary, ' + 'its length will exceed the $1-character maximum by $2 characters.', /* $1 = */ wpSummary.maxLength, /* $2 = */ summary.length - wpSummary.maxLength ));			return; }		wpSummary.value = summary; JJJ.Constants.ADD_TO_SUMMARY_DIV.hide; };	// delayScan postpones the invocation of scan with a certain timeout. // If delayScan is invoked once again during that time, the original // timeout is cancelled, and another, clean timeout is started from zero. //	// delayScan will normally be invoked when a key is pressed---this // prevents frequent re-scans while the user is typing. ARA_Functions.delayScan = function { if (JJJ.Constants.scanTimeoutId != null) { clearTimeout(JJJ.Constants.scanTimeoutId); JJJ.Constants.scanTimeoutId = null; }		JJJ.Constants.scanTimeoutId = setTimeout(JJJ.Functions.scan, 500); };	return ARA_Functions; }

function getARARules {	var ARA_Rules = []; // == Rules == // properties: // * start---the 0-based inclusive index of the first character to be replaced // * end---analogous to start, but exclusive // * replacement---the proposed wikitext // * name---this is what appears at the top of the page // * description---used as a tooltip for the name of the suggestion if (!mw.config.get('wgContentLanguage') || mw.config.get('wgContentLanguage') === 'en') { // from this line on, a level of indent is spared // The rules are stored in an array and are grouped into categories. //*****************************************************************************************	//*****************************************************************************************	// === Functions === //***missing *** ARA_Rules.push(function (s) {		var Re = "{{Reflist";var re = "{{reflist";var ore = " ";var oree = " ";		var b = [];	       var sstart;	        var eend;	        var rreplacement;	        var mySection;	        var name = document.URL;	        if (name.indexOf("section=") < 0)	        {		   if (s.indexOf(" -1 && s.indexOf(re) < 0 && s.indexOf(Re) < 0 && s.indexOf(ore) < 0 && s.indexOf(oree) < 0 && s.indexOf("{{reflist))") < 0 && s.indexOf("{{relist}}") < 0 && s.indexOf("{{Reflist|refs=") < 0 && s.indexOf("{{reflist|refs=") < 0 && document.URL.indexOf("title=Portal:") < 0) //(don't add reflist to portals)	          {	              if (s.indexOf("==References==") > -1 || s.indexOf("== References ==") > -1 || s.indexOf("==Sources==") > -1 || s.indexOf("== Sources ==") > -1)	              {	               if (s.indexOf("==References==") > -1){mySection = "==References=="; sstart = s.indexOf(mySection); eend = sstart + 14;}	               else if (s.indexOf("== References ==") > -1){mySection = "== References ==";sstart=s.indexOf(mySection);eend = sstart + 16;}	               else if (s.indexOf("==Sources==") > -1){mySection = "==Sources==";sstart=s.indexOf(mySection);eend=sstart+11;}	               else {mySection = "== Sources ==";sstart=s.indexOf(mySection);eend=sstart+13;}	               rreplacement = "==References==\n";	             }	           else if (s.indexOf("==Further reading==") > -1)	           { sstart      = s.indexOf("==Further reading=="); eend        = sstart + 19; rreplacement = "==References==\n\n\n==Further reading=="; }	          else if (s.indexOf("==External links==") > -1 || s.indexOf("== External links ==")> -1 || s.indexOf("==External links ==") >-1	                ||  s.indexOf("== External links==") > -1|| s.indexOf("== External Links ==")> -1 || s.indexOf("==External Links==") > -1) {	           if (s.indexOf("==External links==") > -1){mySection = "==External links=="; sstart = s.indexOf(mySection); eend = sstart+18;} else if(s.indexOf("== External links ==")>-1){mySection="== External links ==";sstart=s.indexOf(mySection);eend= sstart + 20;} else if(s.indexOf("== External links==")>-1){mySection="== External links==";sstart=s.indexOf(mySection);eend=sstart+19;} else if(s.indexOf("== External Links ==")>-1){mySection="== External Links ==";sstart=s.indexOf(mySection);eend= sstart + 20;} else if(s.indexOf("==External Links==")>-1){mySection="==External Links==";sstart=s.indexOf(mySection);eend=sstart+18;} else {mySection = "==External links =="; sstart = s.indexOf(mySection); eend = sstart+19;} rreplacement = "==References==\n\n\n==External links=="; }	           else if (s.indexOf("==See also==") > -1 || s.indexOf("== See also ==") > -1) { 		     var seeAlso; if (s.indexOf("==See also==") > 0) seeAlso = "==See also=="; else seeAlso = "== See also =="; var afterSeeAlso = s.split(seeAlso)[1]; if (s.indexOf("==See also==\n{{") > 0) afterSeeAlso = afterSeeAlso.slice(afterSeeAlso.indexOf("}}")); while ((afterSeeAlso.indexOf("| afterSeeAlso.indexOf("{{") == -1) && (afterSeeAlso.indexOf("[[") < afterSeeAlso.indexOf("[[Category:") || afterSeeAlso.indexOf("[[Category:") == -1))		     {			 afterSeeAlso = afterSeeAlso.slice(afterSeeAlso.indexOf("")+2);		      }	  	              sstart       = s.indexOf(afterSeeAlso);	              eend         = sstart;	              rreplacement = "\n\n==References==\n";		   }	           else if (s.indexOf("{{Authority control") > -1)	           {	             sstart                  = s.indexOf("{{Authority control");	             var completeTemplate    = s.split("{{Authority control")[1];	                 completeTemplate    = completeTemplate.slice(completeTemplate.indexOf("}}" + 2));	             var completeTemplateLoc = completeTemplate.indexOf("}}");	             eend                    = sstart + completeTemplateLoc;	             rreplacement            = "==References==\n" + completeTemplate;	           }	           else if (s.indexOf("{{DEFAULTSORT") > -1)	           {	             if (s.indexOf("}}\n\n{{DEFAULTSORT") > -1 || s.indexOf("}}\n{{DEFAULTSORT") > -1)	              {	                if (s.indexOf("}}\n\n{{DEFAULTSORT") > -1) mySection = "}}\n\n{{DEFAULTSORT";	                else mySection = "}}\n{{DEFAULTSORT";	                var completeTemplate = mySection;	                while (s.charAt(s.indexOf(completeTemplate)-1) != '{')	                  completeTemplate = s.charAt(s.indexOf(completeTemplate)-1) + completeTemplate;	                completeTemplate = "{{" + completeTemplate;	                completeTemplate = completeTemplate.split("}}")[0];	                completeTemplate = completeTemplate + "}}";	                while (s.indexOf("}}\n" + completeTemplate) > -1 || s.indexOf("}}\n\n" + completeTemplate) > -1)	                {	                    var secondTemplate = s.split("\n"+completeTemplate)[0];				secondTemplate = secondTemplate.slice(secondTemplate.lastIndexOf("{{"));			    completeTemplate   = secondTemplate+"\n"+completeTemplate;	                }	                sstart           = s.indexOf(completeTemplate);	                eend             = sstart + completeTemplate.length;	                rreplacement     = "==References==\n\n\n" + completeTemplate;	              }	             else 	             {	               sstart = s.indexOf("{{DEFAULTSORT");	               var completeTemplate    = s.split("{{DEFAULTSORT")[1];	                 completeTemplate    = completeTemplate.slice(completeTemplate.indexOf("}}" + 2));	               var completeTemplateLoc = completeTemplate.indexOf("}}");	               eend                    = sstart + completeTemplateLoc;	               rreplacement            = "==References==\n" + completeTemplate;	             }	            }	           else if (s.indexOf("[[Category:") > -1)	           {	              if (s.indexOf("}}\n\n[[Category:") > -1 || s.indexOf("}}\n[[Category:") > -1)	              {	                if (s.indexOf("}}\n\n[[Category:") > -1) mySection = "}}\n\n[[Category:";	                else mySection = "}}\n[[Category:";	                var completeTemplate = mySection;	                while (s.charAt(s.indexOf(completeTemplate)-1) != '{')	                  completeTemplate = s.charAt(s.indexOf(completeTemplate)-1) + completeTemplate;	                completeTemplate = "{{" + completeTemplate;	                completeTemplate = completeTemplate.split("}}")[0];	                completeTemplate = completeTemplate + "}}";	                while (s.indexOf("}}\n" + completeTemplate) > -1 || s.indexOf("}}\n\n" + completeTemplate) > -1)	                {	                    var secondTemplate = s.split("\n"+completeTemplate)[0];				secondTemplate = secondTemplate.slice(secondTemplate.lastIndexOf("{{"));			    completeTemplate   = secondTemplate+"\n"+completeTemplate;	                }	                sstart           = s.indexOf(completeTemplate);	                eend             = sstart + completeTemplate.length;	                rreplacement     = "==References==\n{{Reflist}}\n\n" + completeTemplate;	              }	              else	              {	                 sstart = s.indexOf("[[Category:");	                 var completeTemplate    = s.slice(s.indexOf("[[Category:"));	                     completeTemplate    = completeTemplate.slice(completeTemplate.indexOf("}}" + 2));	                 var completeTemplateLoc = completeTemplate.indexOf("}}");	                 eend                    = sstart + completeTemplateLoc;	                 rreplacement            = "==References==\n{{Reflist}}" + completeTemplate;	              }	           }	           else if (s.indexOf("{{uncat") > -1)	           {	              sstart       = s.indexOf("{{uncat") - 1;	              eend         = s.indexOf("{{uncat") - 1;	              rreplacement = "\n==References==\n{{Reflist}}\n";	           }	           else if (s.indexOf("stub}}") > -1)	           {	              var stub = "stub}}";	                 while (s.charAt(s.indexOf(stub)-1) != '{')	                   stub = s.charAt(s.indexOf(stub)-1) + stub;	                 stub = "{{" + stub;	                 sstart       = s.indexOf(stub) -1;	                 eend         = s.indexOf(stub) -1;	                 rreplacement = "\n==References==\n{{Reflist}}\n";	           }	           else	           {	              sstart = s.length;	              eend   = s.length;	              rreplacement = "\n==References==\n{{Reflist}}\n";	           }		     b.push({					start: sstart,					end: eend,					replacement: rreplacement, 					name: 'missing {{Reflist}}',					description: ' tags are present, but the reference list template is missing.'			});		}	       }	       return b;	});	//***inapplicable tag***	ARA_Rules.push(function (s) {		var b = [];	        var sstart;	        var eend;	        var rreplacement;	    //if there is an "Unreferenced" tag and there are references present		if (  (s.indexOf("{{no footnotes|date") > -1 		    || s.indexOf("{{unreferenced|date") > -1 		    || s.indexOf("{{Unreferenced|date") > -1 		    || s.indexOf("{{unsourced|date")    > -1)		    &&	          (s.indexOf("{{Reflist") > -1 	        || s.indexOf("{{reflist") > -1) 	        &&	           s.indexOf(" -1	       )			{	           var unreferencedTemplate;	           if      (s.indexOf("{{no footnotes|date") > -1) unreferencedTemplate = "{{no footnotes|date";	           else if (s.indexOf("{{unreferenced|date") > -1) unreferencedTemplate = "{{unreferenced|date";	           else if (s.indexOf("{{Unreferenced|date") > -1) unreferencedTemplate = "{{Unreferenced|date";	           else                                            unreferencedTemplate = "{{unsourced|date";			   //build the rest of the tag until we hit the end of the template or the end of the article text	           while (!unreferencedTemplate.endsWith('}}') && unreferencedTemplate.length + 1 < s.length)					unreferencedTemplate += s.charAt(s.indexOf(unreferencedTemplate) + unreferencedTemplate.length);               //if the template is on its own line, remove the entire line	           if (s.charAt(s.indexOf(unreferencedTemplate) - 1) === "\n" 	            && s.charAt(s.indexOf(unreferencedTemplate) + unreferencedTemplate.length) === '\n')	            {	               unreferencedTemplate = "\n" + unreferencedTemplate + "\n";	            }

sstart = s.indexOf(unreferencedTemplate); eend  = sstart + unreferencedTemplate.length; b.push({		   	start:       sstart,				end:         eend,				replacement: '', 				name:        'inapplicable tag',				description: 'There is an unreferenced tag on the page, but tags are present. It may no longer apply.'			   }); }		return b;	});	//***unnecessary reference groups AND missing reference groups***//	//We can use the same function to find both the unnecessary ref groups and the missing ones. Once we've found them, we can decide which	// category they fall under	ARA_Rules.push(function (s) {		var b = []; var noteGroups = ["n", "N", "note", "Note", "nb", "lower-alpha", "upper-alpha"]; //get indices of all 'group='s	   var startIndex     = 0; var searchStr     = "group="; var searchStrLen  = searchStr.length; var index         = null; var indices       = []; while ((index = s.indexOf(searchStr, startIndex)) > -1) {		   indices.push(index); startIndex = index + searchStrLen; }		var indicesLength = indices.length; for (i = 0; i < indicesLength; i++) //for each group= {			var groupIndex        = indices[i]; //index of "group=" var mySectionPrevIndex = groupIndex - 1; var groupNameIndex    = groupIndex + 6; //after "group="; var mySection         = s.substring(groupNameIndex); var groupAndName      = s.substring(groupIndex); //contains "group= ..." if (groupAndName.includes(">")) //if the ref isn't malformed groupAndName = groupAndName.substring(0, groupAndName.indexOf(">")) + ">"; //cut away anything after end of ref, and re-add gt			//else, the ref is malformed var fullRef           = groupAndName; var cutOffQuote       = false; if (mySection[0] == '"') //if quote following group declaration			{				mySection  = mySection.substring(1); //cut that character out				cutOffQuote = true;			}			//find the end of the group name			// : group=sampleName>   ...		    // : group=sampleName /> ...		    // : group="sampleName"  ...		    // : group=sampleName name="winner" ...		    // : Note: group=sample}}		    // : Note: group=sample|name=sampleName... 			var firstBracketAfterIndex = mySection.includes(">")                 ? mySection.indexOf(">")      : s.length;			var firstSlashAfterIndex   = mySection.includes("/")                 ? mySection.indexOf("/")      : s.length;			var firstQuoteAfterIndex   = mySection.includes("\"")                ? mySection.indexOf("\"")    : s.length;			var nameEqualsAfterIndex   = mySection.includes(" name=")            ? mySection.indexOf(" name=") : s.length;			var spaceAfterIndex        = mySection.includes(" ") && !cutOffQuote ? mySection.indexOf(" ")      : s.length;			var bracketAfterIndex      = mySection.includes("}")                 ? mySection.indexOf("}")      : s.length;			var vBarAfterIndex         = mySection.includes("|")                 ? mySection.indexOf("|")      : s.length;			var cutOffIndex            = firstBracketAfterIndex;			if (firstSlashAfterIndex < cutOffIndex) cutOffIndex = firstSlashAfterIndex;			if (firstQuoteAfterIndex < cutOffIndex) cutOffIndex = firstQuoteAfterIndex;		    if (nameEqualsAfterIndex < cutOffIndex) cutOffIndex = nameEqualsAfterIndex;		    if (spaceAfterIndex      < cutOffIndex) cutOffIndex = spaceAfterIndex;		    if (bracketAfterIndex    < cutOffIndex) cutOffIndex = bracketAfterIndex; if (vBarAfterIndex      < cutOffIndex) cutOffIndex = vBarAfterIndex; var groupName = mySection.substring(0, cutOffIndex); //the name of the reference group //if we already have a Reflist tag with this group name, skip this ref var refLists = [ "{{Reflist|group=" + groupName + "}}", "{{Reflist|group=" + groupName + "|", "{{reflist|group=" + groupName + "}}", "{{reflist|group=" + groupName + "|", '{{Reflist|group="' + groupName + '"', '{{reflist|group="' + groupName + '"', ];			var listAlreadyPresent = false; for (j = 0; j < refLists.length; j++) {				if (s.includes(refLists[j])) {					listAlreadyPresent = true; break; }			}			if (listAlreadyPresent) continue; //otherwise, if this is a group that we WANT to add a reflist for if (noteGroups.includes(groupName)) {	   		var startIndex       = null; var endIndex        = null; var replacementText = null; var suggestionName  = 'missing reference groups (' + groupName + ')'; var includesNotesHdr = false; //determine where to place the new list. We'll either be adding it to an existing Notes section or creating a new Notes section. // In certain cases, the Notes section may contain a ;Footnotes sub-heading, in which case we'll want to put the new list beneath that // sub-heading. // If we're creating a new Notes section, we'll want to put the new section above the References section var notesHeaderVariations = ["==Notes==", "== Notes == "]; for (j = 0; j < notesHeaderVariations.length; j++) {	   			var notesHeader = notesHeaderVariations[j]; if (s.includes(notesHeader)) {	   				var notesHeaderWithFootnotesSubHeading = notesHeader + "\n;Footnotes\n"; //determine if the section contains the ;Footnotes sub-heading if (s.includes(notesHeaderWithFootnotesSubHeading)) startIndex = s.indexOf(notesHeaderWithFootnotesSubHeading) + notesHeaderWithFootnotesSubHeading.length; else //no Footnotes sub-heading startIndex = s.indexOf(notesHeader) + notesHeader.length; endIndex       = startIndex; replacementText = "{{Reflist|group=" + groupName + "}}\n"; includesNotesHdr = true; break; }	   		}    			//otherwise, there is no Notes header, so we'll be creating one if (!includesNotesHdr) {   				referencesHeader = "==References=="; if (!s.includes(referencesHeader)) referencesHeader = "== References =="; startIndex     = s.indexOf(referencesHeader) - 1; endIndex       = startIndex; replacementText = "\n==Notes==\n{{Reflist|group=" + groupName + "}}\n"; }				b.push({					start:      startIndex,					end:         endIndex,					replacement: replacementText,					name:        suggestionName,					description: suggestionName				}); }	   	else //the ref group is "unnecessary" {		   	//else, this is a group we want to replace. //Check to see if the reference already has a name. If it doesn't, add one. If it does, just remove the group //get to the beginning of the reference while (mySectionPrevIndex >= 0 && s.charAt(mySectionPrevIndex) != '<') {		             fullRef = s.charAt(mySectionPrevIndex) + fullRef; //prepend the character --mySectionPrevIndex; //decrement index }		       //now at the beginning of the reference (or the page, if the reference was malformed) // and we already have to the end of the ref, so we have the full ref fullRef = "<" + fullRef; //re-build mySection with the group name intact, since we removed it before var addBackNum = cutOffQuote ? 7 : 6;		       mySection = s.substring(groupIndex, groupIndex + addBackNum + cutOffIndex); //whether or not the ref contains "name=" already, we'll start at the same place var startReplaceIndex = s.indexOf(fullRef) + fullRef.indexOf("group="); //start at the beginning of the "group=" //if there is already a name for this ref, we want to remove the entire "group= ". //We need to also remove any quotes around the group name if (fullRef.includes("name=")) {					var endReplaceIndex = startReplaceIndex + addBackNum + groupName.length; //end after the group name if (cutOffQuote) 		 //if we removed a quote earlier, then we know we need to also remove quotes endReplaceIndex += 1; //add to include extra quote //if there are spaces at both ends of the section we're removing, take away one of the spaces so we aren't left with two consecutive spaces if (s.charAt(startReplaceIndex-1) == ' ' && s.charAt(endReplaceIndex) == ' ') ++endReplaceIndex; //if the ref group is at the end of an open ref, check to see if we should remove an extra space preceding the group if (s.charAt(startReplaceIndex-1) == ' ' && s.charAt(endReplaceIndex) == '>') --startReplaceIndex; b.push({						start: startReplaceIndex,						end: endReplaceIndex,						replacement: "", 						name: 'unnecessary reference groups (' + groupName + ')',						description: 'Reference groups in citations are causing an error.'					}); }		        else //there isn't already a name, so just replace "group=" with "name=" { 					var endReplaceIndex = startReplaceIndex + 5; //+ "group" b.push({							start:      startReplaceIndex,							end:         endReplaceIndex,							replacement: "name", 							name:        'unnecessary reference groups (' + groupName + ')',							description: 'Reference groups in citations are causing an error.'					}); }	   	}		}		return b;	});	//***missing notelist***	ARA_Rules.push(function(s) {     		var b               = []; var theMatchWeFound = null; //potential matches var potentialMatchesWithReplacements = [ {				ref: "{{efn-ua", list: ["{{notelist-ua}}", "{{Notelist-ua}}"], name: "missing notelist-ua" },			{				ref: "{{efn-lr", list: ["{{notelist-lr}}", "{{Notelist-lr}}"], name: "missing notelist-lr" },			{				ref: "{{efn|", list: ["{{notelist}}", "{{Notelist}}"], name: "missing notelist" },			{				ref: "group=lower-alpha", list: ["{{notelist}}", "{{Notelist}}"], name: "missing notelist" }		];		//look for matches for (i = 0; i < potentialMatchesWithReplacements.length; i++) {			//if we've already found a match, break if (theMatchWeFound !== null) break; var thisMatch = potentialMatchesWithReplacements[i]; //determine if any of the lists are already present; if any are present, we do not have a match var alreadyHasList = false; for (j = 0; j < thisMatch.list.length; j++) {				if (s.includes(thisMatch.list[j])) {					alreadyHasList = true; break; }			}			if (alreadyHasList) continue; //look for the ref (remember that we've already checked above to determine if the article contains the list) if (s.includes(thisMatch.ref)) {				theMatchWeFound = { ref: thisMatch.ref, list: thisMatch.list[0], name: thisMatch.name };			}		}		//if we found a match, determine the change we need to make and where we need to make it		if (theMatchWeFound !== null) {			var startIndex      = null; var endIndex        = null; var replacementText = null; var containsNotesHdr = false; //if the article contains the ==Notes== header, we simply want to add the notelist to the Notes section var notesHdrVariations = ["==Notes==", "== Notes =="]; for (i = 0; i < notesHdrVariations.length; i++) {				var notesHdr = notesHdrVariations[i]; if (s.includes(notesHdr)) {					startIndex      = s.indexOf(notesHdr) + notesHdr.length; endIndex        = startIndex; replacementText = "\n" + theMatchWeFound.list; containsNotesHdr = true; break; }			}			//if the article does NOT contain the Notes header, then it is assumed that it contains the References header (one of the two variations) if (!containsNotesHdr) {				var refHdrSyntax = "==References=="; if (!s.includes(refHdrSyntax)) refHdrSyntax = "== References =="; startIndex     = s.indexOf(refHdrSyntax); endIndex       = s.indexOf(refHdrSyntax) + refHdrSyntax.length; replacementText = "==Notes==\n" + theMatchWeFound.list + "\n\n" + refHdrSyntax; }			//push result b.push({				start:      startIndex,				end:         endIndex,				replacement: replacementText,				name:        theMatchWeFound.name,				description: theMatchWeFound.name		     }); }		return b;	});	//***invalid tags***	ARA_Rules.push(function (s) { var b = []; var sstart; var eend; var rreplacement; var mySection; var theList; if (s.indexOf("{{Reflist|refs=") < 0 &&       // limiting factors	            s.indexOf("{{reflist|refs=") < 0 &&	            s.indexOf(" -1           &&	            (  s.indexOf("{{Reflist") > -1      || s.indexOf("{{reflist") > -1     || s.indexOf(" ") > -1 || s.indexOf(" ") > -1 ) 	          )	        {	            if (s.indexOf("{{Reflist") > -1) theList = "{{Reflist"; else if (s.indexOf("{{reflist") > -1) theList = "{{reflist";       // defining type of reflist else if (s.indexOf(" ") >-1) theList = " "; else theList = " "; if (s.lastIndexOf(" ") < 0) // if there is only the {	                mySection    = " ") > mySection.indexOf(oneRef)) {	                   while (oneRef.indexOf(" ") < 0) {	                     oneRef = oneRef + mySection.charAt(counter); ++counter; }	                }	                 oneRef = oneRef + mySection.charAt(counter+1); oneRef = oneRef.split(" ")[0]; sstart      = s.lastIndexOf(mySection); eend        = sstart + oneRef.length+11; rreplacement = "*" + oneRef; }		      b.push({					start: sstart,					end: eend,					replacement: rreplacement, 					name: 'Invalid tags',					description: ' tags are located below the {{Reflist}} template, causing an error.'			     }); }	       }		return b;	});	//***broken ref tags***	ARA_Rules.push(function (s) { var b = []; var sstart; var eend; var rreplacement; if (s.indexOf("./ref>") > -1 || s.indexOf(" -1 || s.indexOf(",/ref>") > -1 || s.indexOf(" -1) {	         if (s.indexOf("./ref>") > -1) {	          sstart       = s.indexOf("./ref>"); eend        = sstart + 6; rreplacement = " "; }	         else if (s.indexOf(" -1) {	          sstart       = s.indexOf(" -1) {	          sstart       = s.indexOf("</ref?"); eend        = sstart + 6; rreplacement = " "; }	         else {	          sstart       = s.indexOf(",/ref"); eend        = sstart + 6; rreplacement = " "; }		    b.push({					start: sstart,					end: eend,					replacement: rreplacement, 					name: 'broken tags',					description: 'Broken tags exist on the page.'			}); }		return b;	});	//***broken reflist***	ARA_Rules.push(function (s) {		var b = []; var matches = [ {       		original:    "{{reflist))", replacement: "{{Reflist}}" },   		{    			original:    "{{relist}}", replacement: "{{Reflist}}" },   		{    			original:    "{{reflist group", replacement: "{{Reflist|group" }, 			{				original:   "{{Reflist|2}\n", replacement: "{{Reflist|2}}\n" },			{				original:   "{{Reflist||group=", replacement: "{{Reflist|group=" }		];       for (i = 0; i < matches.length; i++) {       	var match = matches[i]; if (s.includes(match.original)) {       		var startIndex = s.indexOf(match.original); b.push({					start:      startIndex,					end:         startIndex + match.original.length,					replacement: match.replacement, 					name:        "broken {{Reflist}}",					description: "There is a broken Reflist template on the page"				}); }       }		return b;	});	//***empty citations***	ARA_Rules.push(function (s) { var b = []; var sstart; var eend; var rreplacement; if (s.indexOf(" ") > -1) {	          var commentB = s.indexOf(""); if (s.indexOf("")	             ) sstart      = s.indexOf(" "); eend        = sstart + 11; rreplacement = ""; b.push({					start: sstart,					end: eend,					replacement: rreplacement, 					name: 'empty citations',					description: 'Empty citations exist on the page.'			}); }		return b;	});	//***unnecessary whitespace in citation***	ARA_Rules.push(function (s) { var b = []; var replaceableStrings = [" url = ", " url= ", "url =", "url= ", " url=", "group ="," website = ", " website= ", "website =", "website= ", " website="," publisher = ", " publisher= ", "publisher =", "publisher= ", " publisher="]; for (i = 0; i < replaceableStrings.length; i++) {	   	var replaceableString = replaceableStrings[i]; if (s.includes(replaceableString)) {	   		b.push({					start: s.indexOf(replaceableString),					end:   s.indexOf(replaceableString) + replaceableString.length,					replacement: replaceableString.replace(" ", ""),					name: 'extra whitespace in citation (' + replaceableString + ')',					description: 'extra whitespace in citation (' + replaceableString + ')'				}); }	   }		return b;	});	//***URL scheme error (Capital)***	ARA_Rules.push(function (s) { var b      = []; var matches = ["A", "B", "C", "D", "E", "F", "G", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]; for (i = 0; i < matches.length; i++) {			var match = "|url=" + matches[i]; if (s.includes(match)) {				b.push({					start:      s.indexOf(match),					end:         s.indexOf(match) + 5,					replacement: "|url=http://",					name:        'URL scheme error (Capital)',					description: 'Invalid URL parameter',					sup1:        'prepend "http://"'				}); }		}

return b;	});	//***URL scheme error***	ARA_Rules.push(function (s) {		var b      = []; var matches = ["a", "b", "c", "d", "e", "f", "g", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]; for (i = 0; i < matches.length; i++) {			var match = "|url=" + matches[i]; if (s.includes(match)) {				b.push({					start:      s.indexOf(match),					end:         s.indexOf(match) + 5,					replacement: "|url=http://",					name:        'URL scheme error',					description: 'Invalid URL parameter'				}); }		}		return b;	});	//***space in URL parameter OR URL ends with period***	ARA_Rules.push(function (s) { var b = []; //get indices of all '|url='s	   var startIndex = 0; var searchStr = "|url="; var searchStrLen = searchStr.length; var index, indices = []; while ((index = s.indexOf(searchStr, startIndex)) > -1) {		   indices.push(index); startIndex = index + searchStrLen; }		var indicesLength = indices.length; for (i = 0; i < indicesLength; i++) //for each |url= {			var urlStartIndex = indices[i] + searchStr.length; var indexOnward  = s.substring(urlStartIndex); //+searchStr.length to exclude "|url=" //get to the actual beginning of the URL if there are spaces or newlines after the "url=" and before the start of the url while ((indexOnward[0] == ' ' || indexOnward[0] == '\n')) {				indexOnward = indexOnward.substring(1); //cut off the first character ++urlStartIndex; }			var fullRef         = indexOnward; var fullRefPrevIndex = urlStartIndex - 1; //indices of various characters in the citation var firstBracketAfterIndex = indexOnward.includes("}") ? indexOnward.indexOf("}") : s.length; var firstBarAfterIndex    = indexOnward.includes("|") ? indexOnward.indexOf("|") : s.length; //get to the end of the citation fullRef = fullRef.substring(0, firstBracketAfterIndex); //get to the beginning of the citation while (fullRefPrevIndex >= 0 && s.charAt(fullRefPrevIndex) != '{') {	             fullRef = s.charAt(fullRefPrevIndex) + fullRef; //prepend the character --fullRefPrevIndex; //decrement index }	   	//now we have the full ref. //get the entire URL parameter. The parameter should either end with another | or }} for the end of the ref (if not malformed) //find the nearest delimeter var cutOffIndex = firstBarAfterIndex; if (firstBracketAfterIndex < cutOffIndex) cutOffIndex = firstBracketAfterIndex; var url = indexOnward.substring(0, cutOffIndex).trim; //the url parameter //If the url ends with a period if (url.endsWith('.')) {				b.push({						start:       urlStartIndex + url.length - 1,						end:          urlStartIndex + url.length,						replacement:  '', 						name:         'URL parameter ending with a period',						description:  'URLs cannot end with periods',						sup1:         '[Remove trailing period]'					}); }		}		return b;	});	//***invalid ref position***	ARA_Rules.push(function (s) { var b = []; var sstart; var eend; var rreplacement; var spacingType; var mySection; if (s.indexOf("  -1 || s.indexOf(" .") > -1 || s.indexOf(" ,") > -1) {	         if (s.indexOf("  -1) {	             spacingType = "  -1) {	              var oneRef   = " .";               //oneRef =. var counter = s.indexOf(oneRef) - 1; while (oneRef.indexOf("content. oneRef = oneRef.slice(0,oneRef.length-1);   //oneRef = content sstart      = s.indexOf(oneRef); eend        = sstart + oneRef.length + 1; rreplacement = "." + oneRef; }	         else if (s.indexOf(" ,") > -1) {	             var oneRef   = " ,";               //oneRef = , var counter = s.indexOf(oneRef) - 1; while (oneRef.indexOf("content , oneRef = oneRef.slice(0,oneRef.length-1);   //oneRef = <ref..>content sstart      = s.indexOf(oneRef); eend        = sstart + oneRef.length + 1; rreplacement = "," + oneRef; }		    b.push({					start: sstart,					end: eend,					replacement: rreplacement, 					name: 'invalid ref position',					description: 'invalid ref position.'			}); }		return b;	});	/*	//***citation with accessdate and no URL***	ARA_Rules.push(function (s) { var b = []; var sstart; var eend; var citation; var rreplacement; var restOfS; if (s.indexOf("{{cite") > -1) {	          citation = s.split("{{cite")[1]; citation = citation.split("}}")[0]; //move to next citation if the current one is fine if (citation.indexOf("accessdate") < 0 || citation.indexOf("url") > -1) {	             restOfS  = s.slice(s.indexOf(citation)); restOfS = restOfS.slice(restOfS.indexOf("}}")); citation = restOfS.split("{{cite")[1]; citation = citation.split("}}")[0]; }	          //current citation has the problem else if (citation.indexOf("url") < 0 && citation.indexOf("accessdate") > -1) {	                var accessdateSection = "accessdate"; while (accessdateSection.indexOf("|") < 0 && accessdateSection.indexOf("{{") < 0 && accessdateSection.length < 30) {	                    accessdateSection = citation.charAt(citation.indexOf(accessdateSection)-1) + accessdateSection; }	               // accessdateSection = "|" + accessdateSection; var theAccessdate = "accessdate"; while (theAccessdate.indexOf("|") < 0 && theAccessdate.indexOf("}}") < 0 && theAccessdate.length < 30) {	                   theAccessdate = theAccessdate + citation.charAt(theAccessdate.length + 1); }	                sstart = s.indexOf(citation) + citation.indexOf(theAccessdate); //beginning of section: "|accessdate=" eend  = sstart + theAccessdate.length; //- 10 + theAccessdate.length; //end of accessdate section (beginning of section - overlap + section		        b.push({ start: sstart, end: eend, replacement: '', name: 'CS1 error: accessdate without URL', description: 'accessdate is listed in citation but URL is not' });	          }    	        }  		return b;	});*/ //***References in Portals, per WP talk:Portal guidelines#References in portals*** ARA_Rules.push(function (s) {		var b = [];	   var sstart;	    var eend;	    var rreplacement;	    var name;	    if (document.URL.indexOf("title=Portal:") > -1 && s.indexOf("<ref") > -1) //page is a portal page that contains references	    {	    	sstart = s.indexOf("<ref");	    	eend   = s.indexOf(" ") + 6; //may possibly be overwritten below	    	rreplacement = ""; //just take out the reference	    	//if a reference has been previously named and the current reference is just a name, such as	    	//, we'll need to handle this differently since the '/>' comes before another ' '	    	if (s.indexOf(" ")); //cut at the the 	    			if (entireRef.indexOf("/>") > -1) //assume by this that the reference is an empty named reference	    				eend = s.indexOf(entireRef) + entireRef.indexOf("/>") + 2; //stop at the "/>"	    		}	    		else //assume that the reference MUST be a named ref since there is no closing ref tag after this point eend = s.indexOf(entireRef) + entireRef.indexOf("/>") + 2; //stop at the "/>" }	   	b.push({				start: sstart,				end: eend,				replacement: rreplacement, 				name: 'references in Portal page',				description: 'According to Portal guidelines, Portal pages should not contain references.'			}); }		return b;	});	//***Reflists in Portals, per WP talk:Portal guidelines#References in portals***	ARA_Rules.push(function (s) { var b = []; var sstart; var eend; var rreplacement; var name; if (document.URL.indexOf("title=Portal:") > -1 && s.indexOf("{{Reflist}}") > -1) //page is a portal page that contains a Reflist {	   	sstart = s.indexOf("{{Reflist}}"); eend  = sstart + 11; rreplacement = ""; //just take out the reflist b.push({				start: sstart,				end: eend,				replacement: rreplacement, 				name: '{{Reflist}} in Portal page',				description: 'According to Portal guidelines, Portal pages should not contain {{Reflist}}s'			}); }		return b;	});	//***Empty Persondata template***	ARA_Rules.push(function (s) { var b = []; var sstart; var eend; var rreplacement; var name; if (s.indexOf("{{Persondata}}") > -1) //blank persondata template {	   	sstart       = s.indexOf("{{Persondata}}"); eend        = sstart + 14; rreplacement = "{{Persondata\n| NAME             = \n| ALTERNATIVE NAMES = \n| SHORT DESCRIPTION = \n| DATE OF BIRTH     = \n| PLACE OF BIRTH    = \n| DATE OF DEATH     = \n| PLACE OF DEATH    = \n}}"; b.push({					start: sstart,					end: eend,					replacement: rreplacement, 					name: 'empty Persondata template',					description: 'The Persondata template missing required parameters.'			}); }		return b;	});	//***missing Name in Persondata***	ARA_Rules.push(function (s) { var b = []; var sstart; var eend; var rreplacement; var name; if (s.indexOf("{{Persondata\n| NAME") > -1) //non-empty persondata template present {	   	var afterName = s.substring(s.indexOf("{{Persondata\n| NAME")); afterName = afterName.substring(0, afterName.indexOf("}}")); //get just the persondata template var afterNameEq = afterName.indexOf("="); //first '=' in persondata is what we want afterName = afterName.substring(afterName.indexOf("| NAME")); afterName = afterName.substring(afterName.indexOf("NAME"), afterName.indexOf("\n")); //now we just have the NAME line if (afterName.indexOf("=") > -1) //contains the necessary equal sign {	   		var afterEq = afterName.substring(afterName.indexOf("=") + 1); //get everything after the equals sign while (afterEq.length > 0 && afterEq.charAt(0) == ' ') //while beginning of string is whitespace afterEq = afterEq.substring(1); //remove whitespace if (afterEq == null || afterEq.length == 0) //no name is present {	   			name = document.URL; if (name.indexOf("https") > -1) name = name.split("https://en.wikipedia.org/w/index.php?title=")[1]; else name = name.split("http://en.wikipedia.org/w/index.php?title=")[1]; name = name.split("&action=edit")[0]; while (name.indexOf("_") > -1) name = name.replace("_"," "); name = name.replace("&action=submit",""); String.prototype.countWords = function{ return this.split(/\s+/).length;} if (name.indexOf("%C3%A1") > -1) name=name.replace("%C3%A1","á"); if (name.indexOf("%C4%87") > -1) name=name.replace("%C4%87","ć"); if (name.indexOf("%C5%82") > -1) name=name.replace("%C5%82","ł"); if (name.indexOf("%C3%B3")> -1) name=name.replace("%C3%B3","ó"); if (name.indexOf("%C5%9B") > -1) name=name.replace("%C5%9B","ś"); if (name.indexOf("%C3%BC") > -1) name=name.replace("%C3%BC","ü"); if (name.indexOf("%C5%BE") > -1) name=name.replace("%C5%BE","ž"); if (name.indexOf("%C5%A0") > -1) name=name.replace("%C5%A0","Š"); if (name.indexOf("%E2%80%93")>-1)name=name.replace("%E2%80%93","–"); if (name.indexOf("%27")   > -1) name=name.replace("%27","'"); if (name.countWords == 1 || name.indexOf(" of ") > -1 || name.indexOf(" the ") > -1) name = name; else if (name.countWords == 2) { 		               var firstName = name.split(" ")[0]; var lastName = name.split(" ")[1]; if (lastName.charAt(0) == "(")				       {var reversed = name;}				        else				        {var reversed = lastName.concat(", ").concat(firstName);}				        name = reversed;		            }		            else if (name.countWords == 3)		            {		                var firstName = name.slice(0,name.indexOf(" "));		                var midName   = name.split(" ")[1];				        var lastName  = name.split(" ")[2];				        if (midName == "of" || midName == "the")				        {  var reversed = name;}				        else if (lastName.charAt(0) == "(") {var reversed = midName.concat(", ").concat(firstName).concat(" ").concat(lastName);} else {var reversed = lastName.concat(", ").concat(firstName).concat(" ").concat(midName);} name = reversed; }		          else if (name.countWords == 4) {		             var firstName = name.slice(0,name.indexOf(" ")); var midName  = name.split(" ")[1]; var lastName = name.split(" ")[2]; var name4    = name.split(" ")[3]; if (name4.charAt(name4.length-1) != ")")				     {var reversed = name4.concat(", ").concat(firstName).concat(" ").concat(midName).concat(" ").concat(lastName);}				      else				      {var reversed = midName.concat(", ").concat(firstName).concat(" ").concat(lastName).concat(" ").concat(name4);}				      name = reversed;		           }	               sstart       = s.indexOf("{{Persondata\n| NAME") + afterNameEq; //location of '='	               eend         = sstart + 1;	               rreplacement = "= " + name;				     b.push({ start: sstart, end: eend, replacement: rreplacement, name: 'missing Name in Persondata', description: 'The Persondata template is missing the \"Name\" parameter.' });	   		}	    	}	    }		return b;	}); //***publisher parameter contains invalid markup*** ARA_Rules.push(function (s) {		var b = [];

//get indices of all '|publisher='s	   var startIndex = 0; var searchStr = "|publisher="; var searchStrLen = searchStr.length; var index, indices = []; while ((index = s.indexOf(searchStr, startIndex)) > -1) {		   indices.push(index); startIndex = index + searchStrLen; }		var indicesLength = indices.length; for (i = 0; i < indicesLength; i++) //for each |publisher= {			var pubStartIndex = indices[i] + searchStr.length; var indexOnward  = s.substring(pubStartIndex); //+searchStr.length to exclude "|publisher=\'\'" //get to the actual beginning of the Publisher if there are spaces or newlines after the "publisher=" and before the start of the text while ((indexOnward[0] == ' ' || indexOnward[0] == '\n')) {				indexOnward = indexOnward.substring(1); //cut off the first character ++pubStartIndex; }			var fullRef         = indexOnward; var fullRefPrevIndex = pubStartIndex - 1; //indices of various characters in the citation var firstBracketAfterIndex = indexOnward.includes("}") ? indexOnward.indexOf("}") : s.length; var firstBarAfterIndex    = indexOnward.includes("|") ? indexOnward.indexOf("|") : s.length; //get to the end of the citation fullRef = fullRef.substring(0, firstBracketAfterIndex); //get to the beginning of the citation while (fullRefPrevIndex >= 0 && s.charAt(fullRefPrevIndex) != '{') {	             fullRef = s.charAt(fullRefPrevIndex) + fullRef; //prepend the character --fullRefPrevIndex; //decrement index }	   	//now we have the full ref. //get the entire publisher parameter. The parameter should either end with another | or }} for the end of the ref (if not malformed) //find the nearest delimeter var cutOffIndex = firstBarAfterIndex; if (firstBracketAfterIndex < cutOffIndex) cutOffIndex = firstBracketAfterIndex; var pub = indexOnward.substring(0, cutOffIndex).trim; //the pub parameter //If the publisher ends with markup if (pub.endsWith('\'\'')) {				b.push({					start:       pubStartIndex + pub.length - 2,					end:          pubStartIndex + pub.length,					replacement:  '',					name:         '|publisher= ends with invalid markup',					description:  '|publisher= ends with invalid markup',				}); }		}		return b;	});	ARA_Rules.push(function (s) { var b = []; //If the publisher begins with markup var replaceableStrings = ["publisher=\'\'"]; for (i = 0; i < replaceableStrings.length; i++) {			var replaceableString = replaceableStrings[i]; if (s.includes(replaceableString)) {				b.push({					start: s.indexOf(replaceableString),					end:  s.indexOf(replaceableString) + replaceableString.length,					replacement: replaceableString.replace("\'\'", ""), 					name:         '|publisher= begins with invalid markup',					description:  '|publisher= begins with invalid markup',				}); }		}		return b;	});	} // end if mw.config.get('wgContentLanguage') === 'en'	return ARA_Rules; } //