/****************************************************************** editor.js Muze Helene ------------------------------------------------------------------ Author: Muze (info@muze.nl) Date: 28 februari 2004 Copyright 2002, 2003, 2004 Muze This file is part of Helene. Helene is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. Helene is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Helene; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------- This file contains the basic editor functionality. ******************************************************************/ var currentLine=1; var previousLine=1; var lastLine=0; var screenWidth=0; var lineHeight=16; var contentDiv; var lines; var inputLine; var inputLineEntry; var oldpos=-1; var isDirty=false; var winW = 630; var winH = 460; var inputLineHasFocus = false; var offsetLine = 0; var currInputHeight = lineHeight; var letterWidth = 9; var clickPosOffsetLeft = 0; var longestLine = 0; var adjustTop = 0; var minInputWidth = 0; var addedSpaces=0; var keyBinding = new Array(); var keyBindings = new Array(); var cancelKey = false; var autoIndent = true; var startcoordinate = false; /* Undo/Redo vars */ var undoBuffer = []; var redoBuffer = []; var startCursorPos = -1; // Cursor position before text alteration var saveUndo = true; var isMoz = false; var checkPastedReturns = false; /* Selection vars */ var userSelecting = false; var selStart = true; // Is shift+arrows moving begin or end of selection var selHighlightedLine = null; // Last hilighted line when selection active var selStartLine = 0; var selEndLine = 0; var mouseMoveLC = 0; // Last call of mouseMoveHandler for IE trick var selCursorPos = 0; // This is cursor position for selections /* keybindings */ keyBindings["default"] = new Array(); keyBindings["default"]["default"] = do_Default; // Default action for unrecognized keys keyBindings["default"]["40"] = do_Down; // down keyBindings["default"]["s40"] = do_SelectDown; // shift-down keyBindings["default"]["38"] = do_Up; // up keyBindings["default"]["s38"] = do_SelectUp; // shift-up keyBindings["default"]["37"] = do_Left; // left keyBindings["default"]["s37"] = do_SelectLeft; // shift-left keyBindings["default"]["39"] = do_Right; // right keyBindings["default"]["s39"] = do_SelectRight; // shift-right keyBindings["default"]["8"] = do_Backspace; // backspace keyBindings["default"]["46"] = do_Delete; // delete keyBindings["default"]["13"] = do_Linebreak; // enter keyBindings["default"]["34"] = do_PageDown; // page-down keyBindings["default"]["s34"] = do_SelectPageDown; // shift-page-down keyBindings["default"]["33"] = do_PageUp; // page-up keyBindings["default"]["s33"] = do_SelectPageUp; // shift-page-up keyBindings["default"]["36"] = do_Home; // home keyBindings["default"]["9"] = do_Tab; // Tab keyBindings["default"]["c86"] = do_Paste; // ctrl-v keyBindings["default"]["c90"] = do_Undo; // ctrl-z keyBindings["default"]["c89"] = do_Redo; // ctrl-y keyBindings["default"]["cs90"] = do_Redo; // ctrl-shift-z // Do nothing (don't do default action) keyBindings["default"]["s16"] = do_Nothing; // shift keyBindings["default"]["c17"] = do_Nothing; // ctrl keyBindings["default"]["a18"] = do_Nothing; // alt keyBindings["default"]["cs16"] = do_Nothing; // ctrl+shift keyBindings["default"]["cs17"] = do_Nothing; // shift+ctrl keyBindings["default"]["as16"] = do_Nothing; // alt+shift keyBindings["default"]["as18"] = do_Nothing; // shift+alt keyBindings["default"]["ac18"] = do_Nothing; // ctrl+alt keyBindings["default"]["ac17"] = do_Nothing; // alt+ctrl keyBindings["default"]["c67"] = do_Nothing; // ctrl-c /* joe bindings */ keyBindings["joe"] = new Array(); keyBindings["joe"]["c65"] = do_Home; // ctrl-e keyBindings["joe"]["c69"] = do_End; // ctrl-a keyBindings["joe"]["c88"] = do_skipCharsRight; // ctrl-x keyBindings["joe"]["c90"] = do_skipCharsLeft; // ctrl-z keyBindings["joe"]["c75"] = joe_Ctrlk; // ctrl-k keyBindings["joe"]["cs189"] = do_Undo; // ctrl shift - (IE) keyBindings["joe"]["cs109"] = do_Undo; // ctrl shift - (MOZ) keyBindings["joe"]["cs54"] = do_Redo; // ctrl shift 6 keyBindings["joe"]["default"] = joe_Default; /* joe ctrl-k bindings */ keyBindings["joe"]["ctrl-k"] = new Array(); keyBindings["joe"]["ctrl-k"]["85"] = joe_ScrollHome; // ctrl-k u keyBindings["joe"]["ctrl-k"]["86"] = joe_ScrollEnd; // ctrl-k v keyBindings["joe"]["ctrl-k"]["default"] = joe_Default_Ctrlk; /* keybinding to use */ keyBinding = keyBindings["default"]; /* undo buffer constants */ var undo = { CHANGE_TEXT: 1, LINE_AFTER: 2, DELETE_LINE: 3 }; function init() { contentDiv=document.getElementById('content'); codeframeDiv=document.getElementById('codeframe'); lines=document.getElementById('lines'); lineHeight=lines.childNodes[0].offsetHeight; currInputHeight=lineHeight; lastLine=lines.childNodes.length; inputLine=document.getElementById('inputLine'); inputLineEntry=document.getElementById('inputLineEntry'); if (parseInt(navigator.appVersion)>3) { if (navigator.appName=="Netscape") { isMoz = true; winW = window.innerWidth-42; winH = window.innerHeight-2; minInputWidth = winW-16; document.onkeypress=moz_onKeyPress; document.onkeydown=keyDownHandler; // only mozilla has a keyUpHandler document.onkeyup = keyUpHandler; init_mozilla_compat(); /* do some w3c dom magic to get the current font size */ var vw=document.defaultView; var currStyle=vw.getComputedStyle(lines,""); var fontSize=currStyle.getPropertyValue("font-size"); var fontFamily=currStyle.getPropertyValue("font-family"); } else if (navigator.appName.indexOf("Microsoft")!=-1) { winW = document.body.offsetWidth; winH = document.body.offsetHeight; document.onkeydown=keyDownHandler; /* get current font size */ var fontSize=lines.currentStyle.fontSize; var fontFamily=lines.currentStyle.fontFamily; clickPosOffsetLeft = 44; minInputWidth=winW-60; // To handle clipboard paste inputLineEntry.onpaste = pasteText; } else { document.onkeydown=keyDownHandler; /* do some w3c dom magic to get the current font size */ var vw=document.defaultView; var currStyle=vw.getComputedStyle(lines,""); var fontSize=currStyle.getPropertyValue("font-size"); var fontFamily=currStyle.getPropertyValue("font-family"); } } document.getElementById('linenumbers').style.height=winH; updateLineNumbers(); inputLine.style.width=minInputWidth; inputLine.style.height=lineHeight; inputLineEntry.onfocus=inputFocus; inputLineEntry.onblur=inputBlur; inputLineEntry.style.fontSize=fontSize; inputLineEntry.style.fontFamily=fontFamily; inputLineEntry.style.height=lineHeight; inputLineEntry.style.overflow='hidden'; // this is needed because MSIE sometimes shifts the textarea a bit... adjustTop=-inputLineEntry.offsetTop; /* calculate width, height of one letter, fix padding if needed */ var templine=lines.childNodes[0].innerHTML; lines.childNodes[0].innerHTML='m'; letterWidth=document.getElementById("editorLetterWidth").offsetWidth; spanHeight=document.getElementById("editorLetterWidth").offsetHeight; spanTopPadding=document.getElementById("editorLetterWidth").offsetTop; if (lineHeight>spanHeight) { // this is to make background colors span the entire height of the line spanBottomPadding=(lineHeight-spanHeight)-spanTopPadding; } else { spanBottomPadding=0; } if (spanTopPadding || spanBottomPadding) { // get the stylesheet and apply this padding to each span var editorStylesheet=document.styleSheets[0]; if (navigator.appName.indexOf("Microsoft")!=-1) { editorStylesheet.addRule("SPAN", "padding-top: "+spanTopPadding+"px; padding-bottom: "+spanBottomPadding+"px;", 0); } else { editorStylesheet.insertRule("SPAN { padding-top: "+spanTopPadding+"px; padding-bottom: "+spanBottomPadding+"px; }", 0); } } lines.childNodes[0].innerHTML=templine; document.onmousedown=mouseDownHandler; document.onmousemove=mouseMoveHandler; document.onmouseup=mouseUpHandler; var _temp=''; for (var i=0; ilongestLine) { longestLine=_temp.length; } currLine=new hLine(i, _temp); currLine.doHighlight(applyHighlightedLine); } if ((longestLine*letterWidth)>minInputWidth) { inputLine.style.width=longestLine*letterWidth+4; } redrawInputLine(); } function pasteText() { var content = clipboardData.getData('text'); var re = /^([^\n\r]*)(\r\n|\r|\n)/; var match; var cur = currentLine; if (document.selection) { // For IE only var tr = document.selection.createRange(); tr.text = content; content = inputLineEntry.value; } else { // There is nothing to do in Mozilla return; } match = re.exec(content); if( match && match[1] != null ) { inputLineEntry.value = match[1]; isDirty = true; checkDirty(); updateLine(currentLine,match[1]); content = content.substr(match[0].length); } else { isDirty = true; checkDirty(); updateLine(currentLine,content); content = false; } if(content != false) { while( content != false ) { var trans; match = re.exec(content); if( match && match[1] != null ) { trans = match[1]; content = content.substr(match[0].length); } else { trans = content; content = false; } insertLineAfter(cur++,trans); } } event.returnValue=false; } function init_mozilla_compat() { HTMLElement.prototype.insertAdjacentHTML = function (sWhere, sText) { var r = document.createRange(); safewhere = sWhere.toLowerCase(); switch (safewhere) { case "beforebegin": r.setStartBefore(this); this.parentNode.insertBefore(r.createContextualFragment(sText), this); break; case "afterbegin": r.setStartBefore(this.firstChild); this.insertBefore(r.createContextualFragment(sText), this.firstChild); break; case "beforeend": r.setStartAfter(this.lastChild); this.appendChild(r.createContextualFragment(sText)); break; case "afterend": r.setStartAfter(this); this.parentNode.insertBefore(r.createContextualFragment(sText), this.nextSibling); break; } } Node.prototype.removeNode = function( removeChildren ) { var self = this; if ( Boolean( removeChildren ) ) { return this.parentNode.removeChild( self ); } else { var range = document.createRange(); range.selectNodeContents( self ); return this.parentNode.replaceChild( range.extractContents(), self ); } } } function inputFocus() { inputLineHasFocus=true; } function inputBlur() { inputLineHasFocus=false; } function applyHighlightedLine(lineNo, highlightedLine) { // the extra span is to correctly calculate the offset of the content // to position the input line setLineContent(lineNo+1, ''+highlightedLine+''); } function moveDown(amount) { if( userSelecting) { stopSelect(); currentLine = Math.max(0,selEndLine+amount); var selRange = document.selection.createRange(); var lineRange = selRange.duplicate(); lineRange.moveToElementText(lines.childNodes[selEndLine-1]); lineRange.setEndPoint("EndToEnd",selRange); var cursor = lineRange.text.length; redrawInputLine(true); setCursorPos(cursor); return false; } else { checkDirty(); if (currentLinelastLine) { currentLine=lastLine; } redrawInputLine(); } return false; } } function moveUp(amount) { if( userSelecting) { stopSelect(); currentLine = Math.max(0,selStartLine-amount); var selRange = document.selection.createRange(); var lineRange = selRange.duplicate(); lineRange.moveToElementText(lines.childNodes[selStartLine-1]); lineRange.setEndPoint("EndToStart",selRange); var cursor = lineRange.text.length; redrawInputLine(true); setCursorPos(cursor); return false; } else { checkDirty(); if (currentLine>1) { previousLine = currentLine; currentLine-=amount; if (currentLine<1) { currentLine=1; } redrawInputLine(); } return false; } } function redrawInputLine(dontfocus) { var pos; if (!dontfocus) { var oldline; var counter; pos = getCursorPos(); /* * we are gona calculate the real old pos by counting tabs for 8 */ oldpos = pos oldline = getLineContent(previousLine); counter = 0; while(counter < oldpos){ if(oldline.charAt(counter) == "\t"){ oldpos+=7; } counter++; } } // mozilla sometimes jumps the lines offset around, so we need to get the // offset of the content of the line, not the line itself var value=getLineContent(currentLine); var mytop; if( value != '' ) { mytop=lines.childNodes[currentLine-1].firstChild.offsetTop+adjustTop; } else { mytop=lines.childNodes[currentLine-1].offsetTop+adjustTop; } inputLine.style.top=mytop; inputLineEntry.value=value; if (!dontfocus) { pos = oldpos; setCursorPos(pos); // I don't think this is needed anymore // inputLineEntry.focus(); // scroll into view pls.. } startCursorPos = -1; } function getCursorPos() { // don't set the focus to the input line here, or the screen // will 'dance' around. Its not needed anyway. if (document.selection) { var mtext=document.selection.createRange(); var count=0; var moved=0; while (moved=mtext.moveStart('character', -100)) { count-=moved; } chars=mtext.text.length; return chars; } else { var selection=window.getSelection(); if (selection.anchorNode) { var len = inputLineEntry.selectionEnd; return len; } } } function setCursorPos(cursorstart, cursorend, clicked, ignoretab ) { var counter; var contents=new String(inputLineEntry.value); if (cursorstart==-1) { cursorstart=contents.length; } if( !ignoretab ) { counter = cursorstart; cursorstart = 0; while(counter > 0){ if(contents.charAt(cursorstart) == "\t"){ counter -= 7; } cursorstart++; counter--; } } if (contents.lengthcontents.length) { cursorstart=contents.length; } addedSpaces=0; } else { addedSpaces=0; } if (document.selection) { var mtext=inputLineEntry.createTextRange(); //~ mtext.collapse(true); // I think this is not needed mtext.moveStart('character', cursorstart); mtext.collapse(true); if (cursorend) { mtext.moveEnd('character', (cursorend-cursorstart)); } mtext.select(); } else { // do something Mozilla style if (!cursorend) { cursorend=cursorstart; } // for mozilla, set the focus to scroll the input line into view inputLineEntry.focus(); inputLineEntry.setSelectionRange(cursorstart, cursorend); /* var selection=window.getSelection(); if (selection.anchorNode) { inputLineEntry.selectionStart = cursorstart; if (cursorend) { inputLineEntry.selectionEnd = cursorend; } else { inputLineEntry.selectionEnd = cursorstart; } } */ } // calculate cursor position in px if (cursorend) { var scrollToChar=cursorend; } else { var scrollToChar=cursorstart; } cursorpx=letterWidth*(scrollToChar+10); if (cursorpx>150) { //(winW-44)) { // don't do this, let IE and mozilla handle it themselves // document.body.scrollLeft=cursorpx+44; } else { document.body.scrollLeft=0; } return false; } function removeAddedSpaces() { if (addedSpaces) { var _temp=new String(inputLineEntry.value); _temp=_temp.substr(0, _temp.length-addedSpaces); inputLineEntry.value=_temp; addedSpaces=0; } } function getLineContent(lineNo) { var myString = new String(); if(lines.childNodes[lineNo-1]){ if( typeof(lines.childNodes[lineNo-1].originalInnerText)!="undefined" ) { return lines.childNodes[lineNo-1].originalInnerText; } if (lines.childNodes[lineNo-1].innerText) { return lines.childNodes[lineNo-1].innerText; } else if (document.createRange) { var html = document.createRange(); var myLine = lineNo-1; while( lines.childNodes[myLine].tagName != 'LI' ) { myLine++; } html.selectNodeContents(lines.childNodes[myLine]); myString = html.toString(); myString = myString.replace(/\n/g, ''); return myString; } } return ""; } function getContents() { var i=1; var myContent=''; checkDirty(); while( lines.childNodes[i-1] ) { myContent = myContent + getLineContent(i) + '\n'; i++; } return myContent; } function setContents(content) { var myHead; var re = /^([^\n\r]*)(\r\n|\r|\n)/; var match; var originalArray = new Array(); deleteContents(); match = re.exec(content); if( match && match[1] != null ) { updateLine(1,match[1]); originalArray[lastLine] = match[1]; content = content.substr(match[0].length); } else { updateLine(1,content); originalArray[lastLine] = content; content = false; } if(content != false) { var htmlcontent = ''; while( content != false ) { var trans; match = re.exec(content); if( match && match[1] != null ) { trans = match[1]; content = content.substr(match[0].length); } else { trans = content; content = false; } lastLine++; originalArray[lastLine] = trans; trans = trans.replace(/[&]/g,"&"); trans = trans.replace(/[<]/g,"<"); trans = trans.replace(/[>]/g,">"); trans = trans.replace(/[ ]/g," "); trans = trans.replace(/["]/g,"""); htmlcontent += "
  • "+trans+"\n
  • "; } if(htmlcontent){ lines.childNodes[0].insertAdjacentHTML("AfterEnd",htmlcontent); } } var _temp=''; for (var i=1; ilongestLine) { longestLine=_temp.length; } currLine=new hLine(i, _temp); currLine.doHighlight(applyHighlightedLine); } if ((longestLine*letterWidth)>minInputWidth) { inputLine.style.width=longestLine*letterWidth+4; } currentLine=1; updateLineNumbers(); redrawInputLine(); undoBuffer = []; redoBuffer = []; } function setKeyBinding(keybind) { if( keyBindings[keybind] ) { keyBinding = keyBindings[keybind]; } } function setAutoIndent(indentvalue) { if( indentvalue == "on" ) { autoIndent = true; } else { autoIndent = false; } } function keyUpHandler() { // First we check for linebreaks that might have // Been pasted in. if( isMoz && checkPastedReturns ) { fixPastedReturns(); checkPastedReturns = false; } } function fixPastedReturns() { var re = /^([^\n\r]*)(\r\n|\r|\n)/; var match; var content = inputLineEntry.value; var cur = currentLine; match = re.exec(content); if( match && match[1] != null ) { inputLineEntry.value = match[1]; isDirty = true; checkDirty(); updateLine(currentLine,match[1]); content = content.substr(match[0].length); } else { content = false; } while( content != false ) { var trans; match = re.exec(content); if( match && match[1] != null ) { trans = match[1]; content = content.substr(match[0].length); } else { trans = content; content = false; } insertLineAfter(cur++,trans); } } function moz_onKeyPress(evt) { // Mozilla has a lot of trouble canceling events // an onkeydown event can not be cancelled // so we cancel the onkeypress... if( cancelKey ) { cancelKey = false; return false; } else { return true; } } function keyDownHandler(evt) { var charCode; var charString = ''; var keyResult; evt = (evt) ? evt : ((event) ? event : null ); if( evt ) { // Get the key pressed charCode = (evt.charCode ? evt.charCode : ((evt.keyCode) ? evt.keyCode : evt.which)); // Create the encoded character string charString = charCode; if( evt.shiftKey ) { charString = 's' + charString; } if( evt.ctrlKey ) { charString = 'c' + charString; } if( evt.altKey ) { charString = 'a' + charString; } window.status = charString; if( keyBinding[charString] ) { keyResult = keyBinding[charString](); } else { keyResult = keyBinding["default"](charString); } if( ! keyResult ) { cancelKey = true; if( evt.preventDefault ) { // This is the DOM way to cancel it but somehow // Mozilla refuses. Maybe another DOM browser will // make use of it :) evt.preventDefault(); } } return keyResult; } } function joe_Ctrlk(charString) { keyBinding = keyBindings["joe"]["ctrl-k"]; window.status = "ctrl-k "; return false; } function joe_Default_Ctrlk(charString) { keyBinding = keyBindings["joe"]; window.status = "ctrl-k " + charString; return false; } function joe_Default(charString) { var keyBinding; keyBinding = keyBindings["default"]; if( keyBinding[charString] ) { return keyBinding[charString](); } else { window.status = charString; return keyBinding["default"](charString); } } function joe_ScrollEnd() { keyBinding = keyBindings["joe"]; window.status = "ctrl-k v"; currentLine = lastLine; redrawInputLine(); do_End(); return false; } function joe_ScrollHome() { keyBinding = keyBindings["joe"]; window.status = "ctrl-k u"; currentLine = 1; redrawInputLine(); do_Home(); return false; } function do_Nothing(charString) { return true; } function removeSelectedText() { var firstPart = document.selection.createRange(); var secondPart = firstPart.duplicate(); firstPart.collapse(true); secondPart.collapse(false); var startLine = lines.childNodes[selStartLine-1]; var endLine = lines.childNodes[selEndLine-1]; var lineRange = firstPart.duplicate(); lineRange.moveToElementText(startLine); firstPart.setEndPoint('StartToStart',lineRange); var cursor = firstPart.text.length; lineRange.moveToElementText(endLine); secondPart.setEndPoint('EndToEnd',lineRange); updateLine(selStartLine,firstPart.text+secondPart.text); isDirty = true; checkDirty(); for(var i=selStartLine+1;i<=selEndLine;i++) deleteLine(i); currentLine = selStartLine; redrawInputLine(true); setCursorPos(cursor); isDirty = true; return; } function do_Default(charString) { if(userSelecting) { stopSelect(); removeSelectedText(); } removeAddedSpaces(); oldpos=-1; isDirty=true; if(startCursorPos<0) startCursorPos = getCursorPos(); return true; } function selHighlightLine() { if( !isMoz ) { // Mozilla has no 'smart' features like internet explorer. var selRange = document.selection.createRange(); selRange.collapse(selStart); } var line = lines.childNodes[(selStart ? selStartLine : selEndLine)-1]; if(selHighlightedLine != null) { selHighlightedLine.className = ""; selHighlightedLine = null; } if(line) { line.className = "inputLine"; selHighlightedLine = line; } } function selPlaceInputLine(atStart) { var cursor = selGetCursorPos(atStart); var index = atStart ? selStartLine : selEndLine; currentLine = index; inputLineEntry.value = getLineContent(index); redrawInputLine(true); stopSelect(); setCursorPos(cursor); } function selGetCursorPos(atStart) { var selRange = document.selection.createRange(); var index = atStart ? selStartLine : selEndLine; var line = lines.childNodes[index-1]; var lineRange = selRange.duplicate(); lineRange.moveToElementText(line); lineRange.setEndPoint(atStart ? 'EndToStart':'EndToEnd',selRange); return lineRange.text.length; } function do_SelectUp() { var startPos,startLine; if(!userSelecting) { // Nothing selected before startPos = getCursorPos(); startLine = currentLine; } startSelect(); var selRange; if( isMoz ) { selRange = window.getSelection().getRangeAt(0); } else { selRange = document.selection.createRange(); } if(typeof(startPos)!="undefined" && typeof(startLine)!="undefined") { // Nothing selected before selStart = true; if(startLine <= 1) { selRange.moveStart('character', - startPos); selStartLine = selEndLine = startLine; } else { if( isMoz ) { selRange.selectNodeContents(lines.childNodes[startLine-2]); selRange.setStart(lines.childNodes[startLine-2], startPos ); } else { selRange.moveToElementText(lines.childNodes[startLine-2]); // Getting previous line selRange.moveStart('character',Math.min(startPos,selRange.text.length)); // Moving to either cursor pos or length of the line selRange.moveEnd('character',startPos); } selStartLine = startLine - 1; selEndLine = startLine; } selCursorPos = startPos; } else { if(selCursorPos<0) { selCursorPos = selGetCursorPos(true); } var line = lines.childNodes[(selStart ? selStartLine : selEndLine)-1]; var prevLine = line.previousSibling; var lineRange = selRange.duplicate(); if(prevLine) { // We have the previous line lineRange.moveToElementText(prevLine); lineRange.moveStart('character', Math.min(lineRange.text.length, selCursorPos)); if(!selStart && selRange.compareEndPoints('StartToStart',lineRange)>0) { selStart = !selStart; selRange.collapse(selStart); if(selStartLine==selEndLine) selStartLine --; } else if(selStart) selStartLine --; else selEndLine --; selRange.setEndPoint(selStart ? 'StartToStart' : 'EndToStart',lineRange); } else { lineRange.moveToElementText(line); selRange.setEndPoint('StartToStart',lineRange); } if(selRange.text.length==0) { stopSelect(); selPlaceInputLine(selStart); return; } } selRange.select(); selHighlightLine(); return false; } function do_SelectDown() { var startPos,startLine; if(!userSelecting) { // Nothing selected before startPos = getCursorPos(); startLine = currentLine; } startSelect(); var selRange; if( isMoz ) { selRange = window.getSelection().getRangeAt(0); } else { selRange = document.selection.createRange(); } if(typeof(startPos)!="undefined" && typeof(startLine)!="undefined") { // Nothing selected before selCursorPos = startPos; selStart = false; if(startLine >= lines.childNodes.length) { // This is last line selRange.moveStart('character', startPos - 1); selStartLine = selEndLine = startLine; } else { selRange.moveToElementText(lines.childNodes[startLine]); // Getting line after current if(selRange.text.length > startPos) { // If it's length bigger than position selRange.collapse(true); selRange.moveEnd('character', startPos); // Setting end point to startPos } // else selection finishes at end of line var lineRange = selRange.duplicate(); lineRange.moveToElementText(lines.childNodes[startLine-1]); selRange.setEndPoint('StartToStart',lineRange); // Setting start of selection to currentLine selRange.moveStart('character', startPos); // Moving start of selection to cursor position selStartLine = startLine; selEndLine = startLine+1; } } else { if(selCursorPos<0) { selCursorPos = selGetCursorPos(true); } var line = lines.childNodes[(selStart ? selStartLine : selEndLine)-1]; var lineRange = selRange.duplicate(); nextLine = line.nextSibling; if(nextLine) { // if we have next line lineRange.moveToElementText(nextLine); lineRange.moveStart('character', Math.min(selCursorPos, lineRange.text.length)); if(selStart && selRange.compareEndPoints('EndToStart',lineRange)<0) { selStart = !selStart; selRange.collapse(selStart); if(selStartLine==selEndLine) selStartLine ++; } else if(selStart) selStartLine ++; else selEndLine ++; selRange.setEndPoint(selStart ? 'StartToStart' : 'EndToStart',lineRange); } else { // selecting to the end of file lineRange.moveToElementText(line); selRange.setEndPoint('EndToEnd',lineRange); } if(selRange.text.length==0) { stopSelect(); selPlaceInputLine(false); } } selRange.select(); selHighlightLine(); return false; } function do_Down(selecting) { return moveDown(1); } function do_Up(selecting) { return moveUp(1); } function do_SelectLeft() { var startPos,startLine; if(!userSelecting) { // Nothing selected before startPos = getCursorPos(); startLine = currentLine; if(startLine==1 && startPos==0) return; } startSelect(); var selRange; if( isMoz ) { selRange = window.getSelection().getRangeAt(0); } else { selRange = document.selection.createRange(); } if(typeof(startPos)!="undefined" && typeof(startLine)!="undefined") { // Noting selected before selStart = true; // Selecting just a character before cursor selRange.moveToElementText(lines.childNodes[startLine-1]); selRange.moveStart('character',startPos); selRange.collapse(true); selRange.moveStart('character',-1); selStartLine = startLine; selEndLine = startLine; if(startPos == 0) { selStartLine --; } } else { var lineRange = selRange.duplicate(); // Getting the text range of current line lineRange.moveToElementText(lines.childNodes[(selStart ? selStartLine : selEndLine)-1]); if(selRange.compareEndPoints(selStart ? 'StartToStart':'EndToStart',lineRange)==0) { // If first charancter of the line if(selStart) selStartLine = Math.max(1,selStartLine-1); // Don't go before first line else selEndLine = Math.max(1,selStartLine-1); } if(selStart) selRange.moveStart('character', -1); // If either we moving start point else selRange.moveEnd('character', -1); // or end point of selection } if(!selRange.text.length) { // If nothing selected selPlaceInputLine(false); // show input line return false; } selRange.select(); selHighlightLine(); selCursorPos = selGetCursorPos(selStart); return false; } function do_Left(selecting) { var result=true; if(userSelecting) { selPlaceInputLine(true); return; } checkDirty(); if( !selecting ) { stopSelect(); } if (addedSpaces) { removeAddedSpaces(); result=false; } if (getCursorPos()==0) { if (currentLine>1) { moveUp(1); setCursorPos(-1); result=false; } } oldpos=-1; return result; } function do_SelectRight() { var startPos,startLine; if(!userSelecting) { startPos = getCursorPos(); startLine = currentLine; } startSelect(); var selRange; if( isMoz ) { window.getSelection.getRangeAt(0); } else { selRange = document.selection.createRange(); } if(startPos && startLine) { selStart = false; selStartLine = startLine; selEndLine = startLine; selRange.moveToElementText(lines.childNodes[startLine-1]); if(startPos == selRange.text.length) { selEndLine ++; } selRange.moveStart('character',startPos); selRange.collapse(true); selRange.moveEnd('character', 1); } else { var lineRange = selRange.duplicate(); if(!selStart) { lineRange.moveToElementText(lines.childNodes[selEndLine-1]); if(selRange.compareEndPoints('EndToEnd',lineRange)==0) { selEndLine = Math.min(selEndLine+1,lines.childNodes.length); } } if(selStart) selRange.moveStart('character', 1); else selRange.moveEnd('character', 1); if(selStart) { lineRange.moveToElementText(lines.childNodes[selStartLine]); if(selRange.compareEndPoints('StartToStart',lineRange)==0) { selStartLine ++; } } } if(!selRange.text.length) { selPlaceInputLine(false); return false; } selRange.select(); selHighlightLine(); selCursorPos = selGetCursorPos(selStart); return false; } function do_Right(selecting) { var result=true; if(userSelecting) { selPlaceInputLine(false); return false; } checkDirty(); if( !selecting ) { stopSelect(); } if (currentLine=_currline.length) { moveDown(1); setCursorPos(0); result=false; } } oldpos=-1; return result; } function do_Backspace() { var result=true; if(userSelecting) { stopSelect(); removeSelectedText(); return false; } if (addedSpaces) { removeAddedSpaces(); result=false; } else { oldpos=-1; if (getCursorPos()==0) { checkDirty(); glueCurrentLine(-1); result=false; } isDirty=true; } return result; } function do_Delete() { if(userSelecting) { stopSelect(); removeSelectedText(); return; } var result=true; if (addedSpaces) { removeAddedSpaces(); result=false; } else { oldpos=-1; if (currentLine= 'a' && l <= 'z' || l >= 'A' && l <= 'Z') { return true; } else { return false; } } removeAddedSpaces(); cursor = getCursorPos(); if (dir < 0) { cursor--; } curstate = is_letter(inputLineEntry.value.charAt(cursor)); if (!curstate) { /* forward over current state */ while (curstate == is_letter(inputLineEntry.value.charAt(cursor+=dir)) && cursor > 0 && cursor < inputLineEntry.value.length); } else { cursor += dir; } curstate = is_letter(inputLineEntry.value.charAt(cursor)); /* forward over next state */ while (curstate == is_letter(inputLineEntry.value.charAt(cursor+=dir)) && cursor > 0 && cursor < inputLineEntry.value.length); if (dir < 0) { cursor++; } if (cursor < 0) { cursor = 0; } else if (cursor >= inputLineEntry.value.length) { cursor = inputLineEntry.value.length; } setCursorPos(cursor); oldpos = -1; return false; } function KeyUpHandler() { } function Coordinate(x, y) { this.x=x; this.y=y; } function mouseGetCoordinates(evt) { var target = (evt.target) ? evt.target : evt.srcElement; var clickX = -1; var clickY = -1; if( target && target.nodeName!='HTML' && target.nodeName!='BODY' ) { // HTML(moz) and BODY(IE) means clicked outside of our code.. while (target && target.nodeName!='LI' && target.nodeName!='TEXTAREA' && target.nodeName!='DIV') { if( target.parentElement ) { target=target.parentElement; } else { // mozilla style target = target.parentNode; } } if( target ) { // get click position if (evt.pageX) { var offsetX=evt.pageX - ((target.offsetLeft) ? target.offsetLeft : target.left); var offsetY=evt.pageY - ((target.offsetTop) ? target.offsetTop : target.top); } else if (evt.offsetX || evt.offsetY) { var offsetX = evt.offsetX; var offsetY = evt.offsetY; } else if (evt.clientX || evt.clientY) { var offsetX = evt.clientX - ((target.offsetLeft) ? target.offsetLeft : 0); var offsetY = evt.clientY - ((target.offsetTop) ? target.offsetTop : 0); } offsetX-=clickPosOffsetLeft; var clickX=Math.round(offsetX/letterWidth); if(!isMoz) { var tr = document.selection.createRange(); var lineRange = tr.duplicate(); lineRange.moveToElementText(target); tr.moveToPoint(event.clientX,event.clientY); tr.setEndPoint("StartToStart",lineRange); clickX = tr.text.length; } // find a known container of the mouseclick if (target.nodeName=='LI') { //~ clickY=1; //~ while (target=target.previousSibling) { //~ clickY++; //~ } clickY = Math.floor(target.offsetTop/target.offsetHeight) + 1; // It is faster way to determine line number } else if (target.nodeName=='INPUT') { } else if (target.nodeName=='TEXTAREA') { clickY = currentLine; } else if (target.nodeName=='DIV') { } else { clickY=Math.round(offsetY/lineHeight)+1; } } } if (clickX>-1 && clickY>-1) { return new Coordinate(clickX, clickY); } else { return false; } } function mouseUpHandler(evt) { evt = (evt) ? evt : event; var coordinate=mouseGetCoordinates(evt); if (coordinate) { checkDirty(); if (startcoordinate && (!(startcoordinate.x==coordinate.x) || !(startcoordinate.y==coordinate.y)) ) { if (startcoordinate.y==coordinate.y) { // single line selection if (startcoordinate.x>coordinate.x) { var _temp=coordinate; coordinate=startcoordinate; startcoordinate=_temp; } currentLine=coordinate.y; redrawInputLine(true); setCursorPos(startcoordinate.x, coordinate.x, true); oldpos=-1; evt.cancelBubble=true; startcoordinate = null; return false; } else { // multi line selection if(!userSelecting) { startSelect(); } var c_begin; var c_end; if(coordinate.y > startcoordinate.y || coordinate.y == startcoordinate.y && coordinate.x > startcoordinate.x) { c_begin = startcoordinate; c_end = coordinate; selStart = false; } else { c_end = startcoordinate; c_begin = coordinate; selStart = true; } if( !isMoz ) { // Prevent 'Smart' Internet Explorer selecting, Mozilla doesn't need this. var selRange = document.selection.createRange(); var startRow = selRange.duplicate(); var endRow = startRow.duplicate(); startRow.moveToElementText(lines.childNodes[c_begin.y-1]); endRow.moveToElementText(lines.childNodes[c_end.y-1]); selRange.setEndPoint('StartToStart',startRow); if(c_end.x>endRow.text.length) { selRange.setEndPoint('EndToEnd',endRow); } else { selRange.setEndPoint('EndToStart',endRow); selRange.moveEnd('character',c_end.x); } if(c_begin.x>startRow.text.length) { selRange.setEndPoint('StartToEnd',startRow); } else { selRange.moveStart('character',c_begin.x); } selRange.select(); } selStartLine = c_begin.y; selEndLine = c_end.y; selHighlightLine(); } } else { // no selection if(userSelecting) { stopSelect(); } currentLine=coordinate.y; redrawInputLine(true); setCursorPos(coordinate.x, 0, true); oldpos=-1; evt.cancelBubble=true; startcoordinate = null; return false; } } startcoordinate = null; } function mouseDownHandler(evt) { evt = (evt) ? evt : event; startcoordinate=mouseGetCoordinates(evt); startSelect(); selStartLine = startcoordinate.y; selEndLine = startcoordinate.y; selHighlightLine(); selCursorPos = -1; evt.returnValue = false; } function mouseMoveHandler(evt) { if(!isMoz) { // A trick to improve perfomance in IE var tm = (new Date()).getTime(); if(tm - mouseMoveLC < 50) return; mouseMoveLC = tm; } evt = (evt) ? evt : event; evt.returnValue = false; coordinate=mouseGetCoordinates(evt); if(startcoordinate && (coordinate.x!=startcoordinate.x || coordinate.y!=startcoordinate.y)) { if(!userSelecting) { startSelect(); } var c_begin; var c_end; if(coordinate.y > startcoordinate.y || coordinate.y == startcoordinate.y && coordinate.x > startcoordinate.x) { c_begin = startcoordinate; c_end = coordinate; selStart = false; } else { c_end = startcoordinate; c_begin = coordinate; selStart = true; } var selRange; var startRow; var endRow; if( !isMoz) { // Prevent 'Smart' Selections of Internet Explorer, Mozilla doesn't require this code. selRange = document.selection.createRange(); startRow = selRange.duplicate(); endRow = startRow.duplicate(); var line = lines.childNodes[c_begin.y-1]; if(!line) { return; } startRow.moveToElementText(line); line = lines.childNodes[c_end.y-1]; if(!line) { return; } endRow.moveToElementText(line); selRange.setEndPoint('StartToStart',startRow); if(c_end.x>endRow.text.length) { selRange.setEndPoint('EndToEnd',endRow); } else { selRange.setEndPoint('EndToStart',endRow); selRange.moveEnd('character',c_end.x); } if(c_begin.x>startRow.text.length) { selRange.setEndPoint('StartToEnd',startRow); } else { selRange.moveStart('character',c_begin.x); } selRange.select(); } selStartLine = c_begin.y; selEndLine = c_end.y; selHighlightLine(); } } function startSelect() { checkDirty(); userSelecting=true; inputLine.style.visibility = "hidden"; } function stopSelect() { userSelecting=false; if(selHighlightedLine!=null) { selHighlightedLine.className = ""; } inputLine.style.visibility = "visible"; } function updateLine(lineNo, lineContent) { lines.childNodes[lineNo-1].originalInnerText = lineContent; highlightUpdateLine(lineNo-1, lineContent, applyHighlightedLine); if (lineContent.length>longestLine) { longestLine=lineContent.length; if( (longestLine*letterWidth) > minInputWidth ) { inputLine.style.width=longestLine*letterWidth+4; } } } function setLineContent(lineNo, lineContent) { if(lines.childNodes[lineNo-1]){ lines.childNodes[lineNo-1].innerHTML=lineContent; lines.childNodes[lineNo-1].originalInnerHTML=lineContent; } } function insertLineAfter(lineNo, lineContent) { var oldCursor = getCursorPos(); lines.childNodes[lineNo-1].insertAdjacentHTML("AfterEnd","
  • "); highlightAppendLine(lineNo-1, lineContent, false); // do not display this yet currentLine=lineNo+1; updateLine(currentLine, lineContent); oldpos=-1; redrawInputLine(true); updateLineNumbers(); lastLine++; if(saveUndo) { undoBuffer.push({type: undo.LINE_AFTER, line: currentLine, text: lineContent, cursor: getCursorPos(), newCursor: oldCursor }); } } function deleteLine(lineNo) { if(saveUndo) { var content = getLineContent(lineNo); undoBuffer.push({type: undo.DELETE_LINE, line: lineNo, text: content, cursor: getCursorPos() , newCursor: content.length }); } lines.childNodes[lineNo-1].removeNode(true); highlightDeleteLine(lineNo-1, applyHighlightedLine); updateLineNumbers(); lastLine--; } function deleteContents() { var len = lines.childNodes.length; for (var i=len-1; i>0; i--) { lines.childNodes[i].removeNode(true); } setCursorPos(0); highlightReset(); updateLineNumbers(); lastLine=1; undoBuffer = []; redoBuffer = []; } function updateLineNumbers() { if (contentDiv.offsetHeight>winH) { document.getElementById('linenumbers').style.height=contentDiv.offsetHeight; } } function checkDirty() { if (isDirty) { var newContents = inputLineEntry.value; var oldContents = getLineContent(currentLine); if(newContents != oldContents) { undoBuffer.push({ type: undo.CHANGE_TEXT, line: currentLine, text: oldContents, newText: newContents, cursor: startCursorPos, newCursor: getCursorPos() }); updateLine(currentLine, newContents); isDirty=false; startCursorPos = -1; return true; } } else { } return false; } function getLineHead() { var _line=new String(getLineContent(currentLine)); var _pos=getCursorPos(); return _line.substr(0, _pos); } function getLineTail() { var _line=new String(getLineContent(currentLine)); var _pos=getCursorPos(); return _line.substr(_pos); } function breakCurrentLine() { checkDirty(); var head=getLineHead(); var tail=getLineTail(); var re = /([\t ]+)/; var match = re.exec(head); var newCursor = 0; if( autoIndent && match ) { tail = match[1] + tail; newCursor = match[1].length; } insertLineAfter(currentLine, tail); updateLine(currentLine-1, head); setCursorPos(newCursor); inputLineEntry.focus(); } function glueCurrentLine(direction) { checkDirty(); if (direction==-1) { if (currentLine>1) { var tail=getLineContent(currentLine); var head=getLineContent(currentLine-1); updateLine(currentLine-1, head+tail); deleteLine(currentLine); currentLine--; redrawInputLine(); setCursorPos(head.length); } } else { if (currentLine