User:Dacheatbot/editor.js
From Homestar Runner Wiki
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(/—/g, '—'); text = text.replace(/–/g, '–'); text = text.replace(/−/g, '\u2212'); // convert -- and em dashes with or without spaces to em dash surrounded by spaces text = text.replace(/([a-zA-Z\'\"”\]\}\)]) *(--|—|—) *([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\'\"”\]\}])( | )+(\u2212|–|–) +([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) ?(--?|—|—) ?([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 ° into actual ° symbol text = text.replace(/°/g, '°'); // convert the word ohm(s) or the html entity into the actual O symbol (Omega, not the actual ohm symbol Ω) 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)? ?(Ω|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) ?(μ|µ|µ)(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(/−/g, '\u2212'); text = text.replace(/·/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 = 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="←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→" onclick="javascript:Edit(\'findnext\');" title="Find next">'; html += '<span style="margin-left: 0.5em;"></span/>'; html += '<input class="customEdit" type="button" value="↑↓" onclick="javascript:Edit(\'updown\');" title="Jump to the top / bottom">'; html += '<input class="customEdit" type="button" value="↵" 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="←" onclick="javascript:Undo();" title="Undo button clicks">'; html += '<input class="customEdit" type="button" value="→" 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→a" onclick="javascript:Edit(\'lowercase\');" title="Lowercase text">'; html += '<span style="margin-left: 0.5em;"></span/>'; html += '<input class="customEdit" type="button" value="=←" onclick="javascript:Edit(\'headingless\');" title="Decrease heading level of current lines">'; html += '<input class="customEdit" type="button" value="→==" 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="∏" 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="←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.→" 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=" | " onclick="javascript:Edit(\'pipes\');" title="Fix blanks around vertical bars">'; html += '<input class="customEdit" type="button" value="kΩ" onclick="javascript:Edit(\'units\');" title="Fix units">'; html += '<input class="customEdit" type="button" value="√" 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="—" 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); }