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