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.

// <nowiki>

// 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 = $('<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 = $('<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<String, int>

	

	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 '<a href="' + url + '" target="_blank">' + (text || toWhat) + '</a>';

	};

	

	// === 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 (cachex != null) ? cachex : (cachex = 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.plainStarti + 1 != null) && (obj.plainStarti + 1 <= start)) {

				i++;

			}

			var j = i;

			while ((obj.plainStartj + 1 != null) && (obj.plainStartj + 1 <= end)) {

				j++;

			}

			obj.changed.range = document.createRange();

			obj.changed.range.setStart(obj.plainNodei], start - obj.plainStarti]);

			obj.changed.range.setEnd(obj.plainNodej], end - obj.plainStartj]);

			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.suggestionsi];

			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.Rulesi](s); //execute rule

			for (var j = 0; j < a.length; j++) //for each suggestion pushed by the rule

			{

				var returned_suggestion = aj];

				

				//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.translations]) {

			s = JJJ.Constants.translations];

		}

		var index = 1;

		while (argumentsindex]) {

			s = s.replace('$' + index, argumentsindex]); // 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.suggestionsk];

		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.suggestionsk];

		// 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.appliedSuggestionssuggestion.name == null) {

				JJJ.Constants.appliedSuggestionssuggestion.name = 1;

			} else {

				JJJ.Constants.appliedSuggestionssuggestion.name++;

			}

			var a = [];

			for (var i in JJJ.Constants.appliedSuggestions) {

				a.push(i);

			}

			a.sort(function (x, y) {

				return (JJJ.Constants.appliedSuggestionsx > JJJ.Constants.appliedSuggestionsy]) ? -1 :

					   (JJJ.Constants.appliedSuggestionsx < JJJ.Constants.appliedSuggestionsy]) ? 1 :

					   (x < y) ? -1 : (x > y) ? 1 : 0;

			});

			var s = '';

			for (var i = 0; i < a.length; i++) {

				var count = JJJ.Constants.appliedSuggestionsai]];

				s += ', ' + ((count == 1) ? ai : (count + 'x ' + ai]));

			}

			// Cut off the leading ``, '' and add ``using ARA''

	                s = JJJ.Functions._(

					'fixed [[Help:CS1 errors#apostrophe markup|CS1 \'\'markup\'\' error(s)]] likely via [[User:MJL/ARA-light|script]]',

					s.substring(2)

	                        );

	               

			// 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.suggestionsk];



		// 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.appliedSuggestionssuggestion.name == null) {

				JJJ.Constants.appliedSuggestionssuggestion.name = 1;

			} else {

				JJJ.Constants.appliedSuggestionssuggestion.name++;

			}

			var a = [];

			for (var i in JJJ.Constants.appliedSuggestions) {

				a.push(i);

			}

			a.sort(function (x, y) {

				return (JJJ.Constants.appliedSuggestionsx > JJJ.Constants.appliedSuggestionsy]) ? -1 :

					   (JJJ.Constants.appliedSuggestionsx < JJJ.Constants.appliedSuggestionsy]) ? 1 :

					   (x < y) ? -1 : (x > y) ? 1 : 0;

			});

			var s = '';

			for (var i = 0; i < a.length; i++) {

				var count = JJJ.Constants.appliedSuggestionsai]];

				s += ', ' + ((count == 1) ? ai : (count + 'x ' + ai]));

			}

			// Cut off the leading ``, '' and add ``using ARA''

	                s = JJJ.Functions._(

					'fixed [[Help:CS1 errors#apostrophe markup|CS1 \'\'markup\'\' error(s)]] likely via [[User:MJL/ARA-light|script]]',

					s.substring(2)

	                        );

	               

			// 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.suggestionsk];

		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.appliedSuggestionssuggestion.name == null) {

				JJJ.Constants.appliedSuggestionssuggestion.name = 1;

			} else {

				JJJ.Constants.appliedSuggestionssuggestion.name++;

			}

			var a = [];

			for (var i in JJJ.Constants.appliedSuggestions) {

				a.push(i);

			}

			a.sort(function (x, y) {

				return (JJJ.Constants.appliedSuggestionsx > JJJ.Constants.appliedSuggestionsy]) ? -1 :

					   (JJJ.Constants.appliedSuggestionsx < JJJ.Constants.appliedSuggestionsy]) ? 1 :

					   (x < y) ? -1 : (x > y) ? 1 : 0;

			});

			var s = '';

			for (var i = 0; i < a.length; i++) {

				var count = JJJ.Constants.appliedSuggestionsai]];

				s += ', ' + ((count == 1) ? ai : (count + 'x ' + ai]));

			}

			// Cut off the leading ``, '' and add ``using ARA''

	                s = JJJ.Functions._(

					'fixed [[Help:CS1 errors#apostrophe markup|CS1 \'\'markup\'\' error(s)]] likely via [[User:MJL/ARA-light|script]]',

					s.substring(2)

	                        );

	               

			// 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 ===

	

	/* ITALIC MARKUP HERE */

	//***publisher parameter contains italic 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 = indicesi + 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 ((indexOnward0 == ' ' || indexOnward0 == '\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 |publisher= ends with italic markup

			if (pub.endsWith('\'\''))

			{

				b.push({

					start:        pubStartIndex + pub.length - 2,

					end:          pubStartIndex + pub.length,

					replacement:  '',

					name:         '|publisher= ends with italic markup',

					description:  '|publisher= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the publisher begins with italic markup

		var replaceableStrings = "publisher=\'\'", " publisher = \'\'", " publisher= \'\'", "publisher =\'\'", "publisher= \'\'", " publisher=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|publisher= begins with italic markup',

					description:  '|publisher= begins with italic markup',

				});

			}

		}

		return b;

	});

	//***website parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|website='s

	    var startIndex = 0;

	    var searchStr = "|website=";

	    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 |website=

		{

			var webStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(webStartIndex); //+searchStr.length to exclude "|website=\'\'"

			

			//get to the actual beginning of the website if there are spaces or newlines after the "website=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++webStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = webStartIndex - 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 website 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 web = indexOnward.substring(0, cutOffIndex).trim(); //the web parameter

			

			//If the website ends with italic markup

			if (web.endsWith('\'\''))

			{

				b.push({

					start:        webStartIndex + web.length - 2,

					end:          webStartIndex + web.length,

					replacement:  '',

					name:         '|website= ends with italic markup',

					description:  '|website= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the website begins with italic markup

		var replaceableStrings = "website=\'\'", " website = \'\'", " website= \'\'", "website =\'\'", "website= \'\'", " website=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|website= begins with italic markup',

					description:  '|website= begins with italic markup',

				});

			}

		}

		return b;

	});

	

		//***magazine parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|magazine='s

	    var startIndex = 0;

	    var searchStr = "|magazine=";

	    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 |magazine=

		{

			var magStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(magStartIndex); //+searchStr.length to exclude "|magazine=\'\'"

			

			//get to the actual beginning of the magazine if there are spaces or newlines after the "magazine=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++magStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = magStartIndex - 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 magazine 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 mag = indexOnward.substring(0, cutOffIndex).trim(); //the mag parameter

			

			//If |magazine= ends with italic markup

			if (mag.endsWith('\'\''))

			{

				b.push({

					start:        magStartIndex + mag.length - 2,

					end:          magStartIndex + mag.length,

					replacement:  '',

					name:         '|magazine= ends with italic markup',

					description:  '|magazine= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the magazine begins with italic markup

		var replaceableStrings = "magazine=\'\'", " magazine = \'\'", " magazine= \'\'", "magazine =\'\'", "magazine= \'\'", " magazine=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|magazine= begins with italic markup',

					description:  '|magazine= begins with italic markup',

				});

			}

		}

		return b;

	});

	//***work parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|work='s

	    var startIndex = 0;

	    var searchStr = "|work=";

	    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 |work=

		{

			var worStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(worStartIndex); //+searchStr.length to exclude "|work=\'\'"

			

			//get to the actual beginning of the work if there are spaces or newlines after the "work=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++worStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = worStartIndex - 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 work 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 wor = indexOnward.substring(0, cutOffIndex).trim(); //the wor parameter

			

			//If the work ends with italic markup

			if (wor.endsWith('\'\''))

			{

				b.push({

					start:        worStartIndex + wor.length - 2,

					end:          worStartIndex + wor.length,

					replacement:  '',

					name:         '|work= ends with italic markup',

					description:  '|work= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the work begins with italic markup

		var replaceableStrings = "work=\'\'", " work = \'\'", " work= \'\'", "work =\'\'", "work= \'\'", " work=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|work= begins with italic markup',

					description:  '|work= begins with italic markup',

				});

			}

		}

		return b;

	});

	

		//***periodical parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|periodical='s

	    var startIndex = 0;

	    var searchStr = "|periodical=";

	    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 |periodical=

		{

			var perStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(perStartIndex); //+searchStr.length to exclude "|periodical=\'\'"

			

			//get to the actual beginning of the periodical if there are spaces or newlines after the "periodical=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++perStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = perStartIndex - 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 periodical 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 per = indexOnward.substring(0, cutOffIndex).trim(); //the per parameter

			

			//If |periodical= ends with italic markup

			if (per.endsWith('\'\''))

			{

				b.push({

					start:        perStartIndex + per.length - 2,

					end:          perStartIndex + per.length,

					replacement:  '',

					name:         '|periodical= ends with italic markup',

					description:  '|periodical= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the periodical begins with italic markup

		var replaceableStrings = "periodical=\'\'", " periodical = \'\'", " periodical= \'\'", "periodical =\'\'", "periodical= \'\'", " periodical=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|periodical= begins with italic markup',

					description:  '|periodical= begins with italic markup',

				});

			}

		}

		return b;

	});

	//***newspaper parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|newspaper='s

	    var startIndex = 0;

	    var searchStr = "|newspaper=";

	    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 |newspaper=

		{

			var nwpStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(nwpStartIndex); //+searchStr.length to exclude "|newspaper=\'\'"

			

			//get to the actual beginning of the newspaper if there are spaces or newlines after the "newspaper=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++nwpStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = nwpStartIndex - 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 newspaper 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 nwp = indexOnward.substring(0, cutOffIndex).trim(); //the nwp parameter

			

			//If the newspaper ends with italic markup

			if (nwp.endsWith('\'\''))

			{

				b.push({

					start:        nwpStartIndex + nwp.length - 2,

					end:          nwpStartIndex + nwp.length,

					replacement:  '',

					name:         '|newspaper= ends with italic markup',

					description:  '|newspaper= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the newspaper begins with italic markup

		var replaceableStrings = "newspaper=\'\'", " newspaper = \'\'", " newspaper= \'\'", "newspaper =\'\'", "newspaper= \'\'", " newspaper=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|newspaper= begins with italic markup',

					description:  '|newspaper= begins with italic markup',

				});

			}

		}

		return b;

	});

		//***journal parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|journal='s

	    var startIndex = 0;

	    var searchStr = "|journal=";

	    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 |journal=

		{

			var jorStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(jorStartIndex); //+searchStr.length to exclude "|journal=\'\'"

			

			//get to the actual beginning of the journal if there are spaces or newlines after the "journal=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++jorStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = jorStartIndex - 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 journal 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 jor = indexOnward.substring(0, cutOffIndex).trim(); //the jor parameter

			

			//If |journal= ends with italic markup

			if (jor.endsWith('\'\''))

			{

				b.push({

					start:        jorStartIndex + jor.length - 2,

					end:          jorStartIndex + jor.length,

					replacement:  '',

					name:         '|journal= ends with italic markup',

					description:  '|journal= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the journal begins with italic markup

		var replaceableStrings = "journal=\'\'", " journal = \'\'", " journal= \'\'", "journal =\'\'", "journal= \'\'", " journal=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|journal= begins with italic markup',

					description:  '|journal= begins with italic markup',

				});

			}

		}

		return b;

	});

	

	

	/* BOLD MARKUP HERE */

		//***publisher parameter contains bold 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 = indicesi + 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 ((indexOnward0 == ' ' || indexOnward0 == '\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 |publisher= ends with bold markup

			if (pub.endsWith('\'\'\''))

			{

				b.push({

					start:        pubStartIndex + pub.length - 2,

					end:          pubStartIndex + pub.length,

					replacement:  '',

					name:         '|publisher= ends with bold markup',

					description:  '|publisher= ends with bold markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the publisher begins with bold markup

		var replaceableStrings = "publisher=\'\'\'", " publisher = \'\'\'", " publisher= \'\'\'", "publisher =\'\'\'", "publisher= \'\'\'", " publisher=\'\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'\'", ""), 

					name:         '|publisher= begins with bold markup',

					description:  '|publisher= begins with bold markup',

				});

			}

		}

		return b;

	});

	//***website parameter contains bold markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|website='s

	    var startIndex = 0;

	    var searchStr = "|website=";

	    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 |website=

		{

			var webStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(webStartIndex); //+searchStr.length to exclude "|website=\'\'\'"

			

			//get to the actual beginning of the website if there are spaces or newlines after the "website=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++webStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = webStartIndex - 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 website 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 web = indexOnward.substring(0, cutOffIndex).trim(); //the web parameter

			

			//If the website ends with bold markup

			if (web.endsWith('\'\'\''))

			{

				b.push({

					start:        webStartIndex + web.length - 2,

					end:          webStartIndex + web.length,

					replacement:  '',

					name:         '|website= ends with bold markup',

					description:  '|website= ends with bold markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the website begins with bold markup

		var replaceableStrings = "website=\'\'\'", " website = \'\'\'", " website= \'\'\'", "website =\'\'\'", "website= \'\'\'", " website=\'\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'\'", ""), 

					name:         '|website= begins with bold markup',

					description:  '|website= begins with bold markup',

				});

			}

		}

		return b;

	});

	

	/* TECHNICAL - HELPS SCRIPT FUNCTION */

	//***unnecessary whitespace in citation***

	ARA_Rules.push(function (s) {

		var b = [];

	   

	    var replaceableStrings = " publisher=","publisher ="," publisher =", " website=","website ="," website =", " magazine=","magazine ="," magazine =", " work=","work ="," work =", " periodical=","periodical ="," periodical =", " newspaper=","newspaper ="," newspaper =", " journal=", "journal ="," journal= "];

	    for (i = 0; i < replaceableStrings.length; i++)

	    {

	    	var replaceableString = replaceableStringsi];

	    	

	    	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;

	});

	

	} // end if mw.config.get('wgContentLanguage') === 'en'

	

	return ARA_Rules;

}

// </nowiki>
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.

// <nowiki>

// 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 = $('<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 = $('<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<String, int>

	

	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 '<a href="' + url + '" target="_blank">' + (text || toWhat) + '</a>';

	};

	

	// === 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 (cachex != null) ? cachex : (cachex = 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.plainStarti + 1 != null) && (obj.plainStarti + 1 <= start)) {

				i++;

			}

			var j = i;

			while ((obj.plainStartj + 1 != null) && (obj.plainStartj + 1 <= end)) {

				j++;

			}

			obj.changed.range = document.createRange();

			obj.changed.range.setStart(obj.plainNodei], start - obj.plainStarti]);

			obj.changed.range.setEnd(obj.plainNodej], end - obj.plainStartj]);

			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.suggestionsi];

			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.Rulesi](s); //execute rule

			for (var j = 0; j < a.length; j++) //for each suggestion pushed by the rule

			{

				var returned_suggestion = aj];

				

				//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.translations]) {

			s = JJJ.Constants.translations];

		}

		var index = 1;

		while (argumentsindex]) {

			s = s.replace('$' + index, argumentsindex]); // 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.suggestionsk];

		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.suggestionsk];

		// 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.appliedSuggestionssuggestion.name == null) {

				JJJ.Constants.appliedSuggestionssuggestion.name = 1;

			} else {

				JJJ.Constants.appliedSuggestionssuggestion.name++;

			}

			var a = [];

			for (var i in JJJ.Constants.appliedSuggestions) {

				a.push(i);

			}

			a.sort(function (x, y) {

				return (JJJ.Constants.appliedSuggestionsx > JJJ.Constants.appliedSuggestionsy]) ? -1 :

					   (JJJ.Constants.appliedSuggestionsx < JJJ.Constants.appliedSuggestionsy]) ? 1 :

					   (x < y) ? -1 : (x > y) ? 1 : 0;

			});

			var s = '';

			for (var i = 0; i < a.length; i++) {

				var count = JJJ.Constants.appliedSuggestionsai]];

				s += ', ' + ((count == 1) ? ai : (count + 'x ' + ai]));

			}

			// Cut off the leading ``, '' and add ``using ARA''

	                s = JJJ.Functions._(

					'fixed [[Help:CS1 errors#apostrophe markup|CS1 \'\'markup\'\' error(s)]] likely via [[User:MJL/ARA-light|script]]',

					s.substring(2)

	                        );

	               

			// 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.suggestionsk];



		// 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.appliedSuggestionssuggestion.name == null) {

				JJJ.Constants.appliedSuggestionssuggestion.name = 1;

			} else {

				JJJ.Constants.appliedSuggestionssuggestion.name++;

			}

			var a = [];

			for (var i in JJJ.Constants.appliedSuggestions) {

				a.push(i);

			}

			a.sort(function (x, y) {

				return (JJJ.Constants.appliedSuggestionsx > JJJ.Constants.appliedSuggestionsy]) ? -1 :

					   (JJJ.Constants.appliedSuggestionsx < JJJ.Constants.appliedSuggestionsy]) ? 1 :

					   (x < y) ? -1 : (x > y) ? 1 : 0;

			});

			var s = '';

			for (var i = 0; i < a.length; i++) {

				var count = JJJ.Constants.appliedSuggestionsai]];

				s += ', ' + ((count == 1) ? ai : (count + 'x ' + ai]));

			}

			// Cut off the leading ``, '' and add ``using ARA''

	                s = JJJ.Functions._(

					'fixed [[Help:CS1 errors#apostrophe markup|CS1 \'\'markup\'\' error(s)]] likely via [[User:MJL/ARA-light|script]]',

					s.substring(2)

	                        );

	               

			// 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.suggestionsk];

		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.appliedSuggestionssuggestion.name == null) {

				JJJ.Constants.appliedSuggestionssuggestion.name = 1;

			} else {

				JJJ.Constants.appliedSuggestionssuggestion.name++;

			}

			var a = [];

			for (var i in JJJ.Constants.appliedSuggestions) {

				a.push(i);

			}

			a.sort(function (x, y) {

				return (JJJ.Constants.appliedSuggestionsx > JJJ.Constants.appliedSuggestionsy]) ? -1 :

					   (JJJ.Constants.appliedSuggestionsx < JJJ.Constants.appliedSuggestionsy]) ? 1 :

					   (x < y) ? -1 : (x > y) ? 1 : 0;

			});

			var s = '';

			for (var i = 0; i < a.length; i++) {

				var count = JJJ.Constants.appliedSuggestionsai]];

				s += ', ' + ((count == 1) ? ai : (count + 'x ' + ai]));

			}

			// Cut off the leading ``, '' and add ``using ARA''

	                s = JJJ.Functions._(

					'fixed [[Help:CS1 errors#apostrophe markup|CS1 \'\'markup\'\' error(s)]] likely via [[User:MJL/ARA-light|script]]',

					s.substring(2)

	                        );

	               

			// 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 ===

	

	/* ITALIC MARKUP HERE */

	//***publisher parameter contains italic 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 = indicesi + 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 ((indexOnward0 == ' ' || indexOnward0 == '\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 |publisher= ends with italic markup

			if (pub.endsWith('\'\''))

			{

				b.push({

					start:        pubStartIndex + pub.length - 2,

					end:          pubStartIndex + pub.length,

					replacement:  '',

					name:         '|publisher= ends with italic markup',

					description:  '|publisher= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the publisher begins with italic markup

		var replaceableStrings = "publisher=\'\'", " publisher = \'\'", " publisher= \'\'", "publisher =\'\'", "publisher= \'\'", " publisher=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|publisher= begins with italic markup',

					description:  '|publisher= begins with italic markup',

				});

			}

		}

		return b;

	});

	//***website parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|website='s

	    var startIndex = 0;

	    var searchStr = "|website=";

	    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 |website=

		{

			var webStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(webStartIndex); //+searchStr.length to exclude "|website=\'\'"

			

			//get to the actual beginning of the website if there are spaces or newlines after the "website=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++webStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = webStartIndex - 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 website 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 web = indexOnward.substring(0, cutOffIndex).trim(); //the web parameter

			

			//If the website ends with italic markup

			if (web.endsWith('\'\''))

			{

				b.push({

					start:        webStartIndex + web.length - 2,

					end:          webStartIndex + web.length,

					replacement:  '',

					name:         '|website= ends with italic markup',

					description:  '|website= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the website begins with italic markup

		var replaceableStrings = "website=\'\'", " website = \'\'", " website= \'\'", "website =\'\'", "website= \'\'", " website=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|website= begins with italic markup',

					description:  '|website= begins with italic markup',

				});

			}

		}

		return b;

	});

	

		//***magazine parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|magazine='s

	    var startIndex = 0;

	    var searchStr = "|magazine=";

	    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 |magazine=

		{

			var magStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(magStartIndex); //+searchStr.length to exclude "|magazine=\'\'"

			

			//get to the actual beginning of the magazine if there are spaces or newlines after the "magazine=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++magStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = magStartIndex - 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 magazine 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 mag = indexOnward.substring(0, cutOffIndex).trim(); //the mag parameter

			

			//If |magazine= ends with italic markup

			if (mag.endsWith('\'\''))

			{

				b.push({

					start:        magStartIndex + mag.length - 2,

					end:          magStartIndex + mag.length,

					replacement:  '',

					name:         '|magazine= ends with italic markup',

					description:  '|magazine= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the magazine begins with italic markup

		var replaceableStrings = "magazine=\'\'", " magazine = \'\'", " magazine= \'\'", "magazine =\'\'", "magazine= \'\'", " magazine=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|magazine= begins with italic markup',

					description:  '|magazine= begins with italic markup',

				});

			}

		}

		return b;

	});

	//***work parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|work='s

	    var startIndex = 0;

	    var searchStr = "|work=";

	    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 |work=

		{

			var worStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(worStartIndex); //+searchStr.length to exclude "|work=\'\'"

			

			//get to the actual beginning of the work if there are spaces or newlines after the "work=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++worStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = worStartIndex - 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 work 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 wor = indexOnward.substring(0, cutOffIndex).trim(); //the wor parameter

			

			//If the work ends with italic markup

			if (wor.endsWith('\'\''))

			{

				b.push({

					start:        worStartIndex + wor.length - 2,

					end:          worStartIndex + wor.length,

					replacement:  '',

					name:         '|work= ends with italic markup',

					description:  '|work= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the work begins with italic markup

		var replaceableStrings = "work=\'\'", " work = \'\'", " work= \'\'", "work =\'\'", "work= \'\'", " work=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|work= begins with italic markup',

					description:  '|work= begins with italic markup',

				});

			}

		}

		return b;

	});

	

		//***periodical parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|periodical='s

	    var startIndex = 0;

	    var searchStr = "|periodical=";

	    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 |periodical=

		{

			var perStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(perStartIndex); //+searchStr.length to exclude "|periodical=\'\'"

			

			//get to the actual beginning of the periodical if there are spaces or newlines after the "periodical=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++perStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = perStartIndex - 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 periodical 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 per = indexOnward.substring(0, cutOffIndex).trim(); //the per parameter

			

			//If |periodical= ends with italic markup

			if (per.endsWith('\'\''))

			{

				b.push({

					start:        perStartIndex + per.length - 2,

					end:          perStartIndex + per.length,

					replacement:  '',

					name:         '|periodical= ends with italic markup',

					description:  '|periodical= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the periodical begins with italic markup

		var replaceableStrings = "periodical=\'\'", " periodical = \'\'", " periodical= \'\'", "periodical =\'\'", "periodical= \'\'", " periodical=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|periodical= begins with italic markup',

					description:  '|periodical= begins with italic markup',

				});

			}

		}

		return b;

	});

	//***newspaper parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|newspaper='s

	    var startIndex = 0;

	    var searchStr = "|newspaper=";

	    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 |newspaper=

		{

			var nwpStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(nwpStartIndex); //+searchStr.length to exclude "|newspaper=\'\'"

			

			//get to the actual beginning of the newspaper if there are spaces or newlines after the "newspaper=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++nwpStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = nwpStartIndex - 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 newspaper 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 nwp = indexOnward.substring(0, cutOffIndex).trim(); //the nwp parameter

			

			//If the newspaper ends with italic markup

			if (nwp.endsWith('\'\''))

			{

				b.push({

					start:        nwpStartIndex + nwp.length - 2,

					end:          nwpStartIndex + nwp.length,

					replacement:  '',

					name:         '|newspaper= ends with italic markup',

					description:  '|newspaper= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the newspaper begins with italic markup

		var replaceableStrings = "newspaper=\'\'", " newspaper = \'\'", " newspaper= \'\'", "newspaper =\'\'", "newspaper= \'\'", " newspaper=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|newspaper= begins with italic markup',

					description:  '|newspaper= begins with italic markup',

				});

			}

		}

		return b;

	});

		//***journal parameter contains italic markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|journal='s

	    var startIndex = 0;

	    var searchStr = "|journal=";

	    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 |journal=

		{

			var jorStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(jorStartIndex); //+searchStr.length to exclude "|journal=\'\'"

			

			//get to the actual beginning of the journal if there are spaces or newlines after the "journal=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++jorStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = jorStartIndex - 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 journal 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 jor = indexOnward.substring(0, cutOffIndex).trim(); //the jor parameter

			

			//If |journal= ends with italic markup

			if (jor.endsWith('\'\''))

			{

				b.push({

					start:        jorStartIndex + jor.length - 2,

					end:          jorStartIndex + jor.length,

					replacement:  '',

					name:         '|journal= ends with italic markup',

					description:  '|journal= ends with italic markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the journal begins with italic markup

		var replaceableStrings = "journal=\'\'", " journal = \'\'", " journal= \'\'", "journal =\'\'", "journal= \'\'", " journal=\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'", ""), 

					name:         '|journal= begins with italic markup',

					description:  '|journal= begins with italic markup',

				});

			}

		}

		return b;

	});

	

	

	/* BOLD MARKUP HERE */

		//***publisher parameter contains bold 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 = indicesi + 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 ((indexOnward0 == ' ' || indexOnward0 == '\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 |publisher= ends with bold markup

			if (pub.endsWith('\'\'\''))

			{

				b.push({

					start:        pubStartIndex + pub.length - 2,

					end:          pubStartIndex + pub.length,

					replacement:  '',

					name:         '|publisher= ends with bold markup',

					description:  '|publisher= ends with bold markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the publisher begins with bold markup

		var replaceableStrings = "publisher=\'\'\'", " publisher = \'\'\'", " publisher= \'\'\'", "publisher =\'\'\'", "publisher= \'\'\'", " publisher=\'\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'\'", ""), 

					name:         '|publisher= begins with bold markup',

					description:  '|publisher= begins with bold markup',

				});

			}

		}

		return b;

	});

	//***website parameter contains bold markup***

	ARA_Rules.push(function (s) {

		var b = [];



	    //get indices of all '|website='s

	    var startIndex = 0;

	    var searchStr = "|website=";

	    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 |website=

		{

			var webStartIndex = indicesi + searchStr.length;

			var indexOnward   = s.substring(webStartIndex); //+searchStr.length to exclude "|website=\'\'\'"

			

			//get to the actual beginning of the website if there are spaces or newlines after the "website=" and before the start of the text

			while ((indexOnward0 == ' ' || indexOnward0 == '\n'))

			{

				indexOnward = indexOnward.substring(1); //cut off the first character

				++webStartIndex;

			}

				

			var fullRef          = indexOnward;

			var fullRefPrevIndex = webStartIndex - 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 website 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 web = indexOnward.substring(0, cutOffIndex).trim(); //the web parameter

			

			//If the website ends with bold markup

			if (web.endsWith('\'\'\''))

			{

				b.push({

					start:        webStartIndex + web.length - 2,

					end:          webStartIndex + web.length,

					replacement:  '',

					name:         '|website= ends with bold markup',

					description:  '|website= ends with bold markup',

				});

			}

		}

		

		return b;

	});

	

	ARA_Rules.push(function (s) {

		var b = [];

			//If the website begins with bold markup

		var replaceableStrings = "website=\'\'\'", " website = \'\'\'", " website= \'\'\'", "website =\'\'\'", "website= \'\'\'", " website=\'\'\'"];

		for (i = 0; i < replaceableStrings.length; i++)

		{

			var replaceableString = replaceableStringsi];

			if (s.includes(replaceableString))

			{

				b.push({

					start: s.indexOf(replaceableString),

					end:   s.indexOf(replaceableString) + replaceableString.length,

					replacement: replaceableString.replace("\'\'\'", ""), 

					name:         '|website= begins with bold markup',

					description:  '|website= begins with bold markup',

				});

			}

		}

		return b;

	});

	

	/* TECHNICAL - HELPS SCRIPT FUNCTION */

	//***unnecessary whitespace in citation***

	ARA_Rules.push(function (s) {

		var b = [];

	   

	    var replaceableStrings = " publisher=","publisher ="," publisher =", " website=","website ="," website =", " magazine=","magazine ="," magazine =", " work=","work ="," work =", " periodical=","periodical ="," periodical =", " newspaper=","newspaper ="," newspaper =", " journal=", "journal ="," journal= "];

	    for (i = 0; i < replaceableStrings.length; i++)

	    {

	    	var replaceableString = replaceableStringsi];

	    	

	    	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;

	});

	

	} // end if mw.config.get('wgContentLanguage') === 'en'

	

	return ARA_Rules;

}

// </nowiki>

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook