Jump to content

User:Cacycle/editor dev.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Cacycle (talk | contribs) at 19:22, 12 February 2006. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
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.
/* <pre><nowiki> */


/*

Comfortable JavaScript editor extension for Wikipedia edit pages by [[User:Cacycle]]
See [[User:Cacycle/Editor]] for a description and [[User:Cacycle/Editor.js]] for this code.
Features include:
* Regular expression search and replace
* Server-independent ''Show preview'' and ''Show changes''
* One-click fixing of common mistakes
* Undo/redo
* Input boxes with history
See [[User:Cacycle/Editor]] for an installation guide.
The program works only for the mozilla browsers Mozilla, Mozilla Firefox, and Mozilla SeaMonkey.
The code is currently under active development and might change rapidly.
This code has been released into the public domain.

*/


//
// configuration variables
//

// levels of undo (each level holds the whole text)
var undoBufferMax = undoBufferMax || 20;

// style for preview box
var stylePreviewBox = stylePreviewBox || 'background-color: #f9f9f9;';

// style for custom edit buttons
var styleButtons = styleButtons || 'font-size: smaller; padding-left: 0.1em; padding-right: 0.1em; margin-left: 0.1em; margin-right: 0.1em; height: 1.6em; vertical-align: bottom;';

// formatting button options
var spacesAroundPipes = spacesAroundPipes || true;

// history length for summary, find and replace fields
var findHistoryLength = findHistoryLength || 10;

// presets for input field dropdown options
var presetOptions = presetOptions || [];
presetOptions['summary'] = presetOptions['summary'] || [
	'Copyedit',
	'Linkfix',
	'Reverting vandalism',
	'Formatting source text'
];

// expiration time span for history cookies in seconds
var cookieExpireSec = cookieExpireSec || (365 * 24 * 60 * 60);


// global variables

// history
var fieldHist = [];
var cookieName = [];
var inputElement = [];
var selectElement = [];

var checkMarker = [];
checkMarker[true] = '\u2022';
checkMarker[false] = '\u22c5';

// undo
var undoBuffer = new Array(undoBufferMax);
var undoBufferSelStart = new Array(undoBufferMax);
var undoBufferSelEnd = new Array(undoBufferMax);
var undoBufferFirst = 0;
var undoBufferLast = 0;
var undoBufferCurr = 0;

// fullscreen
var normalTextareaWidth;
var normalTextareaHeight;
var normalTextareaMargin;
var normalTextareaRows;
var normalPageXOffset;
var normalPageYOffset;
var normalTreePos = {};
var fullScreenMode = false;
var fullButtonValue = 'Full screen';
var fullButtonTitle = 'Full screen editing mode';
var normalButtonValue = 'Normal view';
var normalButtonTitle = 'Back no normal page view';
var normalFloatButtonValue = 'Back';

// counter
var i;
var j;


// load the editor after page loading
if (window.addOnloadHook != null) {
	addOnloadHook(SetupEditor);
}

//
// find and replace functions
//
function Edit(what) {

// add focus to textbox
	document.editform.wpTextbox1.focus();

// get the textarea object
	var textarea = document.editform.wpTextbox1;

// get the scroll position
	var scrollTopPx = textarea.scrollTop;
	var scrollHeightPx = textarea.scrollHeight;

// convert strange spaces, remove non-\n linebreak characters
	convertStrangeSpaces();

// get the text from the textbox
	var text = textarea.value;
	var textNew;

// get the find text
	var find = document.getElementById('findText');
	var findText = find.value;

// get the replace text
	var replace = document.getElementById('replaceText');
	var replaceText = replace.value;

// get checkboxes
	var caseSensitive = document.getElementById('caseSensitive');
	var regExp = document.getElementById('regExp');
	if (regExp == null) { return; }

// changed flags
	var textChanged = false;
	var posChanged = false;

// get the text selection info
	var startPos = textarea.selectionStart;
	var endPos = textarea.selectionEnd;
	var selected = text.substring(startPos, endPos);
	var startPosNew;
	var endPosNew;

// set cursor to position 0 if cursor has not already been set (better solution???)
	if ( (startPos == endPos) && (startPos == text.length) ) {
		textarea.selectionStart = 0;
		textarea.selectionEnd = 0;
		startPos = 0;
		endPos = 0;
	}

// manipulate selected text
	if (selected != '') {

// lowercase selection
		if ('lowercase'.indexOf(what) >= 0) {
			var selectedNew = selected.toLowerCase();
			textNew = text.substring(0, startPos) + selectedNew + text.substring(endPos);
			startPosNew = startPos;
			endPosNew = endPos;
			textChanged = true;
		}

// bold selection
		if ('bold'.indexOf(what) >= 0) {
			var selectedNew;
			if ( /^\'\'\'.*\'\'\'$/.test(selected) ) {
				selectedNew = selected.replace(/^\'\'\'(.*)\'\'\'$/, '$1');
				startPosNew = startPos;
				endPosNew = endPos - 6 ;
			}
			else {
				selectedNew = "'''" + selected + "'''";
				startPosNew = startPos;
				endPosNew = endPos + 6;
			}
			textNew = text.substring(0, startPos) + selectedNew + text.substring(endPos);
			textChanged = true;
		}

// italic selection
		if ('italic'.indexOf(what) >= 0) {
			var selectedNew;
			if ( /^\'\'.*\'\'$/.test(selected) ) {
				selectedNew = selected.replace(/^\'\'(.*)\'\'$/, '$1');
				startPosNew = startPos;
				endPosNew = endPos - 4 ;
			}
			else {
				selectedNew = "''" + selected + "''";
				startPosNew = startPos;
				endPosNew = endPos + 4;
			}
			textNew = text.substring(0, startPos) + selectedNew + text.substring(endPos);
			textChanged = true;
		}
	}

// increase heading level
	if ('headingmore'.indexOf(what) >= 0) {
		var selectedNew = '';

// nothing selected, get current line
		if (selected == '') {
			var lineStart = text.lastIndexOf('\n', startPos - 1) + 1;
			var lineEnd = text.indexOf('\n', startPos);
			if (lineEnd < 0) {
				lineEnd = text.length;
			}
			selectedNew = text.substring(lineStart, lineEnd);

// increase heading level
			if ( /^\=\=.*\=\= *$/.test(selectedNew) ) {
				selectedNew = selectedNew.replace(/^(\=\=+) *(.*?) *(\=\=+) *$/, '=$1 $2 $3=');
			}

// make the line a heading
			else {
				selectedNew = selectedNew.replace(/(^ +| +$)/g, '');
				if (selectedNew.length < 80) {
					selectedNew = '== ' + selectedNew + ' ==';
				}
				else {
					lineStart = startPos;
					lineEnd = endPos;
					selectedNew = selected;
				}
			}
			startPosNew = lineStart;
			endPosNew = lineStart;
			textNew = text.substring(0, lineStart) + selectedNew + text.substring(lineEnd);
		}

// increase all headings in selected text
		else {
			var lines = selected.split('\n');

// cycle trough the lines
			for (i = 0; i < lines.length; i++) {
				var line = lines[i];

// increase heading level in selected text
				if ( /^==.*== *$/.test(line) ) {
					line = line.replace(/^(==+) *(.*?) *(==+) *$/, '$1= $2 =$3');
				}
				selectedNew += line;
				if (i < lines.length - 1) {
					selectedNew += '\n';
				}
			}
			startPosNew = startPos;
			endPosNew = startPos + selectedNew.length;
			textNew = text.substring(0, startPos) + selectedNew + text.substring(endPos);
		}
		textChanged = true;
	}

// decrease heading level
	if ('headingless'.indexOf(what) >= 0) {
		var selectedNew = '';

// nothing selected, get current line
		if (selected == '') {
			var lineStart = text.lastIndexOf('\n', startPos - 1) + 1;
			var lineEnd = text.indexOf('\n', startPos);
			if (lineEnd < 0) {
				lineEnd = text.length;
			}
			selectedNew = text.substring(lineStart, lineEnd);

// decrease heading level
			if ( /^===.*=== *$/.test(selectedNew) ) {
				selectedNew = selectedNew.replace(/^=(==.*==)= *$/, '$1');
			}
			else if ( /^==.*==$/.test(selectedNew) ) {
				selectedNew = selectedNew.replace(/^== *(.*) *== *$/, '$1');
			}
			startPosNew = lineStart;
			endPosNew = lineStart;
			textNew = text.substring(0, lineStart) + selectedNew + text.substring(lineEnd);
		}

// increase all headings in selected text
		else {
			var lines = selected.split('\n');

// cycle trough the lines
			for (i = 0; i < lines.length; i++) {
				var line = lines[i];

// decrease heading level in selected text
				if ( /^===.*=== *$/.test(line) ) {
					line = line.replace(/^=(==.*==)= *$/, '$1');
				}
				selectedNew += line;
				if (i < lines.length - 1) {
					selectedNew += '\n';
				}
			}
			startPosNew = startPos;
			endPosNew = startPos + selectedNew.length;
			textNew = text.substring(0, startPos) + selectedNew + text.substring(endPos);
		}
		textChanged = true;
	}

// replacements and text fixes
	if ('spaces html punct caps dashes units math'.indexOf(what) >= 0) {

		var startPosFix;
		var endPosFix;
		var selectedFix;

// apply to whole text if nothing is selected
		if (startPos == endPos) {
			startPosFix = 0;
			endPosFix = text.length;
			selectedFix = text;
		}
		else {
			startPosFix = startPos;
			endPosFix = endPos;
			selectedFix = selected;
		}

// apply fixes to selected text
				if ('spaces'.indexOf(what) >= 0) { selectedFix = FixSpaces(selectedFix); }
		else if ('html'.indexOf  (what) >= 0) { selectedFix = FixHTML  (selectedFix); }
		else if ('punct'.indexOf (what) >= 0) { selectedFix = FixPunct (selectedFix); }
		else if ('caps'.indexOf  (what) >= 0) { selectedFix = FixCaps  (selectedFix); }
		else if ('dashes'.indexOf(what) >= 0) { selectedFix = FixDashes(selectedFix); }
		else if ('units'.indexOf (what) >= 0) { selectedFix = FixUnits (selectedFix); }
		else if ('math'.indexOf  (what) >= 0) { selectedFix = FixMath  (selectedFix); }

// remove newlines and spaces
		selectedFix = selectedFix.replace(/\n{3,}/g, '\n\n');
		selectedFix = selectedFix.replace(/^\n+/, '');
		selectedFix = selectedFix.replace(/\n{2,}$/, '\n');

// set selection
		if (startPos == endPos) {
			startPosNew = startPos;
			endPosNew = startPos;
		}
		else {
			startPosNew = startPos;
			endPosNew = startPos + selectedFix.length;
		}

// insert selected into unchanged text
		textNew = text.substring(0, startPosFix) + selectedFix + text.substring(endPosFix);
		textChanged = true;
		posChanged = true;
	}

// prepare find regexp for find and replace
	var regExpFlags = '';
	if ('findprev findnext replaceprev replacenext replaceall'.indexOf(what) >= 0) {

// format the find text as regexp or plain text
		if (regExp.checked) {

// replace \n with newline character, other characters have already been converted
			replaceText = replaceText.replace(/((^|[^\\])(\\\\)*)\\n/g, '$1\n');
		}
		else {
			findText = findText.replace(/([\\^\$\*\+\?\.\(\)\[\]\{\}\:\=\!\|\,\-])/g, '\\$1');
		}

// set regexp flag i
		if ( ! caseSensitive.checked ) {
			regExpFlags = 'i';
		}
	}

// find / replace
	if ('findnext replacenext findprev replaceprev'.indexOf(what) >= 0) {
		if (find.value != '') {

// create regexp
			var regExpFind = new RegExp(findText, regExpFlags + 'g');

// set start position for search to right
			var indexStart;
			var result;
			if ('findnext replacenext'.indexOf(what) >= 0) {
				indexStart = startPos;
				if ( (selected.length > 0) && ('findnext'.indexOf(what) >= 0) ) {
					indexStart = startPos + 1;
				}

// execute the regexp search to the right
				regExpFind.lastIndex = indexStart;
				result = regExpFind.exec(text);
			}

// prepare search to the left
			else {

// set start position for search to left
				indexStart = startPos - 1;
				if ( (selected.length > 0) && ('replaceprev'.indexOf(what) >= 0) ) {
					indexStart = startPos;
				}

// cycle through the matches to the left
				var resultNext;
				do {
					result = resultNext;
					resultNext = regExpFind.exec(text);
					if (resultNext == null) {
						break;
					}
				} while (resultNext.index <= indexStart);
			}

// get the matched string
			var matched;
			var matchedStart;
			var matchedLength;
			if (result != null) {
				matched = result[0];
				matchedStart = result.index;
				matchedLength = matched.length;

// replace only if the next match was already selected
				if ('replacenext replaceprev'.indexOf(what) >= 0) {
					if (selected == matched) {
						var replace = selected.replace(regExpFind, replaceText);
						textNew = text.substr(0, matchedStart) + replace + text.substr(matchedStart + matched.length);
						matchedLength = replace.length;
						textChanged = true;
					}
				}

// select the found match in the textarea
				startPosNew = matchedStart;
				endPosNew = matchedStart + matchedLength;
			}
			else {
				if ('findprev replaceprev'.indexOf(what) >= 0) {
					indexStart = startPos;
				}
				startPosNew = indexStart;
				endPosNew = indexStart;
			}
			posChanged = true;
		}
	}

// replace all
	if ('replaceall'.indexOf(what) >= 0) {
		if (find.value != '') {

// create regexp
			var regExpFind = new RegExp(findText, regExpFlags + 'g');


// replace all in whole text
			if (selected == '') {

// get the new cursorposition
				textNew = text.replace(regExpFind, replaceText);
				var textbefore = textNew.substr(0, startPos);
				textbefore = textbefore.replace(regExpFind, replaceText);
				startPosNew = textbefore.length;
				endPosNew = startPosNew;
				posChanged = true;
			}

// replace all in selection
			else {
				var replace = selected.replace(regExpFind, replaceText);
				startPosNew = startPos;
				endPosNew = startPos + replace.length;
				textNew = text.substr(0, startPos) + replace + text.substr(endPos);
			}
			textChanged = true;
		}
	}

// save search history to cookie
	if ('findnext findprev'.indexOf(what) >= 0) {
		AddToHistory('find');
	}
	if ('replacenext replaceprev replaceall'.indexOf(what) >= 0) {
		AddToHistory('find');
		AddToHistory('replace');
	}

// get the find field from the selection
	if ('findnext findprev replacenext replaceprev getfind'.indexOf(what) >= 0) {
		if ( ('getfind'.indexOf(what) >= 0) || ( (find.value == '') && (selected != '') ) ) {
			if (regExp.checked) {
				find.value = selected.replace(/\n/g, '\\n');
			}
			else {
				find.value = selected.replace(/\n.*/, '');
				startPosNew = startPos;
				endPosNew = endPos;
			}
		}
	}

// undo all
	if ('undoall'.indexOf(what) >= 0) {
		startPosNew = startPos;
		endPosNew = startPos;
		textNew = editformOrig;
		textChanged = true;
	}

// jump to top / bottom
	if ('updown'.indexOf(what) >= 0) {
		if (scrollTopPx > scrollHeightPx / 2) {
			startPosNew = 0;
			endPosNew = 0
		}
		else {
			startPosNew = text.length;
			endPosNew = text.length;
		}
		posChanged = true;
	}

// change textarea, save undo info
	if (textChanged) {
		textarea.value = textNew;
		SaveUndo(text, startPos, endPos);
		SaveUndo(textNew, startPosNew, endPosNew);
	}

// set the selection range
	textarea.setSelectionRange(startPosNew, endPosNew);

// scroll the textarea to the selected text or cursor position
	if (posChanged) {
		ScrollToSelection();
	}
	else {
		textarea.scrollTop = scrollTopPx;
	}
	return;
}


//
// scroll the textarea to the selected text or cursor position
//

function ScrollToSelection() {

	var textarea = document.editform.wpTextbox1;
	var text = textarea.value;
	var rows = textarea.rows;
	var cols = textarea.cols;

// get total number of rows
	var totalRows = GetRows(text, rows, cols);

// determine scroll position
	if (totalRows >= rows) {
		var startPos = textarea.selectionStart;

// breaking of the cursor row depends on trailing characters
		var nextspace = text.indexOf(' ', startPos);
		var nextreturn = text.indexOf('\n', startPos);
		var tostart = text.substr(0, startPos);
		var fromstart = text.substr(startPos);
		fromstart = fromstart.replace(/\s.*/g, '');

// get the rows number of the cursor position
		var currentRow = GetRows(tostart + fromstart, rows, cols);
		currentRow = currentRow - rows / 2;
		if (currentRow < 0) {
			currentRow = 0;
		}

// set scroll position
		var scrollHeightRel = (currentRow - 1) / (totalRows);
		var scrollHeightPx = textarea.scrollHeight;
		var scrollTopPxNew = parseInt(scrollHeightRel * scrollHeightPx);
		textarea.scrollTop = scrollTopPxNew;
	}
	return;
}


//
// get number of rows for a text in a textarea
//

function GetRows(text, rows, cols) {
	var totalRows = 0;
	var lines = text.split('\n');

// cycle trough the lines
	for (i = 0; i < lines.length; i++) {
		totalRows ++;
		var line = lines[i];

// simulate breaking of long lines
		while (line.length > cols) {

// break line before border
			var spaceBefore = line.lastIndexOf(' ', cols);
			if (spaceBefore >= 0) {
				line = line.substr(spaceBefore + 1);
			}
			else {

// break line after border
				var spaceAfter = line.indexOf(' ', cols);
				if (spaceAfter >= 0) {
					line = line.substr(spaceAfter + 1);
					line.replace(/^ */, '');
				}

// no spaces, no break
				else {
					line = '';
				}
			}
			if (line.length > 0) {
				totalRows ++;
			}
		}
	}
	return(totalRows);
}


//
// fix characters, spaces, empty lines, certain headings
//

function FixSpaces(text) {

// remove trailing spaces from lines
	text = text.replace(/ +\n/g, '\n');

// empty line before and after headings, spaces around word (lookahead)
	text = text.replace(/(\n={2,}) *([^\n]*?) *(={2,})(?=\n)/g, '\n$1 $2 $3\n\n');

// uppercase important headings
	text = text.replace(/\n== external links? ==\n/ig, '\n== External links ==\n');
	text = text.replace(/\n== see also ==\n/ig, '\n== See also ==\n');
	text = text.replace(/\n== references? ==\n/ig, '\n== References ==\n');

// add space after * # : ; (list) and after {| |- | (table)
	text = text.replace(/(^|\n)([\*\#\:\;]+|\{\||\|\-|\|\}|\|) */g, '$1$2 ');

// empty line before and after tables
	text = text.replace(/\n+(\{\|)/g, '\n\n$1');
	text = text.replace(/(\n\|\}) *([^\n]*)[\n|$]+/g, '$1\n\n$2\n\n');

// empty line before and after lists
	text = text.replace(/(^|\n)([^\*\#\:\;].*?)\n+([\*\#\:\;])/g, '$1$2\n\n$3');
	text = text.replace(/(^|\n)([\*\#\:\;].*?)\n+([^\*\#\:\;])/g, '$1$2\n\n$3');

// split into lines and change single lines, used to handle tables
	var lines = text.split('\n');
	text = '';
	var tableflag = false;
	for (var i = 0; i < lines.length; i++) {
		var line = lines[i];

// do not change lines starting with a blank
		if ( ! line.match(/^ /) ) {

// detect table
			if ( line.match(/^(\{\||\!|\|[^}])/) ) {
				tableflag = true;
			}
			else if ( line.match(/^\|\}/) ) {
				tableflag = false;
			}

// changes only to be done in tables
			if (tableflag) {

// add spaces around ||
				line = line.replace(/ *\|\| */g, ' || ');

// to do: add spaces around | in tables without changing image markup!
			}

// changes not to be done in tables
			if ( ! tableflag) {

// empty line before and after images
				line = line.replace(/^(\[\[image:.*?\]\])/ig, '\n$1');
				line = line.replace(/(\[\[image:.*?(\[\[.*?\]\].*?)*\]\])$/ig, '$1\n');

// empty line before and after includes
				line = line.replace(/^(\{\{.*?\}\})/g, '\n$1');
				line = line.replace(/(\{\{.*?\}\})$/g, '$1\n');

// to be done: convert single newlines into spaces
//      line = line.replace(/(\n[^\n \*\#\:\;\|\{].*?)\n([^\n \*\#\:\;\|\{])/g, '$1 $2');
			}
		}

// concatenate the lines
		text += line;
		if (i < lines.length - 1) {
			text += '\n';
		}
	}

// remove spaces in wikilinks
	text = text.replace(/\[\[ *([^\n]*?) *\]\]/g, '[[$1]]');

// remove spaces in external links
	text = text.replace(/\[ *([^\n]*?) *\]/g, '[$1]');

// space around pipes in wikilinks but not in images
	if (spacesAroundPipes) {
		text = text.replace(/(\[\[(?!image:)[^\n]+?) *\| *(.*?\]\])/ig, '$1 | $2');

// space around pipes in templates
		text = text.replace(/(\{\{)([^\n]+?)(\}\})/g,
			function (p, p1, p2, p3) {
				p2 = p2.replace(/ *(\|) */g, ' | ');
				return(p1 + p2 + p3);
			}
		);
	}

// no space around pipes before brackets
	text = text.replace(/ +\| +\]\]/g, '|]]');

// no space around pipes before curly brackets
	text = text.replace(/ +\| +\}\}/g, '|}}');

// no empty line between headings and includes
	text = text.replace(/\n(==+ [^\n]*? ==+\n)\n+(\{\{.*?\}\})/g, '$1$2');

// empty line before and after categories
	text = text.replace(/(\[\[category:[^\n]*?\]\]) */gi, '\n\n$1\n\n');

// categories not separated by empty lines (lookahead)
	text = text.replace(/(\[\[category:[^\n]*?\]\])\n*(?=\[\[category:[^\n]*?\]\])/gi, '$1\n');

	return(text);
}


//
// fix html to wikicode
//

function FixHTML(text) {

// fix basic
	text = FixSpaces(text);

// convert italic
	text = text.replace(/<i>|<\/i>/g, '\'\'');

// convert bold
	text = text.replace(/<b>|<\/b>/g, '\'\'\'');

// to do: tables

	return(text);
}


//
// fix space before punctuation marks
//

function FixPunct(text) {

// fix basic
	text = FixSpaces(text);

// remove space before .,: (; could be a definition)
	text = text.replace(/([a-zA-Z\'\"\”\]\}\)]) +([\.\,\:])/g, '$1$2');

	return(text);
}

//
// fix capitalizing of lists, linklists, images, headings
//
function FixCaps(text) {


// fix basic
	text = FixSpaces(text);

// uppercase lists
	text = text.replace(/(^|\n)([\*\#\:\;]+ [^\&\w\n]*)(\w+)/g,
		function (p, p1, p2, p3) {
			if ( ! p3.match(/^(http|ftp|alpha|beta|gamma|delta|epsilon|kappa|lambda)/) ) {
				p3 = p3.substr(0, 1).toUpperCase() + p3.substr(1);
			}
			return(p1 + p2 + p3);
		}
	);

// uppercase link lists (link)
	text = text.replace(/(^|\n)([\*\#\:\;]+ \[\[)([^\n]*?)(\]\])/g,
		function (p, p1, p2, p3, p4) {

// uppercase link
			p3 = p3.replace(/^([^\&\w\n]*)(\w+)/g,
				function (p, p1, p2) {
					if ( ! p2.match(/^(http|ftp|alpha|beta|gamma|delta|epsilon|kappa|lambda)/) ) {
						p2 = p2.substr(0, 1).toUpperCase() + p2.substr(1);
					}
					return(p1 + p2);
				}
			);

// uppercase comment
			p3 = p3.replace(/(\| [^\&\w\n]*)(\w+)/g,
				function (p, p1, p2) {
					if ( ! p2.match(/^(http|ftp|alpha|beta|gamma|delta|epsilon|kappa|lambda)/) ) {
						p2 = p2.substr(0, 1).toUpperCase() + p2.substr(1);
					}
					return(p1 + p2);
				}
			);
			return(p1 + p2 + p3 + p4);
		}
	);

// uppercase headings
	text = text.replace(/(^|\n)(==+ )([^\&\w\n]*\w)([^\n]* ==+(\n|$))/ig,
		function (p, p1, p2, p3, p4) {
			return(p1 + p2 + p3.toUpperCase() + p4);
		}
	);

// uppercase images
	text = text.replace(/(\[\[)image:(\w)([^\n]*\]\])/ig,
		function (p, p1, p2, p3) {
			return(p1 + 'Image:' + p2.toUpperCase() + p3);
		}
	);

	return(text);
}


//
// dash fixer - adds a tab that fixes several obvious en/em dash, minus sign, and such special characters.
// originally from User:Omegatron
//

function FixDashes(text) {

// fix basic
	text = FixSpaces(text);

// convert html entities into actual dash characters
	text = text.replace(/&mdash;/g, '—');
	text = text.replace(/&ndash;/g, '–');
	text = text.replace(/&minus;/g, '\u2212');

// convert -- and em dashes with or without spaces to em dash surrounded by spaces
	text = text.replace(/([a-zA-Z\'\"”\]\}\)]) *(--|—|&mdash;) *([a-zA-Z\'\"“\[\{\(])/g, '$1 — $3');

// convert - or en dashes with spaces to em dash character surrounded by spaces
	text = text.replace(/([a-zA-Z\'\"”\]\}])( |&nbsp;)+(\u2212|–|&ndash;) +([a-zA-Z\'\"“\[\{])/g, '$1$2— $4');

// convert hyphen next to lone number into a minus sign character
	text = text.replace(/([a-zA-Z\'\"”\]\>] )-(\d)/g, '$1\u2212$2');

// convert dashes to en dashes in dates
	text = text.replace(/([ \(][12]\d\d\d) ?(--?|—|&mdash;) ?([12]\d\d\d|\d\d)([ \),.;])/g, '$1–$3$4');

	return(text);
}


//
// unit formatter - new tab adds spaces between number and units, makes units consistent
// originally from User:Omegatron
//

function FixUnits(text) {

// fix basic
	text = FixSpaces(text);

// convert all &deg; into actual ° symbol
	text = text.replace(/&deg;/g, '°');

// convert the word ohm(s) or the html entity into the actual O symbol (Omega, not the actual ohm symbol &#8486;) and make sure it's spaced
	text = text.replace(/(\d) ?(Y|Z|E|P|T|G|M|k|K|h|da|d|c|m|µ|µ|µ|n|p|f|a|z|y)? ?(&Omega;|ohm|Ohm)s?([ ,.])/g, '$1 $2O$4');

// convert various micro symbols into the actual micro symbol, make sure it's spaced
	text = text.replace(/(\d) ?(&mu;|µ|&micro;)(g|s|m|A|K|mol|cd|rad|sr|Hz|N|J|W|Pa|lm|lx|C|V|O|F|Wb|T|H|S|Bq|Gy|Sv|kat|°C|M)([ ,.])/g, '$1 µ$3$4');

// convert capital K to lowercase k in units
	text = text.replace(/(\d) ?K(g|s|m|A|K|mol|cd|rad|sr|Hz|N|J|W|Pa|lm|lx|C|V|O|F|Wb|T|H|S|Bq|Gy|Sv|kat|°C|M)([ ,.])/g, '$1 k$2$3');

// capitalize units correctly
	text = text.replace(/(\d) ?(khz)([ ,.])/gi, '$1 kHz$3');
	text = text.replace(/(\d) ?(mhz)([ ,.])/gi, '$1 MHz$3');
	text = text.replace(/(\d) ?(ghz)([ ,.])/gi, '$1 GHz$3');
	text = text.replace(/(\d) ?(Y|Z|E|P|T|G|M|k|K|h|da|d|c|m|µ|µ|µ|n|p|f|a|z|y)?(hz|HZ)([ ,.])/g, '$1 $2Hz$4');
	text = text.replace(/(\d) ?(Y|Z|E|P|T|G|M|k|K|h|da|d|c|m|µ|µ|µ|n|p|f|a|z|y)?(pa|PA)([ ,.])/g, '$1 $2Pa$4');

// add a space before dB or B
	text = text.replace(/(\d) ?(dB|B)\b/g, '$1 $2');

// add a space before any units that were missed before
	text = text.replace(/(\d) ?(Y|Z|E|P|T|G|M|k|K|h|da|d|c|m|µ|n|p|f|a|z|y)?(g|m|A|K|mol|cd|rad|sr|Hz|N|J|W|Pa|lm|lx|C|V|O|F|Wb|T|H|S|Bq|Gy|Sv|kat|°C|M)([ ,.])/g, '$1 $2$3$4');

// separate one for seconds since they give a lot of false positives like "1970s". Only difference is mandatory prefix.
	text = text.replace(/(\d) ?(Y|Z|E|P|T|G|M|k|K|h|da|d|c|m|µ|n|p|f|a|z|y)(s)([ ,.])/g, '$1 $2$3$4');

// bps or b/s or bits/s --> bit/s
	text = text.replace(/([KkMmGgTtPpEeYyZz])(bps|bits?\/s|b\/s)/g, '$1bit/s');

// Bps or byte/s or bytes/s --> B/s
	text = text.replace(/([KkMmGgTtPpEeYyZz])(Bps|bytes?\/s)/g, '$1B/s');

// after that, make capitalization correct
	text = text.replace(/K(bit|B)\/s/g, 'k$1/s');
	text = text.replace(/m(bit|B)\/s/g, 'M$1/s');
	text = text.replace(/g(bit|B)\/s/g, 'G$1/s');
	text = text.replace(/t(bit|B)\/s/g, 'T$1/s');
	text = text.replace(/e(bit|B)\/s/g, 'E$1/s');
	text = text.replace(/y(bit|B)\/s/g, 'Y$1/s');
	text = text.replace(/z(bit|B)\/s/g, 'Z$1/s');

// fix a common error
	text = text.replace(/mibi(bit|byte)/g, 'mebi$1');

	return(text);
}


//
// math character fixer, originally from User:Omegatron
//
// DO NOT USE ON WHOLE DOCUMENT OR <math> </math> WIKICODE!
//

function FixMath(text) {

// fix basic
	text = FixSpaces(text);

// convert html entities into actual dash characters
	text = text.replace(/&minus;/g, '\u2212');
	text = text.replace(/&middot;/g, '·');

// convert dash next to a number into a minus sign character
	text = text.replace(/([^a-zA-Z0-9\,\_\{])-(\d)/g, '$1\u2212$2');

// changes 2x3 to 2×3
	text = text.replace(/(\d ?)x( ?\d)/g, '$1×$2');

// changes 10^3 to 10<sup>3</sup>
	text = text.replace(/(\d*\.?\d+)\^(\u2212?\d+\.?\d*)/g, '$1<sup>$2</sup>');

// change x^3 to x<sup>3</sup>
	text = text.replace(/([0-9a-zA-Z])\^(\u2212?\d+\.?\d*) /g, '$1<sup>$2</sup>');

// change +/- to ±
	text = text.replace(/( |\d)\+\/(-|\u2212)( |\d)/g, '$1±$3');

	return(text);
}


//
// add a tag to the summary box
//

function AddSummary(summary) {
	var text = document.editform.wpSummary;
	if (text.value.match(/ \*\/ $/)) {
		text += ' ';
	}
	else if (text.value != '') {
		text.value += '; ';
	}
	text.value += summary;
}


//
// save undo information
//

function SaveUndo(text, startPos, endPos) {

	var textarea = document.editform.wpTextbox1;
	if (undoBufferLast == 0) {
		undoBuffer[1] = textarea.value;
		undoBufferSelStart[1] = startPos;
		undoBufferSelEnd[1] = endPos;
		undoBufferCurr = 1;
		undoBufferLast = 1;
	}
	undoBufferLast++;
	undoBufferCurr = undoBufferLast;
	var slot = undoBufferLast % undoBufferMax;
	undoBuffer[slot] = text;
	undoBufferSelStart[slot] = startPos;
	undoBufferSelEnd[slot] = endPos;
}


//
//undo
//

function Undo() {

	var textarea = document.editform.wpTextbox1;
	if (undoBufferCurr - 1 > undoBufferLast - undoBufferMax) {
		if (undoBufferCurr - 1 >= 0) {
			undoBufferCurr--;
			var slot = undoBufferCurr % undoBufferMax;
			textarea.value = undoBuffer[slot];
			textarea.focus();
			textarea.selectionStart = undoBufferSelStart[slot];
			textarea.selectionEnd = undoBufferSelEnd[slot];
			ScrollToSelection();
		}
	}
}


//
// redo
//

function Redo() {

	var textarea = document.editform.wpTextbox1;
	if (undoBufferCurr + 1 <= undoBufferLast) {
		undoBufferCurr++;
		var slot = undoBufferCurr % undoBufferMax;
		var slot = undoBufferCurr % undoBufferMax;
		textarea.value = undoBuffer[slot];
		textarea.focus();
		textarea.selectionStart = undoBufferSelStart[slot];
		textarea.selectionEnd = undoBufferSelEnd[slot];
		ScrollToSelection();
	}
}

//
// resize textarea to ~100% by adapting cols
//

function ResizeTextarea() {

	var textarea = document.editform.wpTextbox1;
	textarea.style.width = '100%';
	var widthMax = textarea.offsetWidth;
	textarea.style.width = 'auto';

// provoke a scrollbar
	var textLength = textarea.value.length;
	var cols = textarea.cols;
	var fillstring = '\n\n';
	for (i = 0; i < cols; i++) {
		fillstring += '\n';
	}

// add the filling string
	var startPos = textarea.selectionStart;
	var endPos = textarea.selectionEnd;
	var scrollTopPx = textarea.scrollTop;
	textarea.value += fillstring;

// find optimal width
	textarea.cols = 20;
	for (var i = 64; i >= 1; i = i / 2) {
		while (textarea.offsetWidth < widthMax) {
			textarea.cols = textarea.cols + i;
		}
		textarea.cols = textarea.cols - i;
	}

// remove filling string
	textarea.value = textarea.value.substr(0, textLength);
	textarea.selectionStart = startPos;
	textarea.selectionEnd = endPos;
	textarea.scrollTop = scrollTopPx;

	return;
}


//
// convert strange spaces, remove non-\n linebreak characters
//

function convertStrangeSpaces() {
	var textarea = document.editform.wpTextbox1;
	var startPos = textarea.selectionStart;
	var endPos = textarea.selectionEnd;

	var text = textarea.value;
	text = text.replace(/[\t\v\u00a0\u2028\u2029]+/g, ' '); // \u00a0 = &nbsp;
	text = text.replace(/[\r\f]/g, '');
	textarea.value = text;

	textarea.selectionStart = startPos;
	textarea.selectionEnd = endPos;

	return;
}


//
// setup routine for javascript editor
//

function SetupEditor() {

	var html = '';

// check if the editor is already installed
	if (document.getElementById('findText') != null) { return; }

// at the moment this works only for mozilla browsers (Mozilla, Mozilla Firefox, Mozilla SeaMonkey)
	var name = navigator.appName.match(/Netscape/i)[0];
	if (name == null) { return; }
	var version = navigator.appVersion.match(/\d+(\.\d+)/)[0];
	if (version == null) { return; }
	if (version < 5.0) { return; }

// get the textarea object
	var textarea = document.editform.wpTextbox1;
	if (textarea == null) { return; }

// install the diff functions
	SetupDiff();

// setup the undo buffers and get the original text for instant change view
	undoBuffer[0] = textarea.value;
	editformOrig = textarea.value;

// set textarea size to maximal row number
	window.onresize = ResizeTextarea;
	ResizeTextarea();

// convert strange spaces, remove non-\n linebreak characters
	convertStrangeSpaces();

// add custom edit area stylesheet definition to head
	var insert = document.getElementsByTagName('head')[0];
	html = '';
	html += '<style type="text/css">';
	html += '.customEdit { ' + styleButtons + '}';
	html += '.previewBox { ' + stylePreviewBox + ' }';
	html += '</style>';
	insert.innerHTML += html;

// create inputWrapper for textarea and buttons (fullscreen elements)
	var inputWrapper = document.createElement('div');
	inputWrapper.id = 'inputWrapper';
	textarea.parentNode.insertBefore(inputWrapper, textarea);

// move textarea to textareaWrapper
	var textareaWrapper = document.createElement('div');
	textareaWrapper.id = 'textareaWrapper';
	inputWrapper.appendChild(textareaWrapper);
	textareaWrapper.appendChild(textarea);

// add all other buttons and inputs to buttonsWrapper
	var buttonsWrapper = document.createElement('div');
	buttonsWrapper.id = 'buttonsWrapper';
	inputWrapper.appendChild(buttonsWrapper);

// add custom formatting buttons
	var customEditButtons = document.createElement('div');
	customEditButtons.id ='customEditButtons';
	html = '';

// find, replace
	html += '<div style="margin-top: 0.2em; margin-left: 0;" id="customEditRow1">';
	html += '<input class="customEdit" type="button" value="Get" onclick="javascript:Edit(\'getfind\');" title="Get the find text from the selection">';
	html += '<input class="customEdit" type="button" value="&larr;Find" onclick="javascript:Edit(\'findprev\');" title="Find previous">';
	html += '<span style="position: relative; padding: 0; margin: 0 0.2em;" id="findComboInput">';
	html += '<input class="customEdit" type="text" value="" style="height: 1.4em; font-family: monospace; height: 1.2em; padding: 0; margin: 0; position: absolute; left: 0; top: 0; z-index: 2;" onfocus="javascript:this.setSelectionRange(0, this.textLength);" id="findText" title="">';
	html += '<select class="customEdit" id="findSelect" style="height: 1.5em; font-family: monospace; border: none; padding: 0; margin: 0; position: relative; vertical-align: baseline; z-index: 1;" onfocus="javascript:SetComboOptions(\'find\')" onChange="javascript:ChangeComboInput(\'find\');">';
	html += '</select>';
	html += '</span>';
	html += '<input class="customEdit" type="button" value="Find&rarr;" onclick="javascript:Edit(\'findnext\');" title="Find next">';
	html += '<span style="margin-left: 0.5em;"></span/>';
	html += '<input class="customEdit" type="button" value="&uarr;&darr;" onclick="javascript:Edit(\'updown\');" title="Jump to the top / bottom">';
	html += '<span style="margin-left: 1em;"></span/>';
	html += '<input class="customEdit" type="button" value="&larr;" onclick="javascript:Undo();" title="Undo buttons">';
	html += '<input class="customEdit" type="button" value="&rarr;" onclick="javascript:Redo();" title="Redo buttons">';
	html += '<span style="margin-left: 0.5em;"></span/>';
	html += '<input class="customEdit" type="button" value="Undo all" onclick="javascript:Edit(\'undoall\');" title="Restore original text, can be undone">';
	html += '<span style="margin-left: 1em;"></span/>';
	html += '<input class="customEdit" type="button" style="font-weight: bold;" value="b" onclick="javascript:Edit(\'bold\');" title="Bold text">';
	html += '<input class="customEdit" type="button" style="font-style: italic;" value="i" onclick="javascript:Edit(\'italic\');" title="Italic text">';
	html += '<input class="customEdit" type="button" value="A&rarr;a" onclick="javascript:Edit(\'lowercase\');" title="Lowercase text">';
	html += '<span style="margin-left: 0.5em;"></span/>';
	html += '<input class="customEdit" type="button" value="=&larr;" onclick="javascript:Edit(\'headingless\');" title="Decrease heading level">';
	html += '<input class="customEdit" type="button" value="&rarr;==" onclick="javascript:Edit(\'headingmore\');" title="Increase heading level">';
	html += '<span style="margin-left: 0.5em;"></span/>';
	html += '<input class="customEdit" type="button" id="fullScreenButtonFloat" style="display: none; position: absolute; z-index: 5;">';
	html += '<input class="customEdit" type="button" id="fullScreenButton">';
	html += '</div>';

// fixing functions
	html += '<div style="margin-top: 0.2em; margin-bottom: 0.5em; margin-left: 0;" id="customEditRow2">';
	html += '<input class="customEdit" type="button" value="All" onclick="javascript:Edit(\'replaceall\');" title="Replace all in whole text or selection">';
	html += '<input class="customEdit" type="button" value="&larr;Repl." onclick="javascript:Edit(\'replaceprev\');" title="Replace previous">';
	html += '<span style="position: relative; padding: 0; margin: 0 0.2em;" id="replaceComboInput">';
	html += '<input class="customEdit" type="text" value="" style="height: 1.4em; font-family: monospace; height: 1.2em; padding: 0; margin: 0; position: absolute; left: 0; top: 0; z-index: 2;" onfocus="this.setSelectionRange(0, this.textLength);" id="replaceText" title="">';
	html += '<select class="customEdit" id="replaceSelect" style="height: 1.5em; font-family: monospace; border: none; padding: 0; margin: 0; position: relative; vertical-align: baseline; z-index: 1;" onfocus="SetComboOptions(\'replace\')" onChange="javascript:ChangeComboInput(\'replace\');">';
	html += '</select>';
	html += '</span>';
	html += '<input class="customEdit" type="button" value="Repl.&rarr;" onclick="javascript:Edit(\'replacenext\');" title="Replace">';
	html += '<span style="margin-left: 0.2em;"></span/>';
	html += '<span title="Search should be case sensitive"><input class="customEdit" style="margin: 0;" type="checkbox" value="1" id="caseSensitive"> Case</span>';
	html += '<span style="margin-left: 0.2em;"></span/>';
	html += '<span title="Search should be a regular expression"><input class="customEdit" style="margin: 0;" type="checkbox" value="1" id="regExp"> Regexp</span>';
	html += '<span style="margin-left: 1em;">Fix:</span/>';
	html += '<input class="customEdit" type="button" value="Basic" onclick="javascript:Edit(\'spaces\');" title="Fix blanks and empty lines">';
	html += '<input class="customEdit" type="button" value="&mdash;" onclick="javascript:Edit(\'dashes\');" title="Fix dashes">';
	html += '<input class="customEdit" type="button" value="k&Omega;" onclick="javascript:Edit(\'units\');" title="Fix units">';
	html += '<input class="customEdit" type="button" value="&radic;" onclick="javascript:Edit(\'math\');" title="Fix math, DO NOT USE ON WHOLE TEXT OR <math></math> WIKICODE!!!">';
	html += '<input class="customEdit" type="button" value="html" onclick="javascript:Edit(\'html\');" title="Fix html to wikicode">';
	html += '<input class="customEdit" type="button" value=".,:" onclick="javascript:Edit(\'punct\');" title="Fix spaces before puntuation">';
	html += '<input class="customEdit" type="button" value="Aa" onclick="javascript:Edit(\'caps\');" title="Fix caps in headers and lists">';
	html += '</div>';
	customEditButtons.innerHTML = html;
	buttonsWrapper.appendChild(customEditButtons);

// add elements to buttonsWrapper
	var element = document.getElementById('editpage-copywarn');
	while (element != null) {
		if (element.id == 'editpage-specialchars') {
			break;
		}
		next_element = element.nextSibling;
		buttonsWrapper.appendChild(element);
		element = next_element;
	}

// add preview and changes buttons
	var customPreview = document.createElement('span');
	customPreview.id = 'customPreviewButtons';
	html = '';
	html += '<span style="margin-left: 0.5em; margin-right: 0.5em">';
	html += 'Instant:\n';
	html += '<input type="button" class="customEdit" title="Show a preview below" value="Preview" id="instantPreview" onclick="NormalScreen(); document.getElementById(\'PreviewBox\').innerHTML = wiki2html(editform.wpTextbox1.value);" />';
	html += '<input type="button" class="customEdit" title="Show changes since your last preview below" value="Changes" id="instantDiff" onclick="NormalScreen(); document.getElementById(\'PreviewBox\').innerHTML = StringDiff(editformOrig, editform.wpTextbox1.value);" />';
	html += '<input type="button" class="customEdit" title="Clear the preview box" value="Clear" id="instantClear" onclick="NormalScreen(); document.getElementById(\'PreviewBox\').innerHTML = \'\';" />';
	html += '</span>';
	html += 'Server:\n';
	customPreview.innerHTML = html;
	var preview = document.getElementById('wpPreview');
	preview.parentNode.insertBefore(customPreview, preview);

// add preview box
	var previewBox = document.createElement('div');
	previewBox.id = 'customPreviewBox';
	html = '';
	html += '<div style="margin-top: 0.5em; margin-bottom: 0.5em; border-width: 1px; border-style: solid; border-color: #808080 #d0d0d0 #d0d0d0 #808080;" id="PreviewBoxOutline">';
	html += '<div class="previewBox" style="padding: 5px; border-width: 1px; border-style: solid; border-color: #404040 #ffffff #ffffff #404040;" id="PreviewBox">';
	html += '</div>';
	html += '</div>';
	previewBox.innerHTML = html;
	inputWrapper.parentNode.insertBefore(previewBox, inputWrapper.nextSibling);

// move linebreak before checkboxes down
	var summary = document.getElementById('wpSummary');
	var checkboxSep = document.createTextNode('');
	summary.parentNode.replaceChild(checkboxSep, summary.nextSibling);

// move 'Summary:' into submit button div
	var summary = document.getElementById('wpSummary');
	var summaryLabel = document.getElementById('wpSummaryLabel');
	summary.parentNode.insertBefore(summaryLabel, summary.parentNode.firstChild);

// make the summary a combo box
	var summary = document.getElementById('wpSummary');
	var htmlPre = '';
	var htmlPost = '';
	html = '';
	htmlPre  += ' <span style="position: relative;" id="summaryComboInput">';
	html     += ' style="padding: 0; margin: 0; position: absolute; left: 0; top: 0; z-index: 2;" onfocus="this.setSelectionRange(0, this.textLength);"';
	htmlPost += '<select style="border: none; padding: 0; margin: 0; position: relative; vertical-align: middle; z-index: 1;" id="wpSummarySelect" onfocus="javascript:SetComboOptions(\'summary\')" onchange="javascript:ChangeComboInput(\'summary\');">';
	htmlPost += '</select>';
	htmlPost += '</span>';
	summary.parentNode.innerHTML = summary.parentNode.innerHTML.replace(/\s*(<input.*?id\=\"wpSummary\")(.*?>)/, htmlPre + '$1' + html + '$2' + htmlPost);

// add margin around submit buttons
	var saveButton = document.getElementById('wpSave');
	saveButton.parentNode.style.marginTop = '0.7em';
	saveButton.parentNode.style.marginBottom = '0.5em';

// move copywarn down
	var copywarn = document.getElementById('editpage-copywarn');
	inputWrapper.parentNode.insertBefore(copywarn, previewBox.nextSibling);

// shorten submit button texts and add onclick handler
	document.getElementById('wpPreview').value = 'Preview';
	document.getElementById('wpDiff').value = 'Changes';
	window.onsubmit = function() {
		NormalScreen;
		AddToHistory('summary');
	};

// set up combo input boxes with history
	fieldHist ['find'] = [];
	cookieName['find'] = 'findHistory';
	inputElement['find'] = new Object(document.getElementById('findText'));
	selectElement['find'] = new Object(document.getElementById('findSelect'));
	selectElement['find'].style.height = (inputElement['find'].clientHeight + 1) +'px';

	fieldHist ['replace'] = [];
	cookieName['replace'] = 'replaceHistory';
	inputElement['replace'] = new Object(document.getElementById('replaceText'));
	selectElement['replace'] = new Object(document.getElementById('replaceSelect'));
	selectElement['replace'].style.height = (inputElement['replace'].clientHeight + 1) +'px';

	fieldHist ['summary'] = [];
	cookieName['summary'] = 'summaryHistory';
	inputElement['summary'] = new Object(document.getElementById('wpSummary'));
	selectElement['summary'] = new Object(document.getElementById('wpSummarySelect'));
	selectElement['summary'].style.height = (inputElement['summary'].clientHeight + 1) +'px';

	ResizeComboInput('find');
	ResizeComboInput('replace');
	ResizeComboInput('summary');

// setup fullscreen mode

// save textbox properties
	normalTextareaWidth = getStyle(textarea, 'width');
	normalTextareaHeight = getStyle(textarea, 'height');
	normalTextareaMargin = getStyle(textarea, 'margin');
	normalTextareaRows = textarea.rows;

// set fullscreen style fixes
	var inputWrapper = document.getElementById('inputWrapper');
	var content = document.getElementById('content');
	var content = document.getElementById('content');
	inputWrapper.style.lineHeight = getStyle(content, 'line-height');

// move globalWrapper elements to new subGlobalWrapper
	var globalWrapper = document.getElementById('globalWrapper');
	var subGlobalWrapper = document.createElement('div');
	subGlobalWrapper.id = 'subGlobalWrapper';
	globalWrapper.appendChild(subGlobalWrapper);
	var element = globalWrapper.firstChild;
	while (element != null) {
		if (element.id == 'subGlobalWrapper') {
			break;
		}
		next_element = element.nextSibling;
		subGlobalWrapper.appendChild(element);
		element = next_element;
	}

// set original tree position of input area
	normalTreePos = inputWrapper.nextSibling;

// set fullscreen button texts
	var fullScreenButton = document.getElementById('fullScreenButton');
	var floatButton = document.getElementById('fullScreenButtonFloat');
	fullScreenButton.value = fullButtonValue;
	fullScreenButton.title = fullButtonTitle;
	floatButton.value = normalFloatButtonValue;
	floatButton.title = normalButtonTitle;

// set button event handlers
	document.captureEvents(Event.click);
	fullScreenButton.onclick = FullScreen;
	floatButton.onclick = NormalScreen;
	floatButton.onblur = function() {
		floatButton.style.right = '0.5em';
		floatButton.style.bottom = '0.5em';
		floatButton.style.top = '';
		floatButton.style.left = '';
	};
	var saveButton = document.getElementById('wpSave');
	var previewButton = document.getElementById('wpPreview');
	var diffButton = document.getElementById('wpDiff');
	saveButton.onclick = NormalScreen;
	previewButton.onclick = NormalScreen;
	diffButton.onclick = NormalScreen;
	return;
}


//
// FullScreen: change to fullscreen input area; event handler for fullscreen buttons
//

function FullScreen(event) {
	fullScreenMode = true;

// save window scroll position
	normalPageYOffset = window.pageYOffset;
	normalPageXOffset = window.pageXOffset;

// get fulltext button coordinates
	var buttonOffsetLeft = event.pageX - window.pageXOffset;
	var buttonOffsetTop = event.pageY - window.pageYOffset;

// move the input area up in the tree
	var inputWrapper = document.getElementById('inputWrapper');
	var globalWrapper = document.getElementById('globalWrapper');
	var subGlobalWrapper = document.getElementById('subGlobalWrapper');
	globalWrapper.insertBefore(inputWrapper, subGlobalWrapper);

// set input area to fullscreen
	inputWrapper.style.position = 'fixed';
	inputWrapper.style.top = '0';
	inputWrapper.style.left = '0';
	inputWrapper.style.right = '0';
	inputWrapper.style.bottom = '0';
	inputWrapper.style.backgroundColor = getStyle(content, 'background-color');
	var buttonsWrapper = document.getElementById('buttonsWrapper');
	buttonsWrapper.style.paddingLeft = '0.5em'
	buttonsWrapper.style.paddingBottom = '0.5em'

// set textarea size
	var textarea = document.getElementById('wpTextbox1');
	textarea.style.margin = '0';

// set the textarea to maximal height
	var textareaWrapper = document.getElementById('textareaWrapper');
	textarea.style.height = (window.innerHeight - buttonsWrapper.offsetHeight - 4) + 'px';

// hide the rest of the page
	subGlobalWrapper.style.display = 'none';

// set floating 'back to normal' button
	var floatButton = document.getElementById('fullScreenButtonFloat');
	floatButton.style.right = '';
	floatButton.style.bottomt = '';
	floatButton.style.display = 'inline';
	floatButton.style.left = (buttonOffsetLeft - floatButton.offsetWidth / 2) + 'px';
	floatButton.style.top = (buttonOffsetTop - floatButton.offsetHeight / 2) + 'px';
	floatButton.focus();

// change fullscreen button text and handler
	var fullScreenButton = document.getElementById('fullScreenButton');
	fullScreenButton.value = normalButtonValue;
	fullScreenButton.title = normalButtonTitle;
	fullScreenButton.onclick = NormalScreen;

// resize textarea to defined cols number
	ResizeTextarea();
	return;
}


//
// NormalScreen: change back to normal page view; event handler for fulscreen buttons
//

function NormalScreen() {

// check if we are in fullscreen mode
	if (fullScreenMode != true) {
		return;
	}
	fullScreenMode = false;

// hide floating 'back to normal' button
	var floatButton = document.getElementById('fullScreenButtonFloat').style.display = 'none';

// show the rest of the page
	document.getElementById('subGlobalWrapper').style.display = 'block';

// set input area back to the original position
	var inputWrapper = document.getElementById('inputWrapper');
	normalTreePos.parentNode.insertBefore(inputWrapper, normalTreePos);
	inputWrapper.style.position = 'static';
	inputWrapper.style.height = '';
	inputWrapper.style.backgroundColor = '';

// reset textarea settings
	var textarea = document.getElementById('wpTextbox1');
	textarea.style.width = normalTextareaWidth;
	textarea.style.height = normalTextareaHeight;
	textarea.style.margin = normalTextareaMargin;
	textarea.rows = normalTextareaRows;
	document.getElementById('buttonsWrapper').style.padding = '';

// change fullscreen button text and handler
	var fullScreenButton = document.getElementById('fullScreenButton');
	fullScreenButton.value = fullButtonValue;
	fullScreenButton.title = fullButtonTitle;
	fullScreenButton.onclick = FullScreen;

// reset window scroll position
	window.scrollTo(normalPageXOffset, normalPageYOffset);

// resize textarea to defined cols number
	ResizeTextarea();
	return;
}


//
// ResizeComboInput: set the size of the background select boxes so that the button is visible
//

function ResizeComboInput(field) {

// add a dummy option
	var dummy;
	if (selectElement[field].options.length == 0) {
		selectElement[field].options[0] = new Option('');
		dummy = true;
	}

// set option widths to 0
	for (i = 0; i < selectElement[field].options.length; i ++) {
		selectElement[field].options[i].style.width = '0';
	}

// calculate select width
	var inputWidth = inputElement[field].clientWidth;
	var selectWidth = selectElement[field].clientWidth;
	var optionWidth = selectElement[field].options[0].offsetWidth;
	var border = inputElement[field].offsetWidth - inputElement[field].clientWidth;
	selectElement[field].style.width = (selectWidth - optionWidth + inputWidth - border) + 'px';

// delete dummy option
	if (dummy) {
		selectElement[field].options[0] = null;
	}

// set option widths to auto
	for (i = 0; i < selectElement[field].options.length; i ++) {
		selectElement[field].options[i].style.width = 'auto';
	}
	return;
}


//
// ChangeComboInput: set the input value to selected option; onchange event handler for select boxes
//

function ChangeComboInput(field) {

// get selection index (-1 for unselected)
	var selected = selectElement[field].selectedIndex;
	if (selected >= 0) {

// get selected option
		var option = selectElement[field].options[selected];
		if (option.text != '') {

// add case and regexp checkboxes to find / replace fields
			if (option.value == 'setcheck') {
				document.getElementById('caseSensitive').checked
					= ( option.text.charAt(0) == checkMarker[true] );
				document.getElementById('regExp').checked
					= ( option.text.charAt(1) == checkMarker[true] );
				inputElement[field].value = option.text.substr(3);
			}
			else {
				inputElement[field].value = option.text;
			}
		}
	}
	return;
}


//
// AddToHistory: add an input value to the cookie history
//

function AddToHistory(field) {
	if (inputElement[field].value != '') {

// load history from cookie
	LoadHistoryFromCookie(field);

// add current value to history
		fieldHist[field].unshift(inputElement[field].value);

// add case and regexp checkboxes to find / replace value
		if ( (field == 'find') || (field == 'replace') ) {
			fieldHist[field][0] =
				checkMarker[ document.getElementById('caseSensitive').checked ] +
				checkMarker[ document.getElementById('regExp').checked ] +
				' ' + fieldHist[field][0];
		}

// remove multiple old copies from history
		i = 1;
		while (i < fieldHist[field].length) {
			if (fieldHist[field][i] == fieldHist[field][0]) {
				fieldHist[field].splice(i, 1);
			}
			else {
				i ++;
			}
		}

// remove new value if it is a preset value
		i = 0;
		if (presetOptions[field] != null) {
			while (i < presetOptions[field].length) {
				if (presetOptions[field][i] == fieldHist[field][0]) {
					fieldHist[field].shift;
					break;
				}
				else {
					i ++;
				}
			}
		}

// cut history to maximal history length
		fieldHist[field] = fieldHist[field].slice(0, findHistoryLength);

// saved history to cookie
		SaveHistoryToCookie(field);
	}
	return;
}


//
// SetComboOptions: generate the select options from cookie history; onfocus handler for select box
//

function SetComboOptions(field) {

// load history from cookie
	LoadHistoryFromCookie(field);

	var option = {};
	var selected = null;
	j = 0;

// delete options
	var options = selectElement[field].options;
	for (i = 0; i > options.length; i ++) {
		selectElement[field].remove(i);
	}

// delete optgroup
	option = document.getElementById(field + 'Optgroup');
	if (option != null) {
		selectElement[field].removeChild(option);
	}

// workaround for onchange not firing when selecting first option from unselected dropdown
	option = document.createElement('option');
	option.style.display = 'none';
	selectElement[field].options[j++] = option;

// add history entries
	for (i = 0; i < fieldHist[field].length; i ++) {
		if (fieldHist[field][i] != null) {
			if (fieldHist[field][i] == inputElement[field].value) {
				selected = j;
			}
			option = document.createElement('option');
			option.text = fieldHist[field][i];
			if ( (field == 'find') || (field == 'replace') ) {
				option.value = 'setcheck';
			}
			selectElement[field].options[j++] = option;
		}
	}

// add preset entries
	if (presetOptions[field] != null) {
		var startPreset = j;
		for (i = 0; i < presetOptions[field].length; i ++) {
			if (presetOptions[field][i] != null) {
				if (presetOptions[field][i] == inputElement[field].value) {
					selected = j;
				}
				option = document.createElement('option');
				option.text = presetOptions[field][i];
				selectElement[field].options[j++] = option;
			}
		}

// add a blank separator
		if (startPreset > 1) {
			option = document.createElement('optgroup');
			option.label = '\u00a0';
			option.id = field + 'Optgroup';
			selectElement[field].insertBefore(option, selectElement[field].options[startPreset]);
		}
	}

// set the selection
	selectElement[field].selectedIndex = selected;
	return;
}


//
// LoadHistoryFromCookie: get the input box history from the respective cookie
//

function LoadHistoryFromCookie(field) {
	var cookie = GetCookie(cookieName[field]);
	if (cookie != null) {
		cookie = decodeURIComponent(cookie);
		fieldHist[field] = cookie.split('\n');
	}
	return;
}


//
// SaveHistoryToCookie: save the input box history to the respective cookie
//

function SaveHistoryToCookie(field) {
	var cookieExpire = new Date();
	cookieExpire.setTime( cookieExpire.getTime() + cookieExpireSec * 1000 );
	var cookie = '';
	cookie = fieldHist[field].join('\n')
	cookie = encodeURIComponent(cookie);
	SetCookie(cookieName[field], cookie, cookieExpire.toGMTString());
	return;
}


// getStyle: get style prperties for non-inline css definitions
function getStyle(element, styleProperty) {
	var style;
	if (element != null) {
		style = document.defaultView.getComputedStyle(element, null).getPropertyValue(styleProperty);
	}
	return(style);
}


//
// getOffsetLeft: get element offset relative to left window border
//

function getOffsetLeft (element) {
	var offset = 0;
	do {
		offset += element.offsetLeft;
	} while ( (element = element.offsetParent) != null );
	return(offset);
}


//
// getOffsetTop: get element offset relative to left window border
//

function getOffsetTop (element) {
	var offset = 0;
	do {
		offset += element.offsetTop;
	} while ( (element = element.offsetParent) != null );
	return(offset);
}


//
// GetCookie
//

function GetCookie(name) {
	var cookie = ' ' + document.cookie;
	var search = ' ' + name + '=';
	var setStr = null;
	var offset = 0;
	var end = 0;
	if (cookie.length > 0) {
		offset = cookie.indexOf(search);
		if (offset != -1) {
			offset += search.length;
			end = cookie.indexOf(';', offset)
			if (end == -1) {
				end = cookie.length;
			}
			setStr = cookie.substring(offset, end);
			setStr = setStr.replace(/\\+/g, ' ');
			setStr = decodeURIComponent(setStr);
		}
	}
	return(setStr);
}


//
// SetCookie
//

function SetCookie(name, value, expires, path, domain, secure) {
	document.cookie = name + '=' + encodeURIComponent(value) +
		((expires) ? '; expires=' + expires : '') +
		((path)    ? '; path=' + path : '') +
		((domain)  ? '; domain=' + domain : '') +
		((secure)  ? '; secure' : '');
}


/* </nowiki></pre> */