User:Koavf/monobook.js

window.hotcat_translations_from_commons = true; /* This imports the latest version of HotCat from Commons. HotCat is a gadget to make changes to categories much easier. Full documentation can be found at http://commons.wikimedia.org/wiki/Help:Gadget-HotCat mw.loader.load( '//commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&action=raw&ctype=text/javascript' ); // See http://en.wikipedia.org/wiki/User:Cameltrader/Advisor.js/Description // for details and installation instructions. // // The script consists of three major parts: // * some helper functions // * the core of the user interface, including code that collects suggestions from a set of rules // * the rule implementations // // All functions, variables, and constants belonging to the script are // encapsulated in a private namespace object---``ct for ``Cameltrader: var ct = ct || {}; // == Helpers == // === DOM manipulation === // 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. ct.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/ ct.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}; }; ct.observe = function (e, eventName, f) { if (e.addEventListener) { e.addEventListener(eventName, f, false); } else { e.attachEvent('on' + eventName, f); } }; ct.stopObserving = function (e, eventName, f) { if (e.removeEventListener) { e.removeEventListener(eventName, f, false); } else { e.detachEvent('on' + eventName, f); } }; ct.stopEvent = function (event) { if (event.preventDefault) { event.preventDefault; event.stopPropagation; } else { event.returnValue = false; event.cancelBubble = true; } }; // ct.anchor is a shortcut to creating a link as a DOM node: ct.anchor = function (text, href, title) { var e = document.createElement('A'); e.href = href; e.appendChild(document.createTextNode(text)); e.title = title || ''; return e; }; // ct.link produces the HTML for a link to a Wikipedia article as a string. // It is convenient to embed in a help popup. ct.hlink = function (toWhat, text) { var wgServer = window.wgServer || 'http://en.wikipedia.org'; var wgArticlePath = window.wgArticlePath || '/wiki/$1'; var url = (wgServer + wgArticlePath).replace('$1', toWhat); return '' + (text || toWhat) + ''; }; // === Helpers a la functional programming === // A higher-order function---produces a cached version of a one-arg function. ct.makeCached = function (f) { var cache = {}; // a closure; the cache is private for f	return function (x) { return (cache[x] != null) ? cache[x] : (cache[x] = f(x)); }; }; // === Regular expressions === // Regular expressions can sometimes become inconveniently large. // In order to make complex ones easier to read, we introduce // a set of macros. Tokens enclosed with ``{ and ``} will be // replaced according to the hashtable below. // // To do the replacements, one must pass the RegExp object // through fixRegExp and use the result instead, like this: // //	var re = ct.fixRegExp(/It happened in {month}/); // // Also, for the sake of convenience, we add the "getAllMatches(re, s)" // method, which is a quick means to find all occurrences of a // regex in some text. It returns an array containing the results // of applying RegExp.exec(..). ct.REG_EXP_REPLACEMENTS = { '{letter}': // all Unicode letters // http://www.codeproject.com/dotnet/UnicodeCharCatHelper.asp '\\u0041-\\u005a\\u0061-\\u007a\\u00aa' + '\\u00b5\\u00ba\\u00c0-\\u00d6' + '\\u00d8-\\u00f6\\u00f8-\\u01ba\\u01bc-\\u01bf' + '\\u01c4-\\u02ad\\u0386\\u0388-\\u0481\\u048c-\\u0556' + '\\u0561-\\u0587\\u10a0-\\u10c5\\u1e00-\\u1fbc\\u1fbe' + '\\u1fc2-\\u1fcc\\u1fd0-\\u1fdb\\u1fe0-\\u1fec' + '\\u1ff2-\\u1ffc\\u207f\\u2102\\u2107\\u210a-\\u2113' + '\\u2115\\u2119-\\u211d\\u2124\\u2126\\u2128' + '\\u212a-\\u212d\\u212f-\\u2131\\u2133\\u2134\\u2139' + '\\ufb00-\\ufb17\\uff21-\\uff3a\\uff41-\\uff5a', '{month}': // English only '(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|'			+ 'January|February|March|April|June|July|August|September|'			+ 'October|November|December)', '{year}': '[12][0-9]{3}' }; ct.fixRegExp = function (re) { // : RegExp if (re.__fixedRE != null) { return re.__fixedRE; }	var s = re.source; for (var alias in ct.REG_EXP_REPLACEMENTS) { s = s.replace(				new RegExp(ct.escapeRegExp(alias), 'g'),				ct.REG_EXP_REPLACEMENTS[alias]		); }	re.__fixedRE = new RegExp(s); // the fixed copy is cached re.__fixedRE.global = re.global; re.__fixedRE.ignoreCase = re.ignoreCase; re.__fixedRE.multiline = re.multiline; return re.__fixedRE; }; ct.escapeRegExp = ct.makeCached(function (s) { // : RegExp	var r = '';	for (var i = 0; i < s.length; i++) {		var code = s.charCodeAt(i).toString(16);		r += '\\u' + '0000'.substring(code.length) + code;	}	return r; }); ct.getAllMatches = function (re, s) { // : Match[] var p = 0; var a = []; while (true) { re.lastIndex = 0; var m = re.exec(s.substring(p)); if (m == null) { return a;		} m.start = p + m.index; m.end = p + m.index + m[0].length; a.push(m); p = m.end; } }; // == Advisor core == // This is the basic functionality of showing and fixing suggestions. // === Global constants and variables === ct.DEFAULT_MAX_SUGGESTIONS = 8; ct.maxSuggestions = ct.DEFAULT_MAX_SUGGESTIONS; ct.suggestions; // : Suggestion[] ct.eSuggestions; // : Element; that's where suggestions are rendered ct.eAddToSummary; // : Element; the proposed edit summary appears there ct.eTextarea; // : Element; the one with id="wpTextbox1" ct.appliedSuggestions = {}; // : Map ct.scannedText = null; // remember what we scan, to check if it is                      // still the same when we try to fix it ct.BIG_THRESHOLD = 100 * 1024; ct.isBigScanConfirmed = false; // is the warning about a big article confirmed ct.isTalkPageScanConfirmed = false; ct.scanTimeoutId = null; // a timeout is set after a keystroke and before // a scan, this variable tracks its id // === int main === // This is the entry point ct.observe(window, 'load', function {	ct.eTextarea = document.getElementById('wpTextbox1');	if (ct.eTextarea == null) {		// This is not an ``?action=edit'' page		return;	}	ct.eSuggestions = document.createElement('DIV');	ct.eSuggestions.style.border = 'dashed #ccc 1px';	ct.eSuggestions.style.color = '#888';	var e = document.getElementById('editform');	while (true) {		var p = e.previousSibling;		if ( (p == null) || ((p.nodeType == 1) && (p.id != 'toolbar')) ) {			break;		}		e = p;	}	e.parentNode.insertBefore(ct.eSuggestions, e);	ct.eAddToSummary = document.createElement('DIV');	ct.eAddToSummary.style.border = 'dashed #ccc 1px';	ct.eAddToSummary.style.color = '#888';	ct.eAddToSummary.style.display = 'none';	var wpSummaryLabel = document.getElementById('wpSummaryLabel');	wpSummaryLabel.parentNode.insertBefore(ct.eAddToSummary, wpSummaryLabel);	ct.scan; // do a scan now ...	ct.observeWikiText(ct.delayScan); // ... and every time the user pauses typing }); // === Internationalisation === // ct._ 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. ct._ = function (s) {	if (ct.translation && ct.translation[s]) {		s = ct.translation[s];	}	var index = 1;	while (arguments[index]) {		s = s.replace('$' + index, arguments[index]); // todo: replace all?		index++;	}	return s; }; // === Editor compatibility layer === // Controlling access to wpTextbox1 helps abstract out compatibility // with editors like wikEd (http://en.wikipedia.org/wiki/User:Cacycle/wikEd) ct.getWikiText = function {	if (window.wikEdUseWikEd) {		var obj = {sel: WikEdGetSelection};		WikEdParseDOM(obj, wikEdFrameBody);		return obj.plain;	}	return ct.eTextarea.value; }; ct.setWikiText = function (s) { if (window.wikEdUseWikEd) { // todo: wikEd compatibility alert(ct._('Changing text in wikEd is not yet supported.')); return; };	ct.eTextarea.value = s; }; ct.focusWikiText = function { if (window.wikEdUseWikEd) { wikEdFrameWindow.focus; return; }	ct.eTextarea.focus; }; ct.selectWikiText = function (start, end) { if (window.wikEdUseWikEd) { var obj = x = {sel: WikEdGetSelection, changed: {}}; WikEdParseDOM(obj, wikEdFrameBody); var i = 0; while ((obj.plainStart[i + 1] != null) && (obj.plainStart[i + 1] <= start)) { i++; }		var j = i;		while ((obj.plainStart[j + 1] != null) && (obj.plainStart[j + 1] <= end)) { j++; }		obj.changed.range = document.createRange; obj.changed.range.setStart(obj.plainNode[i], start - obj.plainStart[i]); obj.changed.range.setEnd(obj.plainNode[j], end - obj.plainStart[j]); WikEdRemoveAllRanges(obj.sel); obj.sel.addRange(obj.changed.range); return; }	ct.setSelectionRange(ct.eTextarea, start, end); }; ct.observeWikiText = function (callback) { // todo: wikEd compatibility ct.observe(ct.eTextarea, 'keyup', ct.delayScan); }; // === Interaction with the user === // ct.scan analyses the text and handles how the proposals are reflected in the UI. ct.scan = function (force) { ct.scanTimeoutId = null; var s = ct.getWikiText; if ((s === ct.scannedText) && !force) { return; // Nothing to do, we've already scanned the very same text }	ct.scannedText = s;	while (ct.eSuggestions.firstChild != null) { ct.eSuggestions.removeChild(ct.eSuggestions.firstChild); }	// Warn about scanning a big article if ((s.length > ct.BIG_THRESHOLD) && !ct.isBigScanConfirmed) { ct.eSuggestions.appendChild(document.createTextNode( ct._('This article is rather long. Advisor.js 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 ') ));		ct.eSuggestions.appendChild(ct.anchor( ct._('scan the text anyway.'), 'javascript: ct.isBigScanConfirmed = true; ct.scan(true); void(0);', ct._('Ignore this warning.') ));		return; }	// Warn about scanning a talk page if ((window.wgCanonicalNamespace != null)				&& /(\b|_)talk$/i.test(window.wgCanonicalNamespace)				&& !ct.isTalkPageScanConfirmed) { ct.eSuggestions.appendChild(document.createTextNode( ct._('Advisor.js 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 ') ));		ct.eSuggestions.appendChild(ct.anchor( ct._('scan the text anyway.'), 'javascript: ct.isTalkPageScanConfirmed = true; ct.scan(true); void(0);', ct._('Ignore this warning.') ));		return; }	ct.suggestions = ct.getSuggestions(s); if (ct.suggestions.length == 0) { ct.eSuggestions.appendChild(document.createTextNode( ct._('OK \u2014 Advisor.js found no issues with the text.') // U+2014 is an mdash ));		return; }	var nSuggestions = Math.min(ct.maxSuggestions, ct.suggestions.length); ct.eSuggestions.appendChild(document.createTextNode( (ct.suggestions.length == 1) ? ct._('1 suggestion: ') : ct._('$1 suggestions: ', ct.suggestions.length) ));	for (var i = 0; i < nSuggestions; i++) { var suggestion = ct.suggestions[i]; var eA = ct.anchor(				suggestion.name,				'javascript:ct.showSuggestion(' + i + '); void(0);',				suggestion.description		); suggestion.element = eA; ct.eSuggestions.appendChild(eA); if (suggestion.replacement != null) { var eSup = document.createElement('SUP'); ct.eSuggestions.appendChild(eSup); eSup.appendChild(ct.anchor( ct._('fix'), 'javascript:ct.fixSuggestion(' + i + '); void(0);' ));		}		ct.eSuggestions.appendChild(document.createTextNode(' ')); }	if (ct.suggestions.length > ct.maxSuggestions) { ct.eSuggestions.appendChild(ct.anchor( '...', 'javascript: ct.maxSuggestions = 1000; ct.scan(true); void(0);', ct._('Show All') ));	} }; // getSuggestions returns the raw data used by scan. // It is convenient for unit testing. ct.getSuggestions = function (s) { var suggestions = []; for (var i = 0; i < ct.rules.length; i++) { var a = ct.rules[i](s); for (var j = 0; j < a.length; j++) { suggestions.push(a[j]); }	}	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; }; // 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. ct.delayScan = function { if (ct.scanTimeoutId != null) { clearTimeout(ct.scanTimeoutId); ct.scanTimeoutId = null; }	ct.scanTimeoutId = setTimeout(ct.scan, 500); }; // 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 ct.showSuggestion = function (k) { if (ct.getWikiText != ct.scannedText) { // The text has changed - just do another scan and don't change selection ct.scan; return; }	var suggestion = ct.suggestions[k]; var now = new Date.getTime; if ((suggestion.help != null) && (ct.lastShownSuggestionIndex === k) && (now - ct.lastShownSuggestionTime < 1000)) { // Show help var p = ct.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); ct.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);			ct.stopObserving(document.body, 'click', arguments.callee);		}); ct.focusWikiText; return; }	ct.lastShownSuggestionIndex = k;	ct.lastShownSuggestionTime = now; ct.selectWikiText(suggestion.start, suggestion.end); }; // Usually, there is a ``fix'' link next to each suggestion. It is handled by: ct.fixSuggestion = function (k) { var s = ct.getWikiText; if (s != ct.scannedText) { ct.scan; return; }	var suggestion = ct.suggestions[k]; if (suggestion.replacement == null) { // the issue is not automatically fixable return; }	ct.setWikiText(			s.substring(0, suggestion.start)			+ suggestion.replacement			+ s.substring(suggestion.end)	); ct.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 (ct.appliedSuggestions[suggestion.name] == null) { ct.appliedSuggestions[suggestion.name] = 1; } else { ct.appliedSuggestions[suggestion.name]++; }		var a = []; for (var i in ct.appliedSuggestions) { a.push(i); }		a.sort(function (x, y) {			return (ct.appliedSuggestions[x] > ct.appliedSuggestions[y]) ? -1 :				  (ct.appliedSuggestions[x] < ct.appliedSuggestions[y]) ? 1 :				   (x < y) ? -1 : (x > y) ? 1 : 0;		}); var s = ''; for (var i = 0; i < a.length; i++) { var count = ct.appliedSuggestions[a[i]]; s += ', ' + ((count == 1) ? a[i] : (count + 'x ' + a[i])); }		// Cut off the leading ``,  and add ``formatting:  and ``using Advisor.js'' s = ct._(				'formatting: $1 (using Advisor.js)',				s.substring(2)		); // Render in DOM while (ct.eAddToSummary.firstChild != null) { ct.eAddToSummary.removeChild(ct.eAddToSummary.firstChild); }		ct.eAddToSummary.style.display = ''; ct.eAddToSummary.appendChild(ct.anchor( ct._('Add to summary'), 'javascript:ct.addToSummary(unescape("' + escape(s) + '"));', ct._('Append the proposed summary to the input field below') ));		ct.eAddToSummary.appendChild(document.createTextNode(': "' + s + '"')); }	// Re-scan immediately ct.scan; }; // The mnemonics of the accepted suggestions are accumulated in ct.appliedSuggestions // and the user is presented with a sample edit summary. If she accepts it, // addToSummary gets called. ct.addToSummary = function (summary) { var wpSummary = document.getElementById('wpSummary'); if (wpSummary.value != '') { summary = wpSummary.value + '; ' + summary; }	if ((wpSummary.maxLength > 0) && (summary.length > wpSummary.maxLength)) { alert(ct._( '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; ct.eAddToSummary.style.display = 'none'; }; // == Rules == // This chapter contains the ``rules'' that produce suggestions---this is where // most of the load resides. Each rule is a javascript function that accepts a // string as a parameter (the wikitext of the page being edited) and returns an // array of ``suggestion'' objects. A suggestion object must have the following // 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 // The set of rules to apply depends on the content language. Different // languages have different formatting conventions, therefore this is not // a matter of internationalisation like the UI core, but of unrelated // implementations. What follows is the implementation for the English-language // Wikipedia. if (!window.wgContentLanguage || (window.wgContentLanguage === 'en')) { // from this line on, a level of indent is spared // The rules are stored in an array: ct.rules = []; // : Function[] // and are grouped into categories. // === Linking rules === ct.rules.push(function (s) {	var re = /\[\[([{letter} ,\(\)\-]+)\|\1\]\]/g;	re = ct.fixRegExp(re);	var a = ct.getAllMatches(re, s);	for (var i = 0; i < a.length; i++) {		var m = a[i];		a[i] = {				start: m.start,				end: m.end,				replacement:  + m[1] + ,				name: 'A|A',				description: '"A|A" can be simplified to A.',				help: ct.hlink('WP:Syntax#Wiki_markup', 'MediaWiki syntax')					+ ' allows links of the form A|A to be abbreviated as A. '		};	}	return a; }); ct.rules.push(function (s) {	var re = /\[\[([{letter} ,\(\)\-]+)\|\1([{letter}]+)\]\]/g;	re = ct.fixRegExp(re);	var a = ct.getAllMatches(re, s);	for (var i = 0; i < a.length; i++) {		var m = a[i];		a[i] = {				start: m.start,				end: m.end,				replacement:  + m[1] +  + m[2],				name: 'A|AB',				description: '"A|AB" can be simplified to AB.',				help: ct.hlink('WP:Syntax#Wiki_markup', 'MediaWiki syntax')					+ ' allows links of the form A|AB to be abbreviated as AB.'		};	}	return a; }); ct.rules.push(function (s) {	// Initialise statics	var _static = arguments.callee;	if (_static.MONTH_MAP == null) {		_static.MONTH_MAP = {				Jan: 'January', Feb: 'February', Mar: 'March', Apr: 'April', May: 'May',				Jun: 'June', Jul: 'July', Aug: 'August', Sep: 'September', Oct: 'October',				Nov: 'November', Dec: 'December', January: 'January', February: 'February',				March: 'March', April: 'April', June: 'June', July: 'July',				August: 'August', September: 'September', October: 'October',				November: 'November', December: 'December'		};	}	// This will match either a date+year or just a year, and will not match solitary dates.	// If the year is part of an ISO date of the form yyyy-mm-dd, the remainder is included.	// The rule only controls the transition from linked to unlinked, as practice has shown	// that improper linking is significantly more common than leaving linkable dates as plain text. var re = /(?:\[\[((?:(\d\d?) +({month}))|(?:({month}) +(\d\d?)))\]\],?? *)?\[\[({year})\]\](-\[\[\d\d-\d\d\]\])?/; re = ct.fixRegExp(re); var a = ct.getAllMatches(re, s); var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; var date = m[1] || null; var year = m[7] || null; if (date == null) { if (!m[8]) { // protect ISO dates---m[8] is the ISO remainder b.push({						start: m.start,						end: m.end,						replacement: year,						name: 'year link',						description: 'Convert link to normal text',						help: 'It is useless to link a year unless it is preceded by a day and month.'							+ ' Years with a day and month are normally linked so that the user '							+ 'preferences for date format can be applied, but linking a year alone '							+ 'has no effect.'				}); }		} else { var isAmerican = !m[2]; var day = (isAmerican) ? m[5] : m[2]; var month = _static.MONTH_MAP[(isAmerican) ? m[4] : m[3]]; var ws = m[6] || ''; // whitespace between date and year var replacement = (isAmerican) ? ( + month + ' ' + day + ',' + ws +  + year + '') : ( + day + ' ' + month +  + ws +  + year + ); if (replacement != m[0]) { b.push({						start: m.start,						end: m.end,						replacement: replacement,						name: 'date format',						description: 'Fix date format',						help: 'Commas in dates should follow one of these styles: '								+ '1 January 1970 '								+ 'January 1, 1970 '								+ 'and month names should not be abbreviated.'				}); }		}	}	return b; }); ct.rules.push(function (s) { // Matches decades in the range 1000s ... 2990s, // linked either as xxx0s or as xxx0s var re = /\[\[([12][0-9][0-9]0)(\]\]s\b|'?s\]\])/g; var a = ct.getAllMatches(re, s); for (var i = 0; i < a.length; i++) { var m = a[i]; a[i] = { start: m.start, end: m.end, replacement: m[1] + 's', name: 'decade link', description: 'Convert link to normal text', help: 'Decades should not be linked, unless they deepen the ' + 'readers\' understanding of the topic.' };	}	return a; }); ct.rules.push(function (s) { // Matches decades in the range 1000s ... 2990s var re = /\bthe +([12][0-9][0-9]0)'s\b/g; var a = ct.getAllMatches(re, s); for (var i = 0; i < a.length; i++) { var m = a[i]; a[i] = { start: m.start, end: m.end, replacement: m[1] + 's', name: 'decade format', description: 'Remove the apostrophe from the decade', help: 'The preferred decade format is without an apostrophe, per ' + ct.hlink('WP:DATE#Longer_periods') + '.' };	}	return a; }); ct.rules.push(function (s) { var re = /\[\[([0-9]{1,2}(st|nd|rd|th) century)\]\]/g var a = ct.getAllMatches(re, s); for (var i = 0; i < a.length; i++) { var m = a[i]; a[i] = { start: m.start, end: m.end, replacement: m[1], name: 'century link', description: 'Convert link to normal text', help: 'Centuries should not be linked, unless they deepen the ' + 'readers\' understanding of the topic.' };	}	return a; }); // === Character formatting rules === ct.rules.push(function (s) { var a = ct.getAllMatches(/ +$/gm, s); var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; if (/^[=\|]$/.test(s[m.start - 1])) { // this can be tolerated, it happens too often in templates continue; }		b.push({				start: m.start,				end: m.end,				replacement: '',				name: 'whitespace',				description: 'Delete trailing whitespace',				help: 'Trailing whitespace at the end of a line is unnecessary.'		}); }	return b; }); ct.rules.push(function (s) { var re = /[{letter}]( +- +)[{letter}]/g; re = ct.fixRegExp(re); var a = ct.getAllMatches(re, s); var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; // Be careful not to break wikilinks. If we find a ']' before we find an '['---drop the suggestion. var rightContext = s.substring(m.end); var indexOfOpening = rightContext.indexOf('['); var indexOfClosing = rightContext.indexOf(']'); if ((indexOfClosing != -1)				&& ((indexOfOpening == -1) || (indexOfOpening > indexOfClosing))) { continue; }		b.push({				start: m.start + 1,				end: m.end - 1,				replacement: ' \u2014 ', // U+2014 is an mdash				name: 'mdash',				description: 'In a sentence, a hyphen surrounded by spaces means almost certainly an mdash.'		}); }	return b; }); ct.rules.push(function (s) { var re = /[^0-9]({year}) *(?:-|\u2014|&mdash;|--) *({year})[^0-9]/g; // U+2014 is an mdash var a = ct.getAllMatches(re, s); for (var i = 0; i < a.length; i++) { var m = a[i]; a[i] = { start: m.start + 1, end: m.end - 1, replacement: m[1] + '\u2013' + m[2], // U+2013 is an ndash name: 'ndash', description: 'Year ranges look better with an n-dash.' };	}	return a; }); ct.rules.push(function (s) { var re = / (\u2014|\u2013|&mdash;|–)/g; // an m/ndash surrounded by normal spaces var a = ct.getAllMatches(re, s); for (var i = 0; i < a.length; i++) { var m = a[i]; a[i] = { start: m.start, end: m.end, replacement: ' ' + m[1], // a non-breaking space and the dash name: 'nbsp-dash', description: 'Put a non-breaking space before the dash', help: 'Putting a ' + ct.hlink('non-breaking space') + ' (&amp;nbsp;</tt>) before a dash would ' + 'prevent the user agent from wrapping it at the beginning of the next line.' };	}	return a; }); ct.rules.push(function (s) { var a = ct.getAllMatches(		/(\{\{\s*(?:IPA[0-3]?|IPAAusE|IPAEng|IPAHe|[Pp]ronAusE|[Pp]ronEng|[Pp]ronounced)\s*\|\s*)([^\|\}]+)/gi, s	); var b = []; var ipaSubstitions = { ':': {					replacement: '\u02d0', // U+02D0 is a ``Modifier letter triangular colon'' (used to denote vowel lengthening in IPA) additionalHelp: " In this case the triangular colon (``\u02d0'', U+02D0</tt>), " + "used to denote vowel lengthening, looks like a regular colon (``:'', U+003A</tt>)." },			'\: {					replacement: '\u02c8', // U+02C8 is a ``Modifier letter vertical line (put before a stresses syllable) additionalHelp: " In this case the vertical line (``\u02c8'', U+02c8</tt>), " + " which is put before a stressed syllable, looks like an apostrophe (`` ' '', U+0027</tt>)." }	};	for (var i = 0; i < a.length; i++) { var m = a[i]; var ipaText = m[2]; for (var j = 0; j < ipaText.length; j++) { var ch = ipaText[j]; if (ipaSubstitions[ch] != null) { b.push({						start: m.start + m[1].length + j,						end: m.start + m[1].length + j + 1,						replacement: ipaSubstitions[ch].replacement,						name: 'IPA character',						description: "Replace ``false friend with the correct IPA character",						help: 'The correct IPA character '							+ ct.hlink('WP:IPA#Entering_IPA_characters', 'should be used')							+ " instead of its ``false friend."							+ ' Unicode contains a reserved range of characters for '							+ ct.hlink('International Phonetic Alphabet', 'IPA')							+ ' transcription. Some of them look very similar to other, '							+ 'more commonly used, alphabetic or punctuation characters (' + ct.hlink('False friend', 'false friends') + ').' + (ipaSubstitions[ch].additionalHelp || '')				}); }		}	}	return b; }); ct.rules.push(function (s) { var re = /&#(([1-9][0-9]{0,4})|x([a-fA-F0-9]{1,4}));/g; var a = ct.getAllMatches(re, s); var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; var charCode = (m[2]) ? parseInt(m[2]) : parseInt(m[3], 16); if ((charCode < 128) || (charCode > 0xffff)) { continue; }		var ch = String.fromCharCode(charCode); var chHex = charCode.toString(16).toUpperCase; chHex = '0000'.substring(chHex.length) + chHex; b.push({				start: m.start,				end: m.end,				replacement: ch,				name: 'unicode-escape',				description: 'Replace with an inline Unicode character',				help: ct.hlink('WP:EDIT#Character_formatting', 'HTML-style escapes')					+ " like ``&amp;#" + m[1]					+ ";</tt> can be written inline using a Unicode character&mdash;in this case ``"					+ ch + " (U+" + chHex + "</tt>)."		}); }	return b; }); ct.rules.push(function (s) { var re = /&([A-Za-z]+);/g; var a = ct.getAllMatches(re, s); var b = []; // Use a DOM element and its innerHTML property to do // the unescaping, let the browser do the dirty job. var e = document.createElement('DIV'); for (var i = 0; i < a.length; i++) { var m = a[i]; if (m[1] == 'nbsp') { // Opera incorrectly replaces nbsp-s with regular spaces: // http://en.wikipedia.org/w/index.php?title=User_talk%3ACameltrader&diff=179233698&oldid=175946199 continue; }		e.innerHTML = m[0]; var ch = e.innerHTML; if (ch.length != 1) { // The entity is not a single Unicode character---ignore it			continue; }		var chHex = ch.charCodeAt(0).toString(16).toUpperCase; chHex = '0000'.substring(chHex.length) + chHex; b.push({				start: m.start,				end: m.end,				replacement: e.innerHTML, // the entity, unescaped				name: 'HTML entity',				description: 'Replace with an inline Unicode character',				help: ct.hlink('WP:EDIT#Character_formatting', 'HTML-style escapes')					+ " like ``&amp;" + m[1]					+ ";</tt> can be written inline using a Unicode character&mdash;in this case ``"					+ ch + " (U+" + chHex + "</tt>)."		}); }	return b; }); ct.rules.push(function (s) { var a = ct.getAllMatches(/\u2026/g, s); // ellipsis var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; b.push({				start: m.start,				end: m.end,				replacement: '...',				name: 'ellipsis',				description: 'Replace ellipsis with three periods/full stops',				help: "The ellipsis character (``\u2026'', U+2026) should be replaced with "					+ "three periods/full stops per "					+ ct.hlink('WP:MOS#Ellipses')		}); }	return b; }); ct.rules.push(function (s) { var a = ct.getAllMatches(/\b(NOT)\b/g, s); var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; if ((s.substring(m.start - 2, m.start) == "")				&& (s.substring(m.end, m.end + 2) == "")) { continue; }		var noMoreLinksRemainder = ' A COLLECTION OF LINKS NOR SHOULD IT BE USED FOR'; if (s.substring(m.end, m.end + noMoreLinksRemainder.length) === noMoreLinksRemainder) { // Tolerate subst'ed Template:NoMoreLinks continue; }		b.push({				start: m.start,				end: m.end,				replacement: "not",				name: 'all-caps',				description: 'Change to lowercase',				help: 'According to the ' + ct.hlink('WP:MOS#Capital_letters', 'Manual of Style')					+ ', the word  + m[1].toLowerCase +  should be italicised instead '					+ 'of being written in all caps.'		}); }	return b; }); // === Template usage rules === ct.rules.push(function (s) { var re = /^[ ':]*(?:Main +article)[ ']*:[ ']*\[\[([^\]]+)\]\][ ']*$/mig; var a = ct.getAllMatches(re, s); var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; if ((m[1] != null) && (m[1] != "")) { b.push({					start: m.start,					end: m.end,					replacement: ,					name: 'template-main',					description: 'Use the template',					help: 'Template ' + ct.hlink('Template:Main', )						+ '</tt> can be used in this place.'			}); }	}	return b; }); ct.rules.push(function (s) { var re = /(\{\{\s*)DEFAULTSORT\s*\|/g; var a = ct.getAllMatches(re, s); var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; b.push({				start: m.start,				end: m.end,				replacement: m[1] + 'DEFAULTSORT:',				name: 'default-sort-magic-word',				description: 'Replace the template with a magic word',				help: 'Usage of the </tt> template is discouraged. The magic word with the same name should be used instead.'		}); }	return b; }); // === Other rules === ct.rules.push(function (s) { var re = /^(?: *)(==+)( *)([^=]*[^= ])( *)\1/gm; var a = ct.getAllMatches(re, s); if (a.length == 0) { return []; }	var b = []; var level = 0; // == Level 1 ==, === Level 2 ===, ==== Level 3 ====, etc.	var editform = document.getElementById('editform'); // If we are editing a section, we have to be tolerant to the first heading's level var isSection = editform && (editform['wpSection'] != null) && (editform['wpSection'].value != ''); // Count spaced and non-spaced headings to find out the majority var counters = {spaced: 0, nonSpaced: 0, unclear: 0}; for (var i = 0; i < a.length; i++) { var m = a[i]; counters[(!m[2] && !m[4]) ? 'nonSpaced' : (m[2] && m[4]) ? 'spaced' : 'unclear']++; }	var predominantSpacingStyle; if (counters.spaced > counters.nonSpaced) { predominantSpacingStyle = 'spaced'; } else if (counters.spaced < counters.nonSpaced) { predominantSpacingStyle = 'nonSpaced'; } else { predominantSpacingStyle = 'unclear'; // We cannot decide which spacing style is predominant, // so we show a suggestion attached to the first heading, // recommending consistent spacing: b.push({				start: a[0].start,				end: a[0].end,				replacement: null,				name: 'heading',				description: 'Consider using consistent heading spacing',				help: 'Heading style should be either '					+ "``== Heading ==</tt> or ``==Heading==</tt>. "					+ "Headings in this article use an equal number of both.  "					+ "Consider choosing a heading style and using it consistently."		}); }	var titleSet = {}; // a set of title names, will be used to detect duplicates for (var i = 0; i < a.length; i++) { var m = a[i]; if (m[2] != m[4]) { var spacer = (predominantSpacingStyle == 'spaced') ? ' ' : (predominantSpacingStyle == 'nonSpaced') ? '' : m[2]; b.push({					start: m.start,					end: m.end,					replacement: m[1] + spacer + m[3] + spacer + m[1],					name: 'heading',					description: 'Fix whitespace',					help: 'Heading style should be either '						+ "``== Heading ==</tt> or ``==Heading==</tt>."			}); } else if ((m[2] && (predominantSpacingStyle == 'nonSpaced'))		      || (!m[2] && (predominantSpacingStyle == 'spaced'))) { var spacer = (m[2]) ? '' : ' ';			b.push({					start: m.start,					end: m.end,					replacement: m[1] + spacer + m[3] + spacer + m[1],					name: 'heading-style',					description: 'Conform to the existing majority of '						+ ((m[2]) ? 'non-spaced' : 'spaced') + ' headings',					help: 'There are two styles of writing headings in wikitext:<ul><li>== Spaced ==<li>==Non-spaced==</ul>'						+ 'Most of the headings in this article are '						+ ((m[2]) ? 'non-spaced' : 'spaced')						+ ' (' + counters.spaced + ' vs ' + counters.nonSpaced + ').  '						+ 'It is recommended that you adapt your style to the majority.'			}); }		var oldLevel = level; level = m[1].length - 1; if ( (level - oldLevel > 1) && (!isSection || (oldLevel > 0)) ) { var h = '======='.substring(0, oldLevel + 2); b.push({					start: m.start,					end: m.end,					replacement: h + m[2] + m[3] + m[2] + h,					name: 'heading-nesting',					description: 'Fix improper nesting',					help: 'A heading ' + ct.hlink('WP:MOS#Section_headings', 'should be')						+ ' nested one level deeper than its parent heading.'			}); }		var frequentMistakes = [ { code: 'see-also', wrong: /^see *al+so$/i,          correct: 'See also' }, { code: 'ext-links', wrong: /^external links?$/i,    correct: 'External links' }, { code: 'refs',     wrong: /^ref+e?r+en(c|s)es?$/i,  correct: 'References' } ];		for (var j = 0; j < frequentMistakes.length; j++) { var fm = frequentMistakes[j]; if (fm.wrong.test(m[3]) && (m[3] != fm.correct)) { var r = m[1] + m[2] + fm.correct + m[2] + m[1]; if (r != m[0]) { b.push({							start: m.start,							end: m.end,							replacement: r,							name: fm.code,							description: 'Change to ``' + fm.correct + ".",							help: 'The correct spelling/capitalisation is ``' + fm.correct + "</tt>."					}); }			}		}		if (titleSet[m[3]] != null) { b.push({					start: m.start + (m[1] || ).length + (m[2] || ).length,					end: m.start + (m[1] || ).length + (m[2] || ).length + m[3].length,					replacement: null, // we cannot propose anything, it's the editor who has to choose a different title					name: 'duplicate-title',					description: 'Avoid duplicate section titles',					help: 'Section names '						+ ct.hlink('WP:MOS#Section_headings', 'should preferably be unique')						+ ' within a page; this applies even for the names of subsections.'			}); }		titleSet[m[3]] = true; }	return b; }); ct.rules.push(function (s) { // U+2013 and U+2014 are an ndash and an mdash var re = /\( *(?:b\.? *)?({year}) *(?:[\-\\u2013\\u2014]|–|&mdash;|--) *\)/g; var a = ct.getAllMatches(re, s); for (var i = 0; i < a.length; i++) { var m = a[i]; a[i] = { start: m.start, end: m.end, replacement: '(born ' + m[1] + ')', name: 'born', description: 'The word \'born\' should be fully written.', help: 'According to ' + ct.hlink('WP:DATE#Dates_of_birth_and_death', 'WP:DATE') + ', the word born should be fully written.' };	}	return a; }); ct.rules.push(function (s) { // ISBN: ten or thirteen digits, each digit optionally followed by a hyphen, the last digit can be 'X' or 'x' var a = ct.getAllMatches(/ISBN *=? *(([0-9Xx]-?)+)/gi, s); var b = []; for (var i = 0; i < a.length; i++) { var m = a[i]; var s = m[1].replace(/[^0-9Xx]+/g, '').toUpperCase; // remove all non-digits if ((s.length !== 10) && (s.length !== 13)) { b.push({					start: m.start,					end: m.end,					name: 'ISBN',					description: 'Should be either 10 or 13 digits long',					help: 'ISBN numbers should be either 10 or 13 digits long. '							+ 'This one consists of ' + s.length + ' digits: <tt>' + m[1] + '</tt>'			}); continue; }		var isNew = (s.length === 13); // old (10 digits) or new (13 digits) var xIndex = s.indexOf('X'); if ((xIndex !== -1) && ((xIndex !== 9) || isNew)) { b.push({					start: m.start,					end: m.end,					name: 'ISBN',					description: 'Improper usage of X as a digit',					help: "``<tt>X</tt>'' can only be used in 10-digit ISBN numbers "							+ ' as the last digit: <tt>' + m[1] + '</tt>'			}); continue; }		var computedChecksum = 0; var modulus = (isNew) ? 10 : 11;		for (var j = s.length - 2; j >= 0; j--) { var digit = s.charCodeAt(j) - 48; // 48 is the ASCII code of '0' var quotient = (isNew) ? ((j & 1) ? 3 : 1) // the new way: 1 for even, 3 for odd : (10 - j);        // the old way: 10, 9, 8, etc computedChecksum = (computedChecksum + (quotient * digit)) % modulus; }		computedChecksum = (modulus - computedChecksum) % modulus; var c = s.charCodeAt(s.length - 1) - 48; var actualChecksum = ((c < 0) || (9 < c)) ? 10 : c;		if (computedChecksum === actualChecksum) { continue; }		b.push({				start: m.start,				end: m.end,				name: 'ISBN',				description: 'Bad ISBN checksum',				help: 'Bad ISBN checksum for <tt>' + m[1] + '</tt> '		}); }	return b; }); } // end if (window.wgContentLanguage === 'en') // Search box for Mediawiki // (c) 2006 User:Zocky, released under GPL // importStylesheetURI('http://en.wikipedia.org/w/index.php?title=User:Zocky/SearchBox.css&action=raw&ctype=text/css'); var sr$t; var sr$f; var sr$s; var sr$r; var sr$w; var sr$i; var sr$re; var sr$mc; function $e(id) {return document.getElementById(id)} function srBack {  if (sr$s.value=='') {sr$t.focus; return }  if (sr$re.checked) {    var searchString = sr$s.value;  } else {    searchString=sr$s.value.replace(/([\[\]\{\}\|\.\*\?\(\)\$\^\\])/g,'\\$1');  }  searchString="("+searchString+")(?![\\s\\S]*"+searchString+")";  if (sr$mc.checked)    var re=new RegExp(searchString);  else    var re=new RegExp(searchString,"i");  var res = re.exec (sr$t.value.substring(0,sr$t.selectionStart));  if (!res) {    var res = re.exec (sr$t.value)  }  if (res) {   sr$t.selectionStart=res.index; sr$t.selectionEnd=res.index+res[1].length; } else sr$t.selectionStart=sr$t.selectionEnd; srSync; } function srNext { if (sr$s.value=='') {sr$t.focus; return } if (sr$re.checked) { var searchString = sr$s.value; } else { searchString=sr$s.value.replace(/([\[\]\{\}\|\.\*\?\(\)\$\^\\])/g,'\\$1'); } if (sr$mc.checked) var re=new RegExp(searchString,"g"); else var re=new RegExp(searchString,"gi"); re.lastIndex=sr$t.selectionEnd; var res = re.exec (sr$t.value) if (!res) { re.lastIndex=0; var res = re.exec (sr$t.value) } if (res) {   sr$t.selectionStart=res.index; sr$t.selectionEnd=res.index+res[0].length; } else sr$t.selectionStart=sr$t.selectionEnd; srSync; } function srReplace { var sels=sr$t.selectionStart; var sele=sr$t.selectionEnd; var selr=sr$t.value.length-sele; if (sr$s.value=='' || sels==sele) {sr$t.focus; return } if (sr$re.checked) { var searchString = sr$s.value; var replaceString = sr$r.value; } else { searchString=sr$s.value.replace(/([\[\]\{\}\|\.\*\?\(\)\$\^\\])/g,'\\$1'); replaceString=sr$r.value.replace(/([\$\\])/g,'\\$1'); } if (sr$mc.checked) var re=new RegExp(searchString,"g"); else var re=new RegExp(searchString,"gi"); re.lastIndex=sels; var res = re.exec (sr$t.value); var $$=0; if (res && res.index==sels && res[0].length==sele-sels) {   if (sr$re.checked) { replaceString=replaceString.replace(/\\\\/g,'&backslash;'); var replaceBits=(" "+replaceString).split(/(?=\$\d)/); replaceString=replaceBits[0].substring(1); for (var i=1; i<replaceBits.length; i++) {       $$=replaceBits[i][1]-'0'; if ($$<res.length) replaceString += res[$$] + replaceBits[i].substring(2) else replaceString += replaceBits[i]; }    replaceString=replaceString.replace (/\\n/,"\n").replace (/&backslash;/g,"\\").replace (/&dollar;/g,"\$") }    sr$t.value= sr$t.value.substring(0,sels) + replaceString + sr$t.value.substring(sele); } sr$t.selectionStart=sels; sr$t.selectionEnd=sr$t.value.length-selr; srSync; } function srReplaceall { if (!sr$s.value) {sr$t.focus; return } var sels=sr$t.selectionStart; var sele=sr$t.selectionEnd; var selr=sr$t.value.length-sele; var reps; if (sr$re.checked) { var searchString = sr$s.value; var replaceString = sr$r.value.replace(/\\\\/,'&backslash;').replace(/\\n/,'\n').replace(/&backslash;/,"\\"); } else { searchString=sr$s.value.replace(/([\[\]\{\}\|\.\*\?\(\)\$\^\\])/g,'\\$1'); replaceString=sr$r.value.replace(/([\$\\])/g,'\\$1'); } if (sele>sels) reps=sr$t.value.substring(sels,sele); else reps=sr$t.value; if (sr$mc.checked) var re=new RegExp(searchString,"g"); else var re=new RegExp(searchString,"gi"); var replaceCounter=0; var replaceFunc=function{replaceCounter++;return replaceString}; reps=reps.replace(re,replaceString); if (sele>sels) sr$t.value = sr$t.value.substring(0,sels) + reps + sr$t.value.substring(sele); else sr$t.value = reps; sr$t.selectionStart=sels; sr$t.selectionEnd=sele>sels ? sr$t.value.length-selr : sels; window.status = replaceCounter+" ocurrences of " + searchString + " replaced."; srSync; } function srToggleCase { var sels=sr$t.selectionStart; var sele=sr$t.selectionEnd; var selr=sr$t.value.length-sele; var selt=sr$t.value.substring(sels,sele); if (sele>sels) {   if (selt==selt.toUpperCase) selt=selt.toLowerCase else if (selt==selt.toLowerCase && sele-sels>1) selt=selt.substring(0,1).toUpperCase+selt.substring(1).toLowerCase else selt=selt.toUpperCase; sr$t.value = sr$t.value.substring(0,sels) + selt + sr$t.value.substring(sele); sr$t.selectionStart=sels; sr$t.selectionEnd=sele>sels ? sr$t.value.length-selr : sels; } srSync; } function srSync {   var i;    var allLines=0; var lineNo=0; var w=sr$t.cols-5; var dummy=sr$t.value.split("\n"); for (i=0;i<dummy.length;i++){allLines+=Math.ceil(dummy[i].length/w)} var dummy=sr$t.value.substring(0,sr$t.selectionStart).split("\n"); for (i=0;i<dummy.length;i++){lineNo+=Math.ceil(dummy[i].length/w)} //   alert (w+" "+lineNo+"/"+allLines); sr$t.scrollTop=sr$t.scrollHeight*(lineNo-10)/allLines; sr$t.focus; } function srInit { if($e('wpTextbox1')) { var srBoxCode =' ' var ep=$e('searchInput'); if (ep) ep.accessKey='none'; sr$t=document.editform.wpTextbox1; sr$w=sr$t.style.width; var sr=document.createElement('div'); sr.innerHTML=srBoxCode; var im=document.createElement('span'); im.innerHTML='<a id="SearchIcon" href="javascript:srShowHide"><img style="cursor: pointer;" title="Search/Replace" alt="Search/Replace" src="http://upload.wikimedia.org/wikipedia/en/1/12/Button_find.png" border="0" height="22" width="23"></a><a href="javascript:srToggleCase"><img style="cursor: pointer;" title="Toggle case" alt="Toggle case" src="http://upload.wikimedia.org/wikipedia/en/1/12/Button_case.png" border="0" height="22" width="23"></a>'; var ep=$e('toolbar'); if (ep) {     ep.appendChild(im) }   else {     var ep=$e('editform'); ep.parentNode.insertBefore(im,ep); }    sr$i=$e('SearchIcon'); sr$i.accessKey="F"; sr.firstChild.style.display='none'; var ep=$e('editform'); ep.parentNode.insertBefore(sr,ep); sr$f=$e('srForm'); sr$s=$e('srSearch'); sr$r=$e('srReplace'); sr$re=$e('srRegexp'); sr$mc=$e('srCase'); } } function srShowHide { if (sr$f.style.display=='none') {   sr$f.style.display='block'; sr$i.accessKey="none"; sr$t.style.width='auto'; sr$s.focus; } else {   sr$f.style.display='none'; sr$t.style.width=sr$w; sr$i.accessKey="F"; } } addOnloadHook(srInit); //