User:Dacheatbot/editor.js

From Homestar Runner Wiki

Jump to: navigation, search

Note: After saving, you may have to bypass your browser's cache to see the changes.

  • Mozilla / Firefox: hold down Shift while clicking Reload, or press Ctrl-Shift-R (Cmd-Shift-R on Apple Mac)
  • Safari: press Cmd-Option-E
  • IE: hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Konqueror: simply click the Reload button, or press F5
  • Opera users may need to completely clear their cache in Tools→Preferences.
// ==UserScript==
// @name          JavaScript editor for Wikimedia websites
// @namespace     http://www.hrwiki.org/
// @description   A custom editor currently being used by HRWikian X-Spider2.
// @include       *wiki*
// ==/UserScript==
// Thanks to Wikipedian Cacycle for this script
// 
// Name:    editor.js
// Version: 0.5.0 (June 18, 2006)
// Info:    http://en.wikipedia.org/wiki/User:Cacycle/editor
// Code:    http://en.wikipedia.org/wiki/User:Cacycle/editor.js
// 
// Comfortable JavaScript editor extension for Wikimedia edit pages by [[Wikipedia:User:Cacycle]]
// See [[Wikipedia:User:Cacycle/Editor]] for a description and [[Wikipedia: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
// * Convert html tables and other markup to wikicode
// * Undo/redo
// * Input boxes with history
// * Fullscreen view
// * Find ahead as you type
// * Horizontal cursor memory
// See [[Wikipedia: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;';

// 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);

// enable cursor horizontal position memory
var cursorMemory = cursorMemory || true;

// show at least this number of lines ahead of cursor movement
var scrollMargin = scrollMargin || 1;

// show at least this number of lines ahead of cursor movement for
var findMargin = findMargin || 2;

// find ahead checkbox selected by default
var findAheadSelected = findAheadSelected || true;


// 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';

// textarea text info object
var textRows = new Object();
textRows.lineStart = [];
textRows.lineLength = [];
textRows.rowStart = [];
textRows.rowLength = [];

var textareaElement = {};
var lastChangePos;

// 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
 textareaElement.focus();

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

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

 var textNew;
  var textLength = textareaElement.value.length;

// 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');

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

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

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

// lowercase selection
 if ('lowercase'.indexOf(what) >= 0) {
 var selectedNew = selected.toLowerCase();
 textNew = textareaElement.value.substring(0, startPos) + selectedNew + textareaElement.value.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 = textareaElement.value.substring(0, startPos) + selectedNew + textareaElement.value.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 = textareaElement.value.substring(0, startPos) + selectedNew + textareaElement.value.substring(endPos);
 textChanged = true;
 }
 }

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

// nothing selected, get current line
 if (selected == '') {
 var lineStart = textareaElement.value.lastIndexOf('\n', startPos - 1) + 1;
 var lineEnd = textareaElement.value.indexOf('\n', startPos);
 if (lineEnd < 0) {
 lineEnd = textLength;
 }
 selectedNew = textareaElement.value.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 = textareaElement.value.substring(0, lineStart) + selectedNew + textareaElement.value.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 = textareaElement.value.substring(0, startPos) + selectedNew + textareaElement.value.substring(endPos);
 }
 textChanged = true;
 }

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

// nothing selected, get current line
 if (selected == '') {
 var lineStart = textareaElement.value.lastIndexOf('\n', startPos - 1) + 1;
 var lineEnd = textareaElement.value.indexOf('\n', startPos);
 if (lineEnd < 0) {
 lineEnd = textLength;
 }
 selectedNew = textareaElement.value.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 = textareaElement.value.substring(0, lineStart) + selectedNew + textareaElement.value.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 = textareaElement.value.substring(0, startPos) + selectedNew + textareaElement.value.substring(endPos);
 }
 textChanged = true;
 }

// replacements and text fixes
 if ('spaces pipes 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 = textLength;
 selectedFix = textareaElement.value;
 }
 else {
 startPosFix = startPos;
 endPosFix = endPos;
 selectedFix = selected;
 }

// apply fixes to selected text
 if      ('spaces'.indexOf(what) >= 0) { selectedFix = FixSpaces(selectedFix); }
 else if ('pipes'.indexOf (what) >= 0) { selectedFix = FixPipes (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 = textareaElement.value.substring(0, startPosFix) + selectedFix + textareaElement.value.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(textareaElement.value);
 }

// 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(textareaElement.value);
 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 = textareaElement.value.substr(0, matchedStart) + replace + textareaElement.value.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 (findText != '') {

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


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

// get the new cursorposition
 textNew = textareaElement.value.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 = textareaElement.value.substr(0, startPos) + replace + textareaElement.value.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 or the current word
 if ('findnext findprev replacenext replaceprev getfind'.indexOf(what) >= 0) {
 if ( ('getfind'.indexOf(what) >= 0) || (find.value == '') ) {

// get from the selection
 var newFind = '';
 if (selected != '') {
   newFind = selected;
 startPosNew = startPos;
 endPosNew = endPos;
 }

// get from the current word
 else {

// get until next nonword char to the right
 endPosNew = endPos;
   var pos = startPos;
   while (pos < textLength) {
     var character = textareaElement.value.substr(pos ++, 1);
     if ( character.match(/\W/) ) {
   endPosNew = pos - 1;
       break;
     }
 newFind += character;
   }

// get until next nonword char to the left
 startPosNew = startPos;
   pos = startPos - 1;
   while (pos >= 0) {
     var character = textareaElement.value.substr(pos --, 1);
     if ( character.match(/\W/) ) {
    startPosNew = pos + 2;
       break;
     }
     newFind = character + newFind;
   }
 }

// replace newlines in find field
 if (regExp.checked) {
 find.value = newFind.replace(/\n/g, '\\n');
 }
 else {
 find.value = newFind.replace(/\n.*/, '');
 }
 }
 }

// 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 = textLength;
 endPosNew = textLength;
 }
 posChanged = true;
 }

// jump to the last changed position, event handler for button
 if ('lastchangepos'.indexOf(what) >= 0) {
 startPosNew = lastChangePos;
 endPosNew = lastChangePos;
 posChanged = true;
  }

// changed textarea, save undo info
 if (textChanged) {
 textareaElement.value = textNew;
 SaveUndo(textareaElement.value, startPos, endPos);
 SaveUndo(textNew, startPosNew, endPosNew);
 textRows.changed = true;
 posChanged = true;
 }

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

// scroll the textarea to the selected text or cursor position
 if (posChanged || textChanged) {
 ParseRows();
 }
 if (posChanged) {
   ScrollTextarea(textRows.selStartRow, textRows.selEndRow, 0, findMargin, scrollTopPx);
 }
 else {
 textareaElement.scrollTop = scrollTopPx;
 }
 return;
}


//
// scroll the textarea if the selected text is outside the viewport
//

function ScrollTextarea(rowStart, rowEnd, lines, margin, scrollTopPx) {

// get top row
  var scrollHeightPx = textareaElement.scrollHeight;
  var scrollTopRow = scrollTopPx / scrollHeightPx * textRows.rowTotal;

// cusor direction: up
 if (lines <= 0) {
   if (scrollTopRow > (rowStart + lines) - margin) {
 scrollTopRow = (rowStart + lines) - margin;
 if (scrollTopRow < 0) {
   scrollTopRow = 0;
 }
 }
 }

// cusor direction: down
 if (lines >= 0) {
   if (scrollTopRow < (rowEnd + 1 + lines) + margin - textRows.rows) {
 scrollTopRow = (rowEnd + 1 + lines) + margin - textRows.rows;
 if (scrollTopRow > textRows.rowTotal + 1 - textRows.rows) {
   scrollTopRow = textRows.rowTotal + 1 - textRows.rows;
 }
 }
 }

// set scroll position
 textareaElement.scrollTop = scrollTopRow / textRows.rowTotal * scrollHeightPx;

 return;
}


//
// ParseRows: get row structure of textarea
//

function ParseRows() {

 textRows.selStart = textareaElement.selectionStart;
 textRows.selEnd = textareaElement.selectionEnd;

// if the text has not changed we don't need to parse lines and rows
 if (textRows.changed != true) {
 if (textRows.textarea == null) {
 textRows.changed = true;
 }
 else if (textRows.textarea.length != textareaElement.value.length) {
 textRows.changed = true;
 }
 else if (textRows.textarea != textareaElement.value) {
 textRows.changed = true;
 }
 }
  if (textRows.changed) {
 textRows.changed = false
 textRows.textarea = textareaElement.value;
 textRows.cols = textareaElement.cols;
 textRows.rows = textareaElement.rows;

// parse lines
 textRows.lineStart = [];
 textRows.lineLength = [];
 var pos;
 var posNext = 0;
 var line = 0;
 do {
 pos = posNext;
 textRows.lineStart[line] = pos;
 posNext = textRows.textarea.indexOf('\n', pos) + 1;
 textRows.lineLength[line] = posNext - pos - 1;
 line ++;
 } while (posNext > 0);
 textRows.lineLength[line - 1] = textRows.textarea.length - pos;
   textRows.lineTotal = line;

// parse rows
 textRows.rowStart = [];
 textRows.rowLength = [];
 var lineTotal = textRows.lineTotal;
 var row = 0;
 for (line = 0; line < lineTotal; line ++) {
 var rowStart;
 var rowStartNext = textRows.lineStart[line];
 var lineEnd = rowStartNext + textRows.lineLength[line];

// cycle row by row to the end of the line
 do {
 rowStart = rowStartNext;
 pos = 0;
 posNext = rowStart;
 if (rowStart + textRows.cols >= lineEnd) {
 rowStartNext = lineEnd;
 }

// find last space before or first after right border
 else {
 do {
 pos = posNext;
 posNext = textRows.textarea.indexOf(' ', pos + 1);
 } while ( (posNext >= 0) && (posNext <= rowStart + textRows.cols) && (posNext < lineEnd) );
 if (pos > rowStart) {
 rowStartNext = pos + 1;
 }
 else if ( (posNext >= 0) && (posNext < lineEnd) ) {
 rowStartNext = posNext + 1;
 }
 else {
 rowStartNext = lineEnd;
 }
 }

// jump over trailing spaces
 while (textRows.textarea.charAt(rowStartNext) == ' ') {
 rowStartNext ++;
 }

// set row start and length
 textRows.rowStart[row] = rowStart;
 textRows.rowLength[row] = rowStartNext - rowStart;
 row ++;
 } while (rowStartNext < lineEnd);
 }
   textRows.rowTotal = row;
 }

// get text selection rows by stepwise approximation
 var rowTotal = textRows.rowTotal;
 var selStart = textRows.selStart;
 var selEnd = textRows.selEnd;

// find the largest 2^n < rows
 var add = 1;
 while (add < rowTotal) {
   add = add * 2;
 }
  add = add / 2;

// approximate with decreasing add
 var selStartRow = add;
 var selEndRow = add;
 while (add >= 1) {

// approximate selection start
 if (selStartRow >= rowTotal) {
 selStartRow -= add;
 }
 else if (textRows.rowStart[selStartRow] > selStart) {
 selStartRow -= add;
 }
 else {
 selStartRow += add;
 }

// approximate selection end
 if (selEndRow >= rowTotal) {
 selEndRow -= add;
 }
 else if (textRows.rowStart[selEndRow] > selEnd) {
 selEndRow -= add;
 }
 else {
 selEndRow += add;
 }
   add = add / 2;
 }
 if (textRows.rowStart[selStartRow] > selStart) {
 selStartRow --;
 }
 if (textRows.rowStart[selEndRow] > selEnd) {
 selEndRow --;
 }
 textRows.selStartRow = selStartRow;
 textRows.selEndRow = selEndRow;

 return;
}


//
// 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 ');
 text = text.replace(/ +\n/g, '\n');

// 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, ' || ');
 }

// 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]');

// 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');

// spaces in comments
 text = text.replace(/(<!--) *(.*?) *(-->)/g, '$1 $2 $3');

// empty lines around html comments, spaces in comments
 text = text.replace(/\n+(<!--.*?-->)\n+/g, '\n$1\n\n');
 text = text.replace(/^(<!--.*?-->)\n+/g, '$1\n');
 text = text.replace(/\n+(<!--.*?-->)$/g, '\n$1');

// 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 space around vertical bars
//

function FixPipes(text) {

// fix basic
 text = FixSpaces(text);

// space around pipes in wikilinks but not in images
 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);
 }
 );

 return(text);
}


//
// fix html to wikicode
//

function FixHTML(text) {

// fix basic
 text = FixSpaces(text);

// convert italic
 text = text.replace(/<i(\s.*?)?>|<\/i(\s.*?)?>/gi, '\'\'');

// convert bold
 text = text.replace(/<b(\s.*?)?>|<\/b(\s.*?)?>/gi, '\'\'\'');

// convert tables
 text = text.replace(/\s*<\/td(\s.*?)?>\s*/gi, '');
 text = text.replace(/\s*<\/th(\s.*?)?>\s*/gi, '');
 text = text.replace(/\s*<\/tr(\s.*?)?>\s*/gi, '');

 text = text.replace(/\s*<td\s*>\s*/gi, '\n| ');
 text = text.replace(/\s*<td\s+(.*?)? *>\s*/gi,
 function (p, p1) {
 return('\n| ' + p1.replace(/\s+/g, ' ') + ' | ');
 }
 );
 text = text.replace(/\s*<th\s*>\s*/gi, '\n! ');
 text = text.replace(/\s*<th\s+(.*?)? *>\s*/gi,
 function (p, p1) {
 return('\n! ' + p1.replace(/\s+/g, ' ') + ' | ');
 }
 );

 text = text.replace(/\s*<tr\s*>\s*/g, '\n|-\n');
 text = text.replace(/\s*<tr\s+(.*?)? *>\s*/gi,
 function (p, p1) {
 return('\n|- ' + p1.replace(/\s+/g, ' ') + '\n');
 }
 );

 text = text.replace(/\s*<table\s*>\s*(\|-\n)?/gi, '\n{|\n');
 text = text.replace(/\s*<table\s+(.*?)? *>\s*(\|-\n)?/gi,
 function (p, p1) {
 return('\n{| ' + p1.replace(/\s+/g, ' ') + '\n');
 }
 );
 text = text.replace(/\s*<\/table\s+(.*?)?>\s*/gi, '\n|}\n');

// convert links
 text = text.replace(/<a\s+(.*?)href\s*=\s*(\"|\')\s*(\S*?)\s*(\"|\')(.*?)>\s*(.*?)\s*<\/a>/gi,
 function (p, p1, p2, p3, p4, p5, p6) {
 if (p6 == '') {
 return('[' + p3 + ']');
 }
 return('[' + p3 + ' ' + p6.replace(/\s+/g, ' ') + ']');
 }
 );
 text = text.replace(/<a\s+(.*?)href\s*=\s*(\S*?)\s+(.*?)>\s*(.*?)\s*<\/a>/gi,
 function (p, p1, p2, p3, p4) {
 if (p4 == '') {
 return('[' + p2 + ']');
 }
 return('[' + p2 + ' ' + p4.replace(/\s+/g, ' ') + ']');
 }
 );

// convert images
 text = text.replace(/<img\s+(.*?)src\s*=\s*(\"|\')\s*(\S*?)\s*(\"|\')(.*?)>/gi,
 function (p, p1, p2, p3, p4, p5) {
 return('[[Image:' + p3.replace(/^.*\/([^\/]+)$/, '$1') + ']]');
 }
 );
 text = text.replace(/<img\s+(.*?)src\s*=\s*(\S*?)\s+(.*?)>/gi,
 function (p, p1, p2, p3) {
 return('[[Image:' + p2.replace(/^.*\/([^\/]+)$/, '$1') + ']]');
 }
 );

// to do: lists, h1 - hx

 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(/^([\*\#\:\;]+ (\&\w+\;|\{\{.*$|[\W\d])*)([^\W\d].*)$/gm,
 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 + p3);
 }
 );


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

// uppercase link
 p2 = p2.replace(/^((\&\w+\;|[\W\d])*)([^\W\d].*)$/,
 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 + p3);
 }
 );

// uppercase comment
 p2 = p2.replace(/(\| *(\&\w+\;|[\W\d])*)([^\W\d].*)$/,
 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 + p3);
 }
 );
 return(p1 + p2 + p3);
 }
 );

// uppercase headings
 text = text.replace(/^(==+ (\&\w+\;|[\W\d])*)([^\W\d].* ==+)$/gm,
 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 + p3);
 }
 );

// uppercase images
 text = text.replace(/(\[\[)image:(\w)([^\n]*\]\])/igm,
 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.getElementById('wpSummary');
 if (text.value.match(/ \*\/ $/)) {
 text += ' ';
 }
 else if (text.value != '') {
 text.value += '; ';
 }
 text.value += summary;
}


//
// save undo information
//

function SaveUndo(text, startPos, endPos) {

 if (undoBufferLast == 0) {
 undoBuffer[1] = textareaElement.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() {

 if (undoBufferCurr - 1 > undoBufferLast - undoBufferMax) {
 if (undoBufferCurr - 1 >= 0) {
 undoBufferCurr--;
 var slot = undoBufferCurr % undoBufferMax;
 textareaElement.value = undoBuffer[slot];
 textareaElement.focus();
 textareaElement.selectionStart = undoBufferSelStart[slot];
 textareaElement.selectionEnd = undoBufferSelEnd[slot];
 textRows.changed = true;
 ParseRows();
 ScrollTextarea(textRows.selStartRow, textRows.selEndRow, 0, findMargin, textareaElement.scrollTop);
 }
 }
}


//
// redo
//

function Redo() {

 if (undoBufferCurr + 1 <= undoBufferLast) {
 undoBufferCurr++;
 var slot = undoBufferCurr % undoBufferMax;
 var slot = undoBufferCurr % undoBufferMax;
 textareaElement.value = undoBuffer[slot];
 textareaElement.focus();
 textareaElement.selectionStart = undoBufferSelStart[slot];
 textareaElement.selectionEnd = undoBufferSelEnd[slot];
 textRows.changed = true;
   ParseRows();
   ScrollTextarea(textRows.selStartRow, textRows.selEndRow, 0, findMargin, textareaElement.scrollTop);
 }
}

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

function ResizeTextarea() {

 var textareaClone = document.getElementById('textareaClone');
 var scrollTopPx = textareaElement.scrollTop;
 textareaClone.style.width = '100%';
 textareaClone.style.display = 'block';
 var widthMax = textareaClone.offsetWidth;
 textareaClone.style.width = 'auto';

// find optimal width
 textareaClone.cols = 20;
 for (var i = 64; i >= 1; i = i / 2) {
 while (textareaClone.offsetWidth < widthMax) {
 textareaClone.cols = textareaClone.cols + i;
 }
 textareaClone.cols = textareaClone.cols - i;
 }
 textareaClone.style.display = 'none';
 textareaElement.cols = textareaClone.cols;
 textareaElement.style.width = 'auto';
 textareaElement.scrollTop = scrollTopPx;

// parse rows
 textRows.changed = true;
  ParseRows();

 return;
}


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

function convertStrangeSpaces() {

 var startPos = textareaElement.selectionStart;
 var endPos = textareaElement.selectionEnd;

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

 textareaElement.selectionStart = startPos;
 textareaElement.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 browser = navigator.appName;
 if (browser == null) { return; }
 if (! /Netscape/i.test(browser)) { return; }
 var version = navigator.appVersion.match(/\d+(\.\d+)/)[0];
 if (version == null) { return; }
 if (version < 5.0) { return; }

// get the textarea object
 textareaElement = document.getElementById('wpTextbox1');
 if (textareaElement == null) { return; }

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

// set textarea size to maximal row number, always show vertical scrollbar
  textareaElement.style.overflow = '-moz-scrollbars-vertical';
  textareaElement.style.overflowX = 'auto';

// 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';
 textareaElement.parentNode.insertBefore(inputWrapper, textareaElement);

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

// 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.1em; 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 += '<input class="customEdit" type="button" value="&crarr;" id="lastChangePos" onclick="javascript:Edit(\'lastchangepos\');" title="Jump to the last changed position">';
  html += '<span style="margin-left: 1em;"></span/>';
 html += '<input class="customEdit" type="button" value="&larr;" onclick="javascript:Undo();" title="Undo button clicks">';
 html += '<input class="customEdit" type="button" value="&rarr;" onclick="javascript:Redo();" title="Redo button clicks">';
 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 of current lines">';
 html += '<input class="customEdit" type="button" value="&rarr;==" onclick="javascript:Edit(\'headingmore\');" title="Increase heading level of current lines">';
 html += '<span style="margin-left: 0.5em;"></span/>';
 html += '<input class="customEdit" type="button" value="&prod;" id="scrollToTop" title="Scroll text area to window top">';
 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 occurrences 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 title="Find ahead as you type (non-regexp only)"><input class="customEdit" style="margin: 0 0.2em 0 0.5em;" type="checkbox" value="1" id="findAhead">Find ahead</span>';
 html += '<span title="Search should be case sensitive"><input class="customEdit" style="margin: 0 0.2em 0 0.3em;" type="checkbox" value="1" id="caseSensitive">Case</span>';
 html += '<span title="Search should be a regular expression"><input class="customEdit" style="margin: 0 0.2em 0 0.3em;" 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="&nbsp;|&nbsp;" onclick="javascript:Edit(\'pipes\');" title="Fix blanks around vertical bars">';
 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 += '<span style="margin-left: 0.5em;"></span/>';
 html += '<input class="customEdit" type="button" value="&mdash;" onclick="javascript:Edit(\'dashes\');" title="Fix dashes">';
 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>';
 html += '<input class="customEdit" type="button" value="Scroll up" id="scrollToTopBottom" title="Scroll text area to window top">';
 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() {
 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(textareaElement, 'width');
 normalTextareaHeight = getStyle(textareaElement, 'height');
 normalTextareaMargin = getStyle(textareaElement, 'margin');
 normalTextareaRows = textareaElement.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);
 document.captureEvents(Event.mouseover);
 document.captureEvents(Event.keyup);
 document.captureEvents(Event.keypress);

// fullscreen
 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 = '';
 };

// scroll to text area top
 var scrollToTop = document.getElementById('scrollToTop');
 var scrollToTopBottom = document.getElementById('scrollToTopBottom');
 scrollToTop.onmouseover = ScrollToTop;
 scrollToTop.onclick = ScrollToTop;
 scrollToTopBottom.onmouseover = ScrollToTop;
 scrollToTopBottom.onclick = ScrollToTop;

// find ahead
 var findText = document.getElementById('findText');
 findText.onkeyup = FindAhead;

// cursor memory, jump to last changed position
 textareaElement.onkeypress = KeyTextArea;
 textareaElement.onkeyup = KeyTextArea;
 textareaElement.onclick = ClickTextArea;

// submit buttons
 var saveButton = document.getElementById('wpSave');
 var previewButton = document.getElementById('wpPreview');
 var diffButton = document.getElementById('wpDiff');
 saveButton.onclick = function() { NormalScreen(); saveButton.onclick = null; saveButton.click(); };
 previewButton.onclick = function() { NormalScreen(); previewButton.onclick = null; previewButton.click(); };
 diffButton.onclick = function() { NormalScreen(); diffButton.onclick = null; diffButton.click(); };

// insert an invisible clone of the textarea for resizing
  var textareaClone = textareaElement.cloneNode(false);
  textareaClone.id = 'textareaClone';
  textareaClone.name = null;
  textareaClone.accesskey = null
  textareaClone.tabindex = null;
 textareaClone.style.position = 'relative';
  textareaClone.style.display = 'none';
 textareaClone.style.zIndex = '-5';
 textareaClone.style.height = '';
 textareaClone.rows = 1;
  textareaClone.style.overflow = 'scroll';
  textareaElement.parentNode.insertBefore(textareaClone, textareaElement.nextSibling);

// resize textarea and parse rows
 window.onresize = ResizeTextarea;
 ResizeTextarea();

// set textarea cursor to start
 textareaElement.setSelectionRange(0, 0);

// default checkboxes
  if (findAheadSelected) {
    document.getElementById('findAhead').checked = true;
  }

 return;
}


//
// FindAhead: find non-regexp text as you type, event handler for find field
//

function FindAhead() {

  if (document.getElementById('findAhead').checked) {
    if (!document.getElementById('regExp').checked) {

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

// get checkboxes
 var caseSensitive = document.getElementById('caseSensitive');
 var regExp = document.getElementById('regExp');

// replace special characters for regexp search
 var startPos = textareaElement.selectionStart;
 findText = findText.replace(/([\\^\$\*\+\?\.\(\)\[\]\{\}\:\=\!\|\,\-])/g, '\\$1');
 if ( ! caseSensitive.checked ) {
 regExpFlags = 'i';
 }
 if (findText != '') {

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

// set start position for search to right
 var indexStart;
 var result;
 indexStart = startPos;

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

// set the selection
 if (result != null) {

// set the selection range
 textareaElement.setSelectionRange(result.index, result.index + result[0].length);

// scroll the textarea to the selected text or cursor position
 ParseRows();
 if (textRows.selStartRow >= textRows.rows) {
   ScrollTextarea(textRows.selStartRow, textRows.selEndRow, 0, 0, 0);
 }
 else {
   ScrollTextarea(textRows.selStartRow, textRows.selEndRow, 0, 0, textareaElement.scrollHeight);
 }
 }
 }
 }
 }
 return;
}


//
// ClickTextArea: event handler for textarea clicks
//

function ClickTextArea(event) {

// reset cursor memory
  textRows.cursorMemory = null;
 return;
}


//
// KeyTextArea: event handler for textarea keypresses
//

function KeyTextArea(event) {

// 'jump to last change' function
 if (event.type == 'keyup') {

// left, right, up, down, page up, page down;
 switch (event.keyCode) {
 case 37: ; case 39: ; case 38: ; case 33: ; case 40: ; case 34: break;
 default:
 if (event.charCode != null) {
 lastChangePos = textareaElement.selectionStart;
 }
   }
  }

// cursor memory function
 else if (event.type == 'keypress') {

// check if cursor memory has been enabled
 if (cursorMemory != true) {
 return;
 }

// left, right
 if ( (event.keyCode == 37) || (event.keyCode == 39) ) {
 textRows.cursorMemory = null;
 }

// up, down, page up, page down; contains a workaround for a bug that misplaces cusor in empty lines
 else if ( (event.keyCode == 38) || (event.keyCode == 40) || (event.keyCode == 33) || (event.keyCode == 34) ) {
 ParseRows();
 var row = textRows.selStartRow;
 var col;
 if (textRows.cursorMemory != null) {
 col = textRows.cursorMemory;
 }
 else {
 col = textRows.selEnd - textRows.rowStart[row];
 textRows.cursorMemory = col;
 }
 var lines;

// up, down, page up, page down
 switch (event.keyCode) {
 case 38: lines = -1; break;
 case 33: lines = scrollMargin - textRows.rows; break;
 case 40: lines = 1; break;
 case 34: lines = textRows.rows - scrollMargin;
 }
 if ( ( (lines < 0) && (row > 0) ) || ( (lines > 0) && (row < textRows.rowTotal) ) ) {
 row = row + lines;
 if (row < 0) {
 row = 0;
 }
 else if (row > textRows.rowTotal) {
 row = textRows.rowTotal;
 }
 var pos;
 if (textRows.rowLength[row] >= col) {
 pos = textRows.rowStart[row] + col;
 if (!event.metaKey && !event.shiftKey && !event.ctrlKey) {
 textareaElement.setSelectionRange(pos, pos);
 event.preventDefault();
 }
 }
 else {
 pos = textRows.rowStart[row] + textRows.rowLength[row];
 }
 ScrollTextarea(textRows.selStartRow, textRows.selEndRow, lines, scrollMargin, textareaElement.scrollTop);
 }
 }
 else {
 textRows.changed = true;
 }
 }
 return;
}


//
// ScrollToTop: event handler for scroll to textarea top button
//

function ScrollToTop(event) {

 var scrollToTop = document.getElementById('scrollToTop');
 var scrollToTopBottom = document.getElementById('scrollToTopBottom');
 var textarea = document.getElementById('textareaWrapper');
 var buttons = document.getElementById('buttonsWrapper');
 var textareaTop = getOffsetTop(textarea);
 var buttonsTop = getOffsetTop(buttons);
 var offset = window.pageYOffset;

// click
 if (event.type == 'click') {
 if (offset == textareaTop) {
   window.scroll(0, buttonsTop);
 scrollToTop.title = "Scroll text area to window top";
 scrollToTopBottom.title = "Scroll text area to window top";
 }
 else {
 window.scroll(0, textareaTop);
 scrollToTop.title = "Scroll button area to window top";
 scrollToTopBottom.title = "Scroll button area to window top";
 }
 }

// mouseover
 else {
 if (offset == textareaTop) {
 scrollToTop.title = "Scroll button area to window top";
 scrollToTopBottom.title = "Scroll button area to window top";
 }
 else {
 scrollToTop.title = "Scroll text area to window top";
 scrollToTopBottom.title = "Scroll text area to window top";
 }
 }
 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 fullscreen 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';
 var content = document.getElementById('content');
 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
 textareaElement.style.margin = '0';

// set the textarea to maximal height
 var textareaWrapper = document.getElementById('textareaWrapper');
 textareaElement.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;

// set rows
 var textareaClone = document.getElementById('textareaClone');
 textareaClone.style.display = 'block';
  var rows = textareaElement.clientHeight / textareaClone.clientHeight * textareaClone.rows;
 textareaClone.style.display = 'none';
 textareaElement.rows = rows;

// resize textarea to defined cols number and parse rows
 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
 textareaElement.style.width = normalTextareaWidth;
 textareaElement.style.height = normalTextareaHeight;
 textareaElement.style.margin = normalTextareaMargin;
 textareaElement.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 properties for non-inline css definitions
function getStyle(element, styleProperty) {
 var style;
 if (element != null) {
 style = document.defaultView.getComputedStyle(element, null).getPropertyValue(styleProperty);
 }
 return(style);
}


//
// 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' : '');
}

//
// 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);
}
Personal tools