// xTextControl Demo: a simple texteditor for a textarea
// requires x.js, xtc.js
//
// Call WriteEditToolbarHTML( aTextFieldID, aPicDir ) to create the Toolbar.
//
// <aTextFieldID> is the ID of the textarea on which to apply the edit functions
// <aPicDir> is the relativ path to the directory with the toolbar button icons
//
// Url of a page that should be invoked with the help button
var CToolbarHelpPage = 'http://walter.bislins.ch/doku/xtc';
//----------------------------------------------------------
function w() { for (var i = 0; i < w.arguments.length; i++) document.write(w.arguments[i]); }
function wl() { for (var i = 0; i < wl.arguments.length; i++) document.write(wl.arguments[i]); document.writeln('');}
function UrlEncode(s) {
var ss = s;
ss = escape(ss);
ss = ss.replace(/\//g,'%2F');
ss = ss.replace(/\+/g,'%2B');
ss = ss.replace(/\*/g,'%2A');
ss = ss.replace(/@/g,'%40');
ss = ss.replace(/%20/g,'+');
return ss;
}
var ToolbarActivated = false;
var ShiftPressed = false;
var SavedText = '';
var SavedCursor = 0;
function OnEditKeyDown( aEvent, aTextFieldID ) {
var ev = new xEvent(aEvent);
var key = String.fromCharCode(ev.keyCode);
if (ToolbarActivated) {
if (key == '1') DoHeadline(aTextFieldID, 1);
if (key == '2') DoHeadline(aTextFieldID, 2);
if (key == '3') DoHeadline(aTextFieldID, 3);
if (key == 'B') DoBold(aTextFieldID);
if (key == 'I') DoItalic(aTextFieldID);
if (key == 'W') DoLink(aTextFieldID);
if (key == 'X') DoExtlink(aTextFieldID);
if (key == 'E') DoMail(aTextFieldID);
if (key == 'R') DoRedir(aTextFieldID);
if (key == 'P') DoImage(aTextFieldID);
if (key == 'M') DoMath(aTextFieldID);
if (ev.keyCode == 189 || ev.keyCode == 109) DoHr(aTextFieldID);
if (ev.keyCode == 190) DoBreak(aTextFieldID);
if (key == 'L') DoUList(aTextFieldID);
if (key == 'N') DoOList(aTextFieldID);
if (key == 'Z') DoIndent(aTextFieldID);
if (key == 'U') DoUnindent(aTextFieldID);
if (key == 'V') DoInsertCB(aTextFieldID);
if (key == 'T') DoTable(aTextFieldID);
if (key == 'H') DoTableHead(aTextFieldID);
if (key == 'C') DoTT(aTextFieldID);
if (key == 'Q') DoQuot(aTextFieldID);
if (key == 'A') Autoformat(aTextFieldID);
if (ev.keyCode == 16) ShiftPressed = !ShiftPressed;
// alert( 'keyCode = ' + ev.keyCode + '\nkey = ' + key );
// ignore pressing of shift, control key
if (ev.keyCode != 16 && ev.keyCode != 17) {
ToolbarActivated = false;
ShiftPressed = false;
ShowKeyState( aTextFieldID );
}
return false;
}
// Ctrl-Z: save current cursor position and let text field do the undo
if ((key == 'Z') && ev.ctrlKey && !ev.altKey && !ev.shiftKey ) {
ResetCursorAfterUndo(aTextFieldID);
return true;
}
// Crtl-W or Ctrl-Space for Wiki Edit Commands (prevents closing of window with Ctrl-W!)
if ((key == 'W' || key == ' ') && ev.ctrlKey && !ev.altKey && !ev.shiftKey ) {
ToolbarActivated = true;
ShiftPressed = false;
ShowKeyState(aTextFieldID);
return false;
}
// Ctrl-Y: CutLines
if ((key == 'Y') && ev.ctrlKey && !ev.altKey && !ev.shiftKey ) {
CutLines(aTextFieldID);
return false;
}
// Ctrl-DEL: DelWord
if ((ev.keyCode == 46) && ev.ctrlKey && !ev.altKey && !ev.shiftKey ) {
DelWord(aTextFieldID);
return false;
}
// disable ESC to prevent accidental loss of text
if (ev.keyCode == 27) {
return false;
}
return true;
}
function ShowKeyState( aTextFieldID ) {
var color = ToolbarActivated ? '#000000' : '#B4B4B4';
xStyle( aTextFieldID+'Toolbar', 'borderColor', color);
}
//---------------------------------------------------------------
// Edit Helper Functions
function Trim( aText ) {
var s = aText.replace( /[\t\n]/g, ' ' );
s = s.replace( /^[ ]*(.*?)[ ]*$/, '$1' );
return s;
}
function RTrim( aText ) {
var s = aText.replace( /[\t\n]/g, ' ' );
s = s.replace( /(.*?)[ ]*$/, '$1' );
return s;
}
var CBlankPattern = /[ \t]/;
var CBlankOrNlPattern = /[ \t\n]/;
var CIBPattern = /[ \t\n\']/;
var CWordCharPattern = /[\w\d_äöüÄÖÜßçàáâèéêôûÁÀÂÉÈÊÔÛ]/;
var CNotWordCharPattern = /[^\w\d_äöüÄÖÜßçàáâèéêôûÁÀÂÉÈÊÔÛ \t\n]/;
var CUrlCharPattern = /[\w\d_+\-$@&\.!\*\"\'\(\),=;\/#\?:]/;
function IsBlank( aChar ) {
return IsCharPattern( aChar, CBlankPattern );
}
function IsBlankOrNl( aChar ) {
return IsCharPattern( aChar, CBlankOrNlPattern );
}
function IsCharPattern( aChar, aCharPattern ) {
return aCharPattern.test(aChar);
}
function GetPatternStartPos( aCharPattern, aText, aPos ) {
// finds also start of pattern at aPos-1
// returns aPos if no pattern found
var p = aPos - 1;
while (p >= 0 && aCharPattern.test(aText.charAt(p))) p--;
return p + 1;
}
function GetPatternEndPos( aCharPattern, aText, aPos ) {
// returnes aPos if no pattern found at aPos
var pos = aPos;
var len = aText.length;
while (pos < len && aCharPattern.test(aText.charAt(pos))) pos++;
return pos;
}
function GetPatternPos( aCharPattern, aText, aPos ) {
// if no pattern is found then { StartPos=aPos, EndPos=aPos } is returned
var pos = { StartPos:0, EndPos:0 };
pos.StartPos = GetPatternStartPos( aCharPattern, aText, aPos );
pos.EndPos = GetPatternEndPos( aCharPattern, aText, aPos );
return pos;
}
function IsEmptyLine( aLine ) {
// returns true if aLine contains only blanks, tabs and NL or is empty
return (aLine.replace( /[ \t\n]/g, '' ).length == 0);
}
function IsInEmptyLine( aText, aPos ) {
// require( aPos >= 0 && aPos <= aText.length );
var line = GetLinePos( aText, aPos );
var s = aText.substring( line.StartPos, line.EndPos );
return IsEmptyLine(s);
}
function GetLinePos( aText, aPos ) {
// returns start and end position of line at aPos
// EndPos is allways behind NL of the line at aPos
var line = { StartPos:0, EndPos:0 };
line.StartPos = GetLineStartPos( aText, aPos );
line.EndPos = GetLineEndPos( aText, aPos );
return line;
}
function SplitLines( aText ) {
var text = aText;
var len = text.length;
if (len > 0 && text.charAt(len-1) == '\n') text = text.substring( 0, len-1 );
return text.split( '\n' );
}
function JoinLines( aLines ) {
return aLines.join( '\n' ) + '\n';
}
function GetLineStartPos( aText, aPos ) {
// require( aPos >= 0 && aPos <= aText.length );
return (aPos > 0) ? aText.lastIndexOf( '\n', aPos-1 ) + 1 : 0;
}
function GetLineLastPos( aText, aPos ) {
// returns pos of last char of line (the NL, if exists)
// require( aPos >= 0 && aPos <= aText.length );
var nlPos = aText.indexOf( '\n', aPos );
if (nlPos == -1) nlPos = aText.length;
return nlPos;
}
function GetLineEndPos( aText, aPos ) {
// returns end pos if line = start pos of next line
// require( aPos >= 0 && aPos <= aText.length );
var nlPos = aText.indexOf( '\n', aPos ) + 1;
if (nlPos == 0) nlPos = aText.length;
return nlPos;
}
function GetLineText( aText, aPos ) {
// returns line inclusiv endling NL (if exists)
var line = GetLinePos( aText, aPos );
return aText.substring( line.StartPos, line.EndPos );
}
function GetTagContentStartPos( aText, aStartTag, aEndTag, aPos ) {
var startTagPos = aText.lastIndexOf( aStartTag, aPos-aStartTag.length );
if (startTagPos == -1) return -1;
var nlPos = aText.lastIndexOf( '\n', aPos-1 );
if (nlPos > startTagPos) return -1;
var startTagContentPos = startTagPos + aStartTag.length;
var endTagPos = aText.lastIndexOf( aEndTag, aPos-aEndTag.length );
if (endTagPos == -1) return startTagContentPos;
if (startTagPos < endTagPos) return -1;
return startTagContentPos;
}
function GetTagContentEndPos( aText, aStartTag, aEndTag, aPos ) {
var endTagPos = aText.indexOf( aEndTag, aPos );
if (endTagPos == -1) return -1;
var nlPos = aText.indexOf( '\n', aPos );
if (nlPos >= 0 && nlPos < endTagPos) return -1;
var startTagPos = aText.indexOf( aStartTag, aPos );
if (startTagPos == -1) return endTagPos;
if (startTagPos < endTagPos) return -1;
return endTagPos;
}
function GetTagContentPos( aText, aStartTag, aEndTag, aSelStart, aSelEnd ) {
// if aPos is not between aStartTag and aEndTag then
// returns StartPos == EndPos == aSelStart
var pos = { StartPos:aSelStart, EndPos:aSelStart };
var startPos = GetTagContentStartPos( aText, aStartTag, aEndTag, aSelStart );
var endPos = GetTagContentEndPos( aText, aStartTag, aEndTag, aSelEnd );
if (startPos == -1 || endPos == -1) return pos;
pos.StartPos = startPos;
pos.EndPos = endPos;
return pos;
}
function FindBlockStart( aText, aPos ) {
// require( aPos >= 0 && aPos <= aText.length );
// returns aText.length if no block is found
var p = aPos;
var len = aText.length;
// find next not empty line to the right
while (IsInEmptyLine(aText,p)) {
p++;
if (p >= len) return len;
}
// p is not in empty line, find first empty line to the left
do {
p = GetLineStartPos( aText, p );
if (p <= 0) return 0;
p--;
}
while (!IsInEmptyLine(aText,p));
return p+1;
}
function FindBlockEnd( aText, aPos ) {
// require( aPos >= 0 && aPos <= aText.length );
// returns start pos of first empty line after block of not empty lines
// returns 0 if no block is found
var p = aPos;
var len = aText.length;
// find first not empty line to the left
while (p >= 0 && IsInEmptyLine(aText,p)) {
p--;
}
if (p < 0) return 0;
// p is not in empty line, find first empty line to the right
do {
p = GetLineEndPos( aText, p );
if (p >= len) return len;
}
while (!IsInEmptyLine(aText,p));
return p;
}
function FindBlockStartTag( aText, aTag, aPos ) {
var p = aPos;
while (p >= 0) {
p = GetLineStartPos( aText, p );
var line = RTrim(GetLineText( aText, p ));
if (IsEmptyLine(line)) return -1;
if (line == aTag) return p;
p--;
}
return -1;
}
function FindBlockEndTag( aText, aTag, aPos ) {
var p = aPos;
var len = aText.length;
while (p <= len) {
p = GetLineLastPos( aText, p );
var line = RTrim(GetLineText( aText, p ));
if (IsEmptyLine(line)) return -1;
if (line == aTag) return GetLineStartPos( aText, p );
p++;
}
return -1;
}
function ExpandSelectionToPattern( aTextControl, aCharPattern ) {
var pos = GetPatternPos( aCharPattern, aTextControl.Text, aTextControl.SelStart );
if (pos.StartPos < pos.EndPos) {
aTextControl.SetSelectionRange( pos.StartPos, pos.EndPos, false );
}
}
function ExpandSelectionToLines( aTextControl ) {
var text = aTextControl.Text;
var startPos = GetLineStartPos( text, aTextControl.SelStart );
// if selection not empty and endPos is on start of line, do not select this line
var endPos = GetLineStartPos( text, aTextControl.SelEnd );
if (aTextControl.SelStart == aTextControl.SelEnd || endPos != aTextControl.SelEnd) {
endPos = GetLineEndPos( text, aTextControl.SelEnd );
}
aTextControl.SetSelectionRange( startPos, endPos, false );
}
function ExpandSelectionToBlocks( aTextControl ) {
// returns false, if selection is outside any block
var text = aTextControl.Text;
var blockStartPos = FindBlockStart( text, aTextControl.SelStart );
// if selection is not empty then adjust end pos to ensure only full selected blocks are returned
var selEndPos = aTextControl.SelEnd;
if (selEndPos > aTextControl.SelStart) selEndPos--;
var blockEndPos = FindBlockEnd( text, selEndPos );
// if no blocks are found
if (blockStartPos >= blockEndPos) return false;
aTextControl.SetSelectionRange( blockStartPos, blockEndPos, false );
return true;
}
//---------------------------------------------------------------
// Edit Functions
function ResetCursorAfterUndo(aTextFieldID) {
// save text of text field and let the undo function do his work
// after some time check the difference and set the cursor back to the start of the change
// or the last saved cursor position, if no changes are found
var tc = new xTextControl(xGet(aTextFieldID));
SavedText = tc.Text;
SavedCursor = tc.SelStart;
setTimeout( function(){ DoResetCursor(aTextFieldID); }, 100 );
}
function DoResetCursor(aTextFieldID) {
var pos;
var tc = new xTextControl(xGet(aTextFieldID));
if (tc.Text == SavedText) {
pos = SavedCursor;
} else {
pos = GetPosOfFirstDiff( tc.Text, SavedText );
}
tc.SetCaretPos( pos );
}
function GetPosOfFirstDiff( t1, t2 ) {
var pos = 0;
var l1 = t1.length;
var l2 = t2.length;
while (pos < l1 && pos < l2) {
if (t1.charAt(pos) != t2.charAt(pos)) return pos;
pos++;
}
return pos;
}
function SelectPattern( aTextFieldID, aCharPattern ) {
var tc = new xTextControl(xGet(aTextFieldID));
ExpandSelectionToPattern( tc, aCharPattern );
tc.UpdateControlSelection();
}
function SelectLines( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
ExpandSelectionToLines( tc );
tc.UpdateControlSelection();
}
function SelectBlock( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
if (ExpandSelectionToBlocks(tc)) {
tc.UpdateControlSelection();
}
}
function ToggleBlockTags( aTextFieldID, aStartTag, aText, aEndTag ) {
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
// check, if tags allready exists
var tagsFound = false;
var emptyBlock = false;
var blockStartTagPos = FindBlockStartTag( text, aStartTag, tc.SelStart );
var blockEndTagPos = -1;
if (blockStartTagPos >= 0) {
blockEndTagPos = FindBlockEndTag( text, aEndTag, tc.SelEnd );
if (blockEndTagPos >= 0) tagsFound = true;
}
if (!tagsFound) {
if (ExpandSelectionToBlocks(tc)) {
var firstLine = RTrim(GetLineText( text, tc.SelStart ));
var lastLine = RTrim(GetLineText( text, tc.SelEnd-1 ));
if (firstLine == aStartTag && lastLine == aEndTag) {
blockStartTagPos = tc.SelStart;
blockEndTagPos = GetLineStartPos( text, tc.SelEnd-1 );
tagsFound = true;
}
}
else {
// selection is not inside a block, expand it to whole empty lines
emptyBlock = true;
var lineStartPos = GetLineStartPos( text, tc.SelStart );
var lineEndPos = GetLineEndPos( text, tc.SelEnd );
if (lineEndPos > text.length) lineEndPos = text.length;
tc.SetSelectionRange( lineStartPos, lineEndPos, false );
}
}
if (tagsFound) {
// tags allready exists, remove them
var keepStartPos = GetLineEndPos( text, blockStartTagPos );
var keepText = text.substring( keepStartPos, blockEndTagPos );
var selEndPos = GetLineEndPos( text, blockEndTagPos );
tc.ChangeSelection( blockStartTagPos, selEndPos, keepText, true );
return;
}
// no such tags in current blocks
// block must have a ending '\n'
var newText = tc.SelText
var newSelStart = tc.SelStart;
var newSelEnd = newText.length;
if (newText.length > 0 && newText.charAt(newText.length-1) != '\n') {
newText += '\n';
newSelEnd++;
}
// insert tags and select block
if (emptyBlock) {
// insert a blank block
newText = aText + '\n';
newSelEnd = newText.length;
}
newText = aStartTag + '\n' + newText + aEndTag + '\n';
newSelStart += aStartTag.length + 1;
// insert blank lines around block
if (tc.SelStart > 0) {
if (!IsInEmptyLine( text, tc.SelStart-1 )) {
newText = '\n' + newText;
newSelStart++;
}
}
if (tc.SelEnd <= text.length) {
if (!IsInEmptyLine( text, tc.SelEnd )) {
newText += '\n';
}
}
newSelEnd += newSelStart;
tc.ChangeSelection( tc.SelStart, tc.SelEnd, newText, false );
tc.SetSelectionRange( newSelStart, newSelEnd, true );
}
function InsertBlock( aTextFieldID, aStartTag, aText, aEndTag, bNlOnTop ) {
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
var newSelStart = GetLineStartPos( text, tc.SelStart );
var selStart = newSelStart;
var selEnd = newSelStart;
var newText = aStartTag + aText + aEndTag + '\n';
newSelStart += aStartTag.length;
// insert blank lines
if (bNlOnTop && selStart > 0) {
if (!IsInEmptyLine( text, selStart-1 )) {
newText = '\n' + newText;
newSelStart++;
}
}
if (selEnd <= text.length) {
if (!IsInEmptyLine( text, selEnd )) {
newText += '\n';
}
}
var newSelEnd = newSelStart + aText.length;
tc.ChangeSelection( selStart, selEnd, newText, false );
tc.SetSelectionRange( newSelStart, newSelEnd, true );
}
function ToggleLinkTags( aTextFieldID, aStartTag, aLink, aSep, aText, aEndTag, aCharPattern ) {
if (aCharPattern == '') aCharPattern = CWordCharPattern;
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
// check if selection is inside existing tags, but not if starttag = endtag and selection is not empty
var tagContent = GetTagContentPos( text, aStartTag, aEndTag, tc.SelStart, tc.SelEnd );
if (aStartTag == aEndTag) {
if (tc.SelStart == tc.SelEnd) {
var patSel = GetPatternPos( aCharPattern, text, tc.SelStart );
if (patSel.StartPos < patSel.EndPos) {
tc.SetSelectionRange( patSel.StartPos, patSel.EndPos, false );
}
}
if (tc.SelStart < tc.SelEnd && (tc.SelStart != tagContent.StartPos || tc.SelEnd != tagContent.EndPos)) {
// not my tags
tagContent.EndPos = tagContent.StartPos = tc.SelStart;
}
}
if (tagContent.StartPos == tagContent.EndPos) {
// not in tag
if (tc.SelStart == tc.SelEnd) {
// nothing selected, check if pattern to select is nearby
var patSel = GetPatternPos( aCharPattern, text, tc.SelStart );
if (patSel.StartPos == patSel.EndPos) {
// no pattern to select, so insert tags and aLink
var newText = aStartTag + aLink + aEndTag;
tc.ChangeSelection( tc.SelStart, tc.SelEnd, newText, false );
var newSelStart = tc.SelStart + aStartTag.length;
var newSelEnd = tc.SelEnd - aEndTag.length;
tc.SetSelectionRange( newSelStart, newSelEnd, true );
return;
}
// pattern to select found, select it
tc.SetSelectionRange( patSel.StartPos, patSel.EndPos, false );
}
// pattern selected, so insert tags
// leerzeichen am Ende der selektion ausserhalb des End-Tags platzieren!
var selStart = tc.SelStart;
var selEnd = tc.SelEnd;
while (selEnd > selStart && IsBlankOrNl(text.charAt(selEnd-1))) selEnd--;
if (selEnd <= selStart) selEnd = tc.SelEnd;
// tags einfügen
var newText = aStartTag + text.substring( selStart, selEnd ) + aEndTag;
tc.ChangeSelection( selStart, selEnd, newText, false );
selStart = tc.SelStart + aStartTag.length;
selEnd = tc.SelEnd - aEndTag.length;
tc.SetSelectionRange( selStart, selEnd, true );
return;
}
// selection is inside existing tag, check if aSep is inside tags
var sepPos = -1;
if (aSep != '') {
sepPos = text.indexOf( aSep, tagContent.StartPos );
if (sepPos >= tagContent.EndPos) sepPos = -1;
}
var newText = '';
if (sepPos >= 0) {
// aSep found between tags, remove tags and keep text on selected side of aSep
if (tc.SelStart <= sepPos) {
// keep left side of aSep
newText = text.substring( tagContent.StartPos, sepPos );
}
else {
// keep right side of aSep
newText = text.substring( sepPos+aSep.length, tagContent.EndPos );
}
var selStart = tagContent.StartPos - aStartTag.length;
var selEnd = tagContent.EndPos + aEndTag.length;
tc.ChangeSelection( selStart, selEnd, newText, true );
return;
}
// selection inside existing tags but no separator found
if (aSep == '') {
// remove tags and select contents
var newText = text.substring( tagContent.StartPos, tagContent.EndPos );
var selStart = tagContent.StartPos - aStartTag.length;
var selEnd = tagContent.EndPos + aEndTag.length;
tc.ChangeSelection( selStart, selEnd, newText, true );
return;
}
// insert separator and copy and select existing content
if (aText != '') {
newText = aSep + aText;
} else {
newText = aSep + text.substring( tagContent.StartPos, tagContent.EndPos );
}
var selStart = tagContent.EndPos;
var selEnd = selStart;
tc.ChangeSelection( selStart, selEnd, newText, false );
selStart = tc.SelStart + aSep.length;
selEnd = tc.SelEnd;
tc.SetSelectionRange( selStart, selEnd, true );
}
var CTagTypeItalic = 0;
var CTagTypeBold = 1;
function ToggleIBTags( aTextFieldID, aTagType ) {
// aTagType = CTagTypeItalic | CTagTypeBold
var ITag = '\'\'';
var BTag = '\'\'\'';
var IBTag = ITag + BTag;
var currentTag = (aTagType == CTagTypeItalic) ? ITag : BTag;
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
// reduce selection to ensure that tags are outside
if (tc.SelStart < tc.SelEnd) {
var startPos = tc.SelStart;
var endPos = tc.SelEnd;
while (endPos > startPos && IsCharPattern(text.charAt(endPos-1),CIBPattern)) endPos--;
while (startPos < endPos && IsCharPattern(text.charAt(startPos),CIBPattern)) startPos++;
if (startPos < endPos ) {
tc.SetSelectionRange( startPos, endPos, false );
}
}
// check if selection is inside existing tags, but not if starttag = endtag and selection is not empty
var tagContentIB = GetTagContentPos( text, IBTag, IBTag, tc.SelStart, tc.SelEnd );
var tagContentB = GetTagContentPos( text, BTag, BTag, tc.SelStart, tc.SelEnd );
var tagContentI = GetTagContentPos( text, ITag, ITag, tc.SelStart, tc.SelEnd );
var isIB = (tagContentIB.StartPos < tagContentIB.EndPos);
var isB = (tagContentB.StartPos < tagContentB.EndPos);
var isI = (tagContentI.StartPos < tagContentI.EndPos);
if (!isIB && isB && isI) {
// only the nearer tag is the relevant one
if (tagContentB.StartPos >= tagContentI.StartPos && tagContentB.EndPos <= tagContentI.EndPos) {
isI = false;
} else {
isB = false;
}
}
if (!isIB && isB) isI = false;
if (tc.SelStart == tc.SelEnd) {
ExpandSelectionToPattern( tc, CWordCharPattern )
}
var isInTag = false;
var cStartPos = 0;
var cEndPos = 0;
if (tc.SelStart < tc.SelEnd) {
// somthing is selected
if (isIB && tc.SelStart == tagContentIB.StartPos && tc.SelEnd == tagContentIB.EndPos) isInTag = true;
if (aTagType == CTagTypeItalic) {
if (isI && tc.SelStart == tagContentI.StartPos && tc.SelEnd == tagContentI.EndPos) {
isInTag = true;
cStartPos = tagContentI.StartPos;
cEndPos = tagContentI.EndPos;
}
} else {
if (isB && tc.SelStart == tagContentB.StartPos && tc.SelEnd == tagContentB.EndPos) {
isInTag = true;
cStartPos = tagContentB.StartPos;
cEndPos = tagContentB.EndPos;
}
}
}
if (!isInTag) {
// not in tag
if (tc.SelStart == tc.SelEnd) {
// nothing selected, so insert template
var newText = currentTag + 'Text' + currentTag;
tc.ChangeSelection( tc.SelStart, tc.SelEnd, newText, false );
var selStart = tc.SelStart + currentTag.length;
var selEnd = selStart + 4;
tc.SetSelectionRange( selStart, selEnd, true );
return;
}
// pattern selected, so insert tags
// leerzeichen am Ende der selektion ausserhalb des End-Tags platzieren!
var selStart = tc.SelStart;
var selEnd = tc.SelEnd;
while (selEnd > tc.SelStart && IsBlankOrNl(text.charAt(selEnd-1))) selEnd--;
if (selEnd <= selStart) selEnd = tc.SelEnd;
// tags einfügen
var newText = currentTag + text.substring( selStart, selEnd ) + currentTag;
tc.ChangeSelection( selStart, selEnd, newText, false );
selStart = tc.SelStart + currentTag.length;
selEnd = tc.SelEnd - currentTag.length;
tc.SetSelectionRange( selStart, selEnd, true );
return;
}
// selection is inside existing tag, so remove tags and select contents
var newText = text.substring( cStartPos, cEndPos );
var selStart = cStartPos - currentTag.length;
var selEnd = cEndPos + currentTag.length;
tc.ChangeSelection( selStart, selEnd, newText, true );
}
function ToggleInlineTags( aTextFieldID, aStartTag, aDefaultText, aEndTag ) {
ToggleLinkTags( aTextFieldID, aStartTag, aDefaultText, '', '', aEndTag, CWordCharPattern );
}
function InsertText( aTextFieldID, aStr ) {
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
var selStart = tc.SelStart;
var selEnd = tc.SelEnd;
if (selStart < selEnd) {
// selektion trimmen, sodass nur ganze Worte markiert sind
while (selEnd > selStart && IsBlankOrNl(text.charAt(selEnd-1))) selEnd--;
if (selEnd <= selStart) selEnd = tc.SelEnd;
}
tc.ChangeSelection( selStart, selEnd, aStr, true );
}
function MovePrevLine( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
var cPos = tc.SelStart;
var nPos = GetLineStartPos( text, cPos );
if (nPos > 0) nPos = GetLineStartPos( text, nPos-1 );
tc.SetCaretPos( nPos );
}
function DelWord( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
if (tc.SelStart < tc.SelEnd) {
// if selection is not empty delete selection
tc.ChangeSelectionText( '', true );
return;
}
var text = tc.Text;
if (tc.SelStart == text.length) return;
var c = text.charAt( tc.SelStart );
var selEnd = tc.SelEnd;
if (c == '\n') {
// delete end of line
selEnd++;
}
else if (IsCharPattern( c, CWordCharPattern )) {
// delete word and trailing blanks
selEnd = GetPatternEndPos( CWordCharPattern, text, selEnd );
if (selEnd < text.length && IsCharPattern(text.charAt(selEnd),CBlankPattern)) {
// find end of blanks
selEnd = GetPatternEndPos( CBlankPattern, text, selEnd );
}
}
else if (IsCharPattern( c, CBlankPattern )) {
// delete blanks
selEnd = GetPatternEndPos( CBlankPattern, text, selEnd );
}
else {
// delete special chars and trailing blanks
selEnd = GetPatternEndPos( CNotWordCharPattern, text, selEnd );
if (selEnd < text.length && IsCharPattern(text.charAt(selEnd),CBlankPattern)) {
// find end of blanks
selEnd = GetPatternEndPos( CBlankPattern, text, selEnd );
}
}
tc.ChangeSelection( tc.SelStart, selEnd, '', true );
}
function CutLines( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
ExpandSelectionToLines( tc );
var isAtEnd = (tc.Text.length == tc.SelEnd);
tc.ChangeSelection( tc.SelStart, tc.SelEnd, '', true );
if (isAtEnd) MovePrevLine(aTextFieldID);
}
function MakeHeader( aTextFieldID, aLevel, sampleText ) {
var s = '=';
for (var i = 0; i < aLevel; i++) s += '=';
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
var line = GetLinePos( text, tc.SelStart );
var header = RTrim( text.substring( line.StartPos, line.EndPos ) );
// check Wiki header level
var hl = header.replace(/^(=*).*/, '$1').length - 1;
if (header == '') {
header = sampleText;
} else {
// strip Wiki header code
header = header.replace( /^[= ]*(.*?)[= ]*$/, '$1' );
}
if (hl == aLevel) {
// if header is allready this level make a paragraph
var newText = header + '\n';
tc.ChangeSelection( line.StartPos, line.EndPos, newText, true );
return;
}
// make header
var newText = s + ' ' + header + ' ' + s + '\n';
var newStart = line.StartPos + aLevel + 2;
var newEnd = newStart + header.length;
// add empty lines
if (line.StartPos > 0 && !IsInEmptyLine(text,line.StartPos-1)) {
newText = '\n' + newText;
newStart++; newEnd++;
}
if (line.EndPos >= text.length || !IsInEmptyLine(text,line.EndPos)) {
newText += '\n';
}
tc.ChangeSelection( line.StartPos, line.EndPos, newText, false );
tc.SetSelectionRange( newStart, newEnd, true );
}
function Indent( aTextFieldID, aChar ) {
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
// check cursor on empty line
if (tc.SelStart == tc.SelEnd && IsInEmptyLine(text,tc.SelStart)) {
var selStart = GetLineStartPos( text, tc.SelStart );
var selEnd = GetLineLastPos( text, tc.SelEnd );
var newText = aChar + ' text';
tc.ChangeSelection( selStart, selEnd, newText, false );
selEnd = tc.SelStart + newText.length;
selStart = tc.SelStart + aChar.length + 1;
tc.SetSelectionRange( selStart, selEnd, true );
return;
}
ExpandSelectionToLines( tc );
var lines = SplitLines( tc.SelText );
var replStr = '$1' + aChar + ' ';
for (i = 0; i < lines.length; i++) {
if (!IsEmptyLine(lines[i])) {
lines[i] = lines[i].replace( /(^[\*#:]*)[ \t]*/, replStr );
}
}
var newText = JoinLines( lines );
tc.ChangeSelectionText( newText, true );
}
function Unindent( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
ExpandSelectionToLines( tc );
var lines = SplitLines( tc.SelText );
for (i = 0; i < lines.length; i++) {
var line = lines[i].replace( /(^[\*#:]*?)[\*#:]([^\*#:])/, '$1$2' );
lines[i] = line.replace( /(^[ ]*)/, '' );
}
var newText = JoinLines( lines );
tc.ChangeSelectionText( newText, true );
}
function ParseTextTable( aText ) {
var s = aText.replace( /\"\"/g, '\001' );
var l = s.split( '"' );
for (var i = 1; i < l.length; i += 2) {
var s = l[i].replace( /;/g, '\002' );
l[i] = s.replace( /\n/g, '\003' );
}
s = l.join('');
var rows = s.split( '\n' );
var tab = new Array();
for (var row = 0; row < rows.length; row++ ) {
s = rows[row];
if (!IsEmptyLine(s)) {
l = s.split(';');
for (var col = 0; col < l.length; col++ ) {
s = Trim(l[col]);
s = s.replace( /\003/g, '\n' );
s = s.replace( /\002/g, ';' );
s = s.replace( /\001/g, '"' );
l[col] = s;
}
tab.push( l );
}
}
return tab;
}
function DoTable( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
// check cursor on empty line
if (!ExpandSelectionToBlocks(tc)) {
InsertBlock( aTextFieldID, '{| ', 'grid', '\n! \n! \n|-\n| \n| \n|}', true );
return;
}
// parse table
var tab = ParseTextTable( tc.SelText );
// make wiki table
var s = '{| grid\n';
for (var i = 0; i < tab.length; i++) {
var row = tab[i];
if (i > 0) s += '|-\n';
for (var j = 0; j < row.length; j++) {
s += '| ' + row[j] + '\n';
}
}
s += '|}\n';
tc.ChangeSelectionText( s, true );
}
function DoTableHead( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
var text = tc.Text;
// check cursor on empty line
if (tc.SelStart == tc.SelEnd && IsInEmptyLine(text,tc.SelStart)) return;
ExpandSelectionToLines( tc );
var lines = SplitLines( tc.SelText );
for (var i = 0; i < lines.length; i++) {
var s = lines[i];
if (s.length > 0) {
if (s.charAt(0) == '|') {
if (s.length > 1 && s.charAt(1) != '-' && s.charAt(1) != '}') {
s = '!' + s.substring(1);
lines[i] = s;
}
}
else if (s.charAt(0) == '!') {
s = '|' + s.substring(1);
lines[i] = s;
}
}
}
var newText = JoinLines( lines );
tc.ChangeSelectionText( newText, true );
}
function Autoformat( aTextFieldID ) {
var tc = new xTextControl(xGet(aTextFieldID));
ExpandSelectionToLines( tc );
var txt = '\n' + tc.SelText;
txt = txt.replace(/ß/g, 'ss');
// remove trailing blanks
txt = txt.replace(/\t/g, ' ');
txt = txt.replace(/[ ]+\n/g, '\n');
// remove hyphenations (Trennzeichen)
txt = txt.replace(/([a-zöäü])\-([a-zöäü])/g, '$1$2');
// make lists
txt = txt.replace(/\n *[^a-zA-Z0-9äöüÄÖÜ \'\"\n\{\|=#;\[«<(\/%]+? */g, '\n* ');
txt = txt.replace(/\n *\d+\. *([^\n])/g, '\n# $1');
// insert blank line before list
txt = txt.replace(/(\n[\*#] [^\n])/g, '\n$1');
// remove returns in paragraphs
txt = txt.replace(/([^\n])\n([^\n])/g, '$1 $2');
txt = txt.replace(/ \/\/\/ /g, '<br>\n');
// remove blank lines in lists
txt = txt.replace(/\n([\*#])/g, '$1');
txt = txt.replace(/(\n[^\*#\n].+\n)([\*#])/g, '$1\n$2');
// make separated lines without ., <, ' and : to header
txt = txt.replace(/(\n)([^\*=\n\.:\<\']+)(\n)/g, '$1== $2 ==$3');
txt = txt.replace(/<br>/g, ' ///');
// remove inserted nl at begin
txt = txt.substring(1);
tc.ChangeSelectionText( txt, true );
}
// -------------------------------------------------------------------------------------
function DoBlockFormat( aSelectControl, aTextFieldID ) {
var wert = aSelectControl.options[aSelectControl.options.selectedIndex].value;
if (wert == 'intro') ToggleBlockTags( aTextFieldID, '<intro>', 'Text', '</intro>' );
if (wert == 'bigdir') ToggleBlockTags( aTextFieldID, '{{bigdir}}', '* [[]]', '{{end bigdir}}' );
if (wert == 'poem') ToggleBlockTags( aTextFieldID, '<poem>', 'Text', '</poem>' );
if (wert == 'comment') ToggleBlockTags( aTextFieldID, '<comment>', 'Text', '</comment>' );
if (wert == 'code') ToggleBlockTags( aTextFieldID, '<code>', 'Text', '</code>' );
if (wert == 'hidden') ToggleBlockTags( aTextFieldID, '<hidden>', 'Text', '</hidden>' );
if (wert == 'html') ToggleBlockTags( aTextFieldID, '<html>', 'Text', '</html>' );
if (wert == 'div') ToggleBlockTags( aTextFieldID, '{{div|$style}}', 'Text', '{{end div}}' );
if (wert == 'data') ToggleBlockTags( aTextFieldID, '{{data}}', 'Text', '{{end data}}' );
if (wert == 'keywords') InsertBlock( aTextFieldID, '#KEYWORDS ', 'Keyword1, Keyword2, ...', '', false );
if (wert == 'include') InsertBlock( aTextFieldID, '{{include|', 'Page#Part', '}}', false );
if (wert == 'style') InsertBlock( aTextFieldID, '{{style}}\n#wiki .', 'class', ' { }\n{{end style}}', true );
if (wert == 'script') InsertBlock( aTextFieldID, '{{script}}\n', 'javascript();', '\n{{end script}}', true );
if (wert == 'macrodef') InsertBlock( aTextFieldID, '{{*', 'Name', '|Text*}}', true );
if (wert == 'wscript') InsertBlock( aTextFieldID, '<wscript>\n', 'WikiScript', '\n</wscript>', true );
aSelectControl.options.selectedIndex = 0;
}
function DoBold(id) { ToggleIBTags( id, CTagTypeBold ); }
function DoItalic(id) { ToggleIBTags( id, CTagTypeItalic ); }
function DoQuot(id) { ToggleInlineTags( id, '<<', 'Text', '>>' ); }
function DoTT(id) { ToggleInlineTags( id, '<<<', 'Text', '>>>' ); }
function DoRedir(id) { ToggleInlineTags( id, '#REDIRECT [[', 'Link', ']]' ); }
function DoImage(id) { InsertBlock( id, '[[', 'Bild:picture.jpg', '|zoom|180px|right|framed|Text]]', false ); }
function DoMath(id) { ToggleInlineTags( id, '{{math|', 'x', '}}' ); }
function DoNowiki(id) { ToggleInlineTags( id, '<nowiki>', 'Text', '</nowiki>' ); }
function DoLink(id) { ToggleLinkTags( id, '[[', 'Link', '|', '', ']]', CWordCharPattern ); }
function DoExtlink(id) { ToggleLinkTags( id, '[', 'http://www.domain.ch/', ' ', 'Website', ']', CUrlCharPattern ); }
function DoMail(id) { ToggleLinkTags( id, '[mailto:', 'name@domain.com', ' ', 'Name', ']', CUrlCharPattern ); }
function DoHeadline(id,lvl) { MakeHeader( id, lvl, 'Headline' ); }
function DoBreak(id) { InsertBlock( id, '{{break}}', '', '', true ); }
function DoHr(id) { InsertBlock( id, '----', '', '', true ); }
function DoInsertCB(id) { InsertBlock( id, '', GetFirstClipboardEntry(), '', false ); }
function DoUList(id) { Indent( id, '*' ); }
function DoOList(id) { Indent( id, '#' ); }
function DoIndent(id) { Indent( id, ':' ); }
function DoUnindent(id) { Unindent( id ); }
function GetFirstClipboardEntry() {
if (WIKI_CLIPBOARD.length == 0) return '';
return WIKI_CLIPBOARD[0];
}
function DoHelp(aTextFieldID) {
window.open( CToolbarHelpPage, "_blank" );
}
// -------------------------------------------------------------------------------------
function WriteEditToolbarHTML( aTextFieldID, aPicDir ) {
var id = aTextFieldID;
w('<table id="' + id + 'Toolbar" style="width:100%;background-color:#EEEEEE;border:2px solid #B4B4B4;border-width:2px 1px;margin:4px 0 0 0;"><tr>');
w('<td style="vertical-align:top;padding:0;white-space:nowrap;">');
w('<img src="'+aPicDir+'zbl.gif" width="4" height="26" alt="">');
w('<img src="'+aPicDir+'zh1.gif" width="21" height="26" onclick="DoHeadline(\''+id+'\',1)" alt="Header Level 1 [Crtl-Space]+[1]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zh2.gif" width="21" height="26" onclick="DoHeadline(\''+id+'\',2)" alt="Header Level 2 [Crtl-Space]+[2]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zh3.gif" width="21" height="26" onclick="DoHeadline(\''+id+'\',3)" alt="Header Level 3 [Crtl-Space]+[3]">');
w('<img src="'+aPicDir+'zbr.gif" width="2" height="26" alt="">');
w('<img src="'+aPicDir+'zbl.gif" width="4" height="26" alt="">');
w('<img src="'+aPicDir+'zbold.gif" width="21" height="26" onclick="DoBold(\''+id+'\')" alt="Bold [Crtl-Space]+[B]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zitalic.gif" width="21" height="26" onclick="DoItalic(\''+id+'\')" alt="Italic [Crtl-Space]+[I]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zq.gif" width="21" height="26" onclick="DoQuot(\''+id+'\')" alt="Quot [Crtl-Space]+[Q]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'ztt.gif" width="21" height="26" onclick="DoTT(\''+id+'\')" alt="Teletype [Crtl-Space]+[C]">');
w('<img src="'+aPicDir+'zbr.gif" width="2" height="26" alt="">');
w('<img src="'+aPicDir+'zbl.gif" width="4" height="26" alt="">');
w('<img src="'+aPicDir+'zul.gif" width="21" height="26" onclick="DoUList(\''+id+'\')" alt="Unordered List [Crtl-Space]+[L]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zol.gif" width="21" height="26" onclick="DoOList(\''+id+'\')" alt="Ordered List [Crtl-Space]+[N]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zin.gif" width="21" height="26" onclick="DoIndent(\''+id+'\')" alt="Indent [Crtl-Space]+[Z]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zuin.gif" width="21" height="26" onclick="DoUnindent(\''+id+'\')" alt="Unindent [Crtl-Space]+[U]">');
w('<img src="'+aPicDir+'zbr.gif" width="2" height="26" alt="">');
w('<img src="'+aPicDir+'zbl.gif" width="4" height="26" alt="">');
w('<img src="'+aPicDir+'zwlink.gif" width="21" height="26" onclick="DoLink(\''+id+'\')" alt="Wiki Link [Crtl-Space]+[W]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zlink.gif" width="21" height="26" onclick="DoExtlink(\''+id+'\')" alt="Web Link [Crtl-Space]+[X]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zmail.gif" width="21" height="26" onclick="DoMail(\''+id+'\')" alt="Email [Crtl-Space]+[E]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zredir.gif" width="21" height="26" onclick="DoRedir(\''+id+'\')" alt="Redirect [Crtl-Space]+[R]">');
w('<img src="'+aPicDir+'zbr.gif" width="2" height="26" alt="">');
w('<img src="'+aPicDir+'zbl.gif" width="4" height="26" alt="">');
w('<img src="'+aPicDir+'zpic.gif" width="21" height="26" onclick="DoImage(\''+id+'\')" alt="Image [Crtl-Space]+[P]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zfx.gif" width="21" height="26" onclick="DoMath(\''+id+'\')" alt="Math-Format [Crtl-Space]+[M]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'ztab.gif" width="21" height="26" onclick="DoTable(\''+id+'\')" alt="Create Table from Text [Crtl-Space]+[T]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'ztabh.gif" width="21" height="26" onclick="DoTableHead(\''+id+'\')" alt="Toggle Table Head [Crtl-Space]+[H]">');
w('<img src="'+aPicDir+'zbr.gif" width="2" height="26" alt="">');
w('<img src="'+aPicDir+'zbl.gif" width="4" height="26" alt="">');
w('<img src="'+aPicDir+'znowiki.gif" width="21" height="26" onclick="DoNowiki(\''+id+'\')" alt="nowiki">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zhr.gif" width="21" height="26" onclick="DoHr(\''+id+'\')" alt="Line [Crtl-Space]+[-]">');
w('<img src="'+aPicDir+'zb.gif" width="1" height="26" alt="">');
w('<img src="'+aPicDir+'zbreak.gif" width="21" height="26" onclick="DoBreak(\''+id+'\')" alt="Break [Crtl-Space]+[.]">');
w('<img src="'+aPicDir+'zbr.gif" width="2" height="26" alt="">');
w('<img src="'+aPicDir+'zbl.gif" width="4" height="26" alt="">');
w('<img src="'+aPicDir+'zhelp.gif" width="21" height="26" onclick="DoHelp(\''+id+'\')" alt="Help">');
w('<img src="'+aPicDir+'zbr.gif" width="2" height="26" alt="">');
w('</td><td style="width:100%;vertical-align:top;padding:2px 0 0 3px;">');
wl('<select size="1" name="BlockFunctions" onchange="DoBlockFormat(this,\''+id+'\')">');
wl('<option value="-" selected="selected">Block-Format</option>');
wl('<option value="intro">* Intro</option>');
wl('<option value="bigdir">* Verzeichnis</option>');
wl('<option value="poem">* Poem</option>');
wl('<option value="code">* Code</option>');
wl('<option value="div">* Div</option>');
wl('<option value="data">* Data</option>');
wl('<option value="comment">* Kommentar</option>');
wl('<option value="hidden">* Unsichtbar</option>');
wl('<option value="html">* HTML</option>');
wl('<option value="-">Bausteine</option>');
wl('<option value="keywords">* Keywords</option>');
wl('<option value="include">* Include</option>');
wl('<option value="style">* StyleSheet</option>');
wl('<option value="macrodef">* MacroDef</option>');
wl('<option value="script">* JavaScript</option>');
wl('<option value="wscript">* WikiScript</option>');
wl('</select>');
wl('</td></tr></table>');
}