diff options
Diffstat (limited to 'src/static/js/ace2_inner.js')
-rw-r--r-- | src/static/js/ace2_inner.js | 435 |
1 files changed, 96 insertions, 339 deletions
diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index da8dea85..ddb614be 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -28,7 +28,7 @@ $ = jQuery = require('./rjquery').$; _ = require("./underscore"); var isNodeText = Ace2Common.isNodeText, - browser = Ace2Common.browser, + browser = $.browser, getAssoc = Ace2Common.getAssoc, setAssoc = Ace2Common.setAssoc, isTextNode = Ace2Common.isTextNode, @@ -154,7 +154,8 @@ function Ace2Inner(){ var dmesg = noop; window.dmesg = noop; - var scheduler = parent; + + var scheduler = parent; // hack for opera required var textFace = 'monospace'; var textSize = 12; @@ -1621,9 +1622,17 @@ function Ace2Inner(){ lines = ccData.lines; var lineAttribs = ccData.lineAttribs; var linesWrapped = ccData.linesWrapped; + var scrollToTheLeftNeeded = false; if (linesWrapped > 0) { + if(!browser.ie){ + // chrome decides in it's infinite wisdom that its okay to put the browsers visisble window in the middle of the span + // an outcome of this is that the first chars of the string are no longer visible to the user.. Yay chrome.. + // Move the browsers visible area to the left hand side of the span + // Firefox isn't quite so bad, but it's still pretty quirky. + var scrollToTheLeftNeeded = true; + } // console.log("Editor warning: " + linesWrapped + " long line" + (linesWrapped == 1 ? " was" : "s were") + " hard-wrapped into " + ccData.numLinesAfter + " lines."); } @@ -1691,6 +1700,10 @@ function Ace2Inner(){ //console.log("removed: "+id); }); + if(scrollToTheLeftNeeded){ // needed to stop chrome from breaking the ui when long strings without spaces are pasted + $("#innerdocbody").scrollLeft(0); + } + p.mark("findsel"); // if the nodes that define the selection weren't encountered during // content collection, figure out where those nodes are now. @@ -1896,7 +1909,7 @@ function Ace2Inner(){ var prevLine = rep.lines.prev(thisLine); var prevLineText = prevLine.text; var theIndent = /^ *(?:)/.exec(prevLineText)[0]; - if (/[\[\(\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB; + if (/[\[\(\:\{]\s*$/.exec(prevLineText)) theIndent += THE_TAB; var cs = Changeset.builder(rep.lines.totalWidth()).keep( rep.lines.offsetOfIndex(lineNum), lineNum).insert( theIndent, [ @@ -2817,7 +2830,6 @@ function Ace2Inner(){ rep.selStart = selectStart; rep.selEnd = selectEnd; rep.selFocusAtStart = newSelFocusAtStart; - if (mozillaFakeArrows) mozillaFakeArrows.notifySelectionChanged(); currentCallStack.repChanged = true; return true; @@ -3287,7 +3299,7 @@ function Ace2Inner(){ listType = /([a-z]+)([12345678])/.exec(listType); var type = listType[1]; var level = Number(listType[2]); - + //detect empty list item; exclude indentation if(text === '*' && type !== "indent") { @@ -3317,8 +3329,10 @@ function Ace2Inner(){ function doIndentOutdent(isOut) { - if (!(rep.selStart && rep.selEnd) || - ((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) + if (!((rep.selStart && rep.selEnd) || + ((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) && + (isOut != true) + ) { return false; } @@ -3326,7 +3340,6 @@ function Ace2Inner(){ var firstLine, lastLine; firstLine = rep.selStart[0]; lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0)); - var mods = []; for (var n = firstLine; n <= lastLine; n++) { @@ -3539,7 +3552,6 @@ function Ace2Inner(){ { // if (DEBUG && window.DONT_INCORP) return; if (!isEditable) return; - var type = evt.type; var charCode = evt.charCode; var keyCode = evt.keyCode; @@ -3561,6 +3573,11 @@ function Ace2Inner(){ var isModKey = ((!charCode) && ((type == "keyup") || (type == "keydown")) && (keyCode == 16 || keyCode == 17 || keyCode == 18 || keyCode == 20 || keyCode == 224 || keyCode == 91)); if (isModKey) return; + // If the key is a keypress and the browser is opera and the key is enter, do nothign at all as this fires twice. + if (keyCode == 13 && browser.opera && (type == "keypress")){ + return; // This stops double enters in Opera but double Tabs still show on single tab keypress, adding keyCode == 9 to this doesn't help as the event is fired twice + } + var specialHandled = false; var isTypeForSpecialKey = ((browser.msie || browser.safari) ? (type == "keydown") : (type == "keypress")); var isTypeForCmdKey = ((browser.msie || browser.safari) ? (type == "keydown") : (type == "keypress")); @@ -3690,12 +3707,73 @@ function Ace2Inner(){ doDeleteKey(); specialHandled = true; } + if((evt.which == 33 || evt.which == 34) && type == 'keydown'){ + var oldVisibleLineRange = getVisibleLineRange(); + var topOffset = rep.selStart[0] - oldVisibleLineRange[0]; + if(topOffset < 0 ){ + topOffset = 0; + } + + var isPageDown = evt.which === 34; + var isPageUp = evt.which === 33; + + scheduler.setTimeout(function(){ + var newVisibleLineRange = getVisibleLineRange(); + var linesCount = rep.lines.length(); + + var newCaretRow = rep.selStart[0]; + if(isPageUp){ + newCaretRow = oldVisibleLineRange[0]; + } + + if(isPageDown){ + newCaretRow = newVisibleLineRange[0] + topOffset; + } + + //ensure min and max + if(newCaretRow < 0){ + newCaretRow = 0; + } + if(newCaretRow >= linesCount){ + newCaretRow = linesCount-1; + } + + rep.selStart[0] = newCaretRow; + rep.selEnd[0] = newCaretRow; + updateBrowserSelectionFromRep(); + }, 200); + } + + /* Attempt to apply some sanity to cursor handling in Chrome after a copy / paste event + We have to do this the way we do because rep. doesn't hold the value for keyheld events IE if the user + presses and holds the arrow key */ + if((evt.which == 37 || evt.which == 38 || evt.which == 39 || evt.which == 40) && $.browser.chrome){ + + var newVisibleLineRange = getVisibleLineRange(); // get the current visible range -- This works great. + var lineHeight = textLineHeight(); // what Is the height of each line? + var myselection = document.getSelection(); // get the current caret selection, can't use rep. here because that only gives us the start position not the current + var caretOffsetTop = myselection.focusNode.parentNode.offsetTop; // get the carets selection offset in px IE 214 + + if(caretOffsetTop){ // sometimes caretOffsetTop bugs out and returns 0, not sure why, possible Chrome bug? Either way if it does we don't wanna mess with it + var lineNum = Math.round(caretOffsetTop / lineHeight) ; // Get the current Line Number IE 84 + newVisibleLineRange[1] = newVisibleLineRange[1]-1; + var caretIsVisible = (lineNum > newVisibleLineRange[0] && lineNum < newVisibleLineRange[1]); // Is the cursor in the visible Range IE ie 84 > 14 and 84 < 90? + + if(!caretIsVisible){ // is the cursor no longer visible to the user? + // Oh boy the caret is out of the visible area, I need to scroll the browser window to lineNum. + // Get the new Y by getting the line number and multiplying by the height of each line. + if(evt.which == 37 || evt.which == 38){ // If left or up + var newY = lineHeight * (lineNum -1); // -1 to go to the line above + }else if(evt.which == 39 || evt.which == 40){ // if down or right + var newY = getScrollY() + (lineHeight*3); // the offset and one additional line + } + setScrollY(newY); // set the scroll height of the browser + } + + } - if (mozillaFakeArrows && mozillaFakeArrows.handleKeyEvent(evt)) - { - evt.preventDefault(); - specialHandled = true; } + } if (type == "keydown") @@ -3801,7 +3879,6 @@ function Ace2Inner(){ selection.endPoint = getPointForLineAndChar(se); selection.focusAtStart = !! rep.selFocusAtStart; - setSelection(selection); } @@ -4119,6 +4196,11 @@ function Ace2Inner(){ selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); selection.focusAtStart = (((range.startContainer != range.endContainer) || (range.startOffset != range.endOffset)) && browserSelection.anchorNode && (browserSelection.anchorNode == range.endContainer) && (browserSelection.anchorOffset == range.endOffset)); + + if(selection.startPoint.node.ownerDocument !== window.document){ + return null; + } + return selection; } else return null; @@ -5032,331 +5114,6 @@ function Ace2Inner(){ } editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList; editorInfo.ace_doInsertOrderedList = doInsertOrderedList; - - var mozillaFakeArrows = (browser.mozilla && (function() - { - // In Firefox 2, arrow keys are unstable while DOM-manipulating - // operations are going on. Specifically, if an operation - // (computation that ties up the event queue) is going on (in the - // call-stack of some event, like a timeout) that at some point - // mutates nodes involved in the selection, then the arrow - // keypress may (randomly) move the caret to the beginning or end - // of the document. If the operation also mutates the selection - // range, the old selection or the new selection may be used, or - // neither. - // As long as the arrow is pressed during the busy operation, it - // doesn't seem to matter that the keydown and keypress events - // aren't generated until afterwards, or that the arrow movement - // can still be stopped (meaning it hasn't been performed yet); - // Firefox must be preserving some old information about the - // selection or the DOM from when the key was initially pressed. - // However, it also doesn't seem to matter when the key was - // actually pressed relative to the time of the mutation within - // the prolonged operation. Also, even in very controlled tests - // (like a mutation followed by a long period of busyWaiting), the - // problem shows up often but not every time, with no discernable - // pattern. Who knows, it could have something to do with the - // caret-blinking timer, or DOM changes not being applied - // immediately. - // This problem, mercifully, does not show up at all in IE or - // Safari. My solution is to have my own, full-featured arrow-key - // implementation for Firefox. - // Note that the problem addressed here is potentially very subtle, - // especially if the operation is quick and is timed to usually happen - // when the user is idle. - // features: - // - 'up' and 'down' arrows preserve column when passing through shorter lines - // - shift-arrows extend the "focus" point, which may be start or end of range - // - the focus point is kept horizontally and vertically scrolled into view - // - arrows without shift cause caret to move to beginning or end of selection (left,right) - // or move focus point up or down a line (up,down) - // - command-(left,right,up,down) on Mac acts like (line-start, line-end, doc-start, doc-end) - // - takes wrapping into account when doesWrap is true, i.e. up-arrow and down-arrow move - // between the virtual lines within a wrapped line; this was difficult, and unfortunately - // requires mutating the DOM to get the necessary information - var savedFocusColumn = 0; // a value of 0 has no effect - var updatingSelectionNow = false; - - function getVirtualLineView(lineNum) - { - var lineNode = rep.lines.atIndex(lineNum).lineNode; - while (lineNode.firstChild && isBlockElement(lineNode.firstChild)) - { - lineNode = lineNode.firstChild; - } - return makeVirtualLineView(lineNode); - } - - function markerlessLineAndChar(line, chr) - { - return [line, chr - rep.lines.atIndex(line).lineMarker]; - } - - function markerfulLineAndChar(line, chr) - { - return [line, chr + rep.lines.atIndex(line).lineMarker]; - } - - return { - notifySelectionChanged: function() - { - if (!updatingSelectionNow) - { - savedFocusColumn = 0; - } - }, - handleKeyEvent: function(evt) - { - // returns "true" if handled - if (evt.type != "keypress") return false; - var keyCode = evt.keyCode; - if (keyCode < 37 || keyCode > 40) return false; - incorporateUserChanges(); - - if (!(rep.selStart && rep.selEnd)) return true; - - // {byWord,toEnd,normal} - var moveMode = (evt.altKey ? "byWord" : (evt.ctrlKey ? "byWord" : (evt.metaKey ? "toEnd" : "normal"))); - - var anchorCaret = markerlessLineAndChar(rep.selStart[0], rep.selStart[1]); - var focusCaret = markerlessLineAndChar(rep.selEnd[0], rep.selEnd[1]); - var wasCaret = isCaret(); - if (rep.selFocusAtStart) - { - var tmp = anchorCaret; - anchorCaret = focusCaret; - focusCaret = tmp; - } - var K_UP = 38, - K_DOWN = 40, - K_LEFT = 37, - K_RIGHT = 39; - var dontMove = false; - if (wasCaret && !evt.shiftKey) - { - // collapse, will mutate both together - anchorCaret = focusCaret; - } - else if ((!wasCaret) && (!evt.shiftKey)) - { - if (keyCode == K_LEFT) - { - // place caret at beginning - if (rep.selFocusAtStart) anchorCaret = focusCaret; - else focusCaret = anchorCaret; - if (moveMode == "normal") dontMove = true; - } - else if (keyCode == K_RIGHT) - { - // place caret at end - if (rep.selFocusAtStart) focusCaret = anchorCaret; - else anchorCaret = focusCaret; - if (moveMode == "normal") dontMove = true; - } - else - { - // collapse, will mutate both together - anchorCaret = focusCaret; - } - } - if (!dontMove) - { - function lineLength(i) - { - var entry = rep.lines.atIndex(i); - return entry.text.length - entry.lineMarker; - } - - function lineText(i) - { - var entry = rep.lines.atIndex(i); - return entry.text.substring(entry.lineMarker); - } - - if (keyCode == K_UP || keyCode == K_DOWN) - { - var up = (keyCode == K_UP); - var canChangeLines = ((up && focusCaret[0]) || ((!up) && focusCaret[0] < rep.lines.length() - 1)); - var virtualLineView, virtualLineSpot, canChangeVirtualLines = false; - if (doesWrap) - { - virtualLineView = getVirtualLineView(focusCaret[0]); - virtualLineSpot = virtualLineView.getVLineAndOffsetForChar(focusCaret[1]); - canChangeVirtualLines = ((up && virtualLineSpot.vline > 0) || ((!up) && virtualLineSpot.vline < ( - virtualLineView.getNumVirtualLines() - 1))); - } - var newColByVirtualLineChange; - if (moveMode == "toEnd") - { - if (up) - { - focusCaret[0] = 0; - focusCaret[1] = 0; - } - else - { - focusCaret[0] = rep.lines.length() - 1; - focusCaret[1] = lineLength(focusCaret[0]); - } - } - else if (moveMode == "byWord") - { - // move by "paragraph", a feature that Firefox lacks but IE and Safari both have - if (up) - { - if (focusCaret[1] === 0 && canChangeLines) - { - focusCaret[0]--; - focusCaret[1] = 0; - } - else focusCaret[1] = 0; - } - else - { - var lineLen = lineLength(focusCaret[0]); - if (browser.windows) - { - if (canChangeLines) - { - focusCaret[0]++; - focusCaret[1] = 0; - } - else - { - focusCaret[1] = lineLen; - } - } - else - { - if (focusCaret[1] == lineLen && canChangeLines) - { - focusCaret[0]++; - focusCaret[1] = lineLength(focusCaret[0]); - } - else - { - focusCaret[1] = lineLen; - } - } - } - savedFocusColumn = 0; - } - else if (canChangeVirtualLines) - { - var vline = virtualLineSpot.vline; - var offset = virtualLineSpot.offset; - if (up) vline--; - else vline++; - if (savedFocusColumn > offset) offset = savedFocusColumn; - else - { - savedFocusColumn = offset; - } - var newSpot = virtualLineView.getCharForVLineAndOffset(vline, offset); - focusCaret[1] = newSpot.lineChar; - } - else if (canChangeLines) - { - if (up) focusCaret[0]--; - else focusCaret[0]++; - var offset = focusCaret[1]; - if (doesWrap) - { - offset = virtualLineSpot.offset; - } - if (savedFocusColumn > offset) offset = savedFocusColumn; - else - { - savedFocusColumn = offset; - } - if (doesWrap) - { - var newLineView = getVirtualLineView(focusCaret[0]); - var vline = (up ? newLineView.getNumVirtualLines() - 1 : 0); - var newSpot = newLineView.getCharForVLineAndOffset(vline, offset); - focusCaret[1] = newSpot.lineChar; - } - else - { - var lineLen = lineLength(focusCaret[0]); - if (offset > lineLen) offset = lineLen; - focusCaret[1] = offset; - } - } - else - { - if (up) focusCaret[1] = 0; - else focusCaret[1] = lineLength(focusCaret[0]); - savedFocusColumn = 0; - } - } - else if (keyCode == K_LEFT || keyCode == K_RIGHT) - { - var left = (keyCode == K_LEFT); - if (left) - { - if (moveMode == "toEnd") focusCaret[1] = 0; - else if (focusCaret[1] > 0) - { - if (moveMode == "byWord") - { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false); - } - else - { - focusCaret[1]--; - } - } - else if (focusCaret[0] > 0) - { - focusCaret[0]--; - focusCaret[1] = lineLength(focusCaret[0]); - if (moveMode == "byWord") - { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false); - } - } - } - else - { - var lineLen = lineLength(focusCaret[0]); - if (moveMode == "toEnd") focusCaret[1] = lineLen; - else if (focusCaret[1] < lineLen) - { - if (moveMode == "byWord") - { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true); - } - else - { - focusCaret[1]++; - } - } - else if (focusCaret[0] < rep.lines.length() - 1) - { - focusCaret[0]++; - focusCaret[1] = 0; - if (moveMode == "byWord") - { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true); - } - } - } - savedFocusColumn = 0; - } - } - - var newSelFocusAtStart = ((focusCaret[0] < anchorCaret[0]) || (focusCaret[0] == anchorCaret[0] && focusCaret[1] < anchorCaret[1])); - var newSelStart = (newSelFocusAtStart ? focusCaret : anchorCaret); - var newSelEnd = (newSelFocusAtStart ? anchorCaret : focusCaret); - updatingSelectionNow = true; - performSelectionChange(markerfulLineAndChar(newSelStart[0], newSelStart[1]), markerfulLineAndChar(newSelEnd[0], newSelEnd[1]), newSelFocusAtStart); - updatingSelectionNow = false; - currentCallStack.userChangedSelection = true; - return true; - } - }; - })()); var lineNumbersShown; var sideDivInner; |