mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-09 08:25:00 -04:00
Merge ded9825097
into 7e4bba0e31
This commit is contained in:
commit
93f5ea141b
2 changed files with 314 additions and 605 deletions
|
@ -24,7 +24,7 @@ hash node > /dev/null 2>&1 || {
|
||||||
NODE_VERSION=$(node --version)
|
NODE_VERSION=$(node --version)
|
||||||
if [ ! $(echo $NODE_VERSION | cut -d "." -f 1-2) = "v0.4" ]; then
|
if [ ! $(echo $NODE_VERSION | cut -d "." -f 1-2) = "v0.4" ]; then
|
||||||
echo "You're running a wrong version of node, you're using $NODE_VERSION, we need v0.4.x" >&2
|
echo "You're running a wrong version of node, you're using $NODE_VERSION, we need v0.4.x" >&2
|
||||||
exit 1
|
# exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#Is npm installed?
|
#Is npm installed?
|
||||||
|
|
|
@ -306,9 +306,25 @@ function OUTER(gscope)
|
||||||
|
|
||||||
if (currentCallStack)
|
if (currentCallStack)
|
||||||
{
|
{
|
||||||
console.error("Can't enter callstack " + type + ", already in " + currentCallStack.type);
|
window.console.log("Can't enter callstack " + type + ", already in " + currentCallStack.type);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentCallStack = {
|
||||||
|
type: type,
|
||||||
|
docTextChanged: false,
|
||||||
|
selectionAffected: false,
|
||||||
|
userChangedSelection: false,
|
||||||
|
domClean: false,
|
||||||
|
profileRest: profileRest,
|
||||||
|
isUserChange: false,
|
||||||
|
// is this a "user change" type of call-stack
|
||||||
|
repChanged: false,
|
||||||
|
editEvent: newEditEvent(type),
|
||||||
|
startNewEvent: startNewEvent
|
||||||
|
};
|
||||||
|
|
||||||
var profiling = false;
|
var profiling = false;
|
||||||
|
|
||||||
function profileRest()
|
function profileRest()
|
||||||
|
@ -378,41 +394,17 @@ function OUTER(gscope)
|
||||||
return oldEvent;
|
return oldEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentCallStack = {
|
|
||||||
type: type,
|
|
||||||
docTextChanged: false,
|
|
||||||
selectionAffected: false,
|
|
||||||
userChangedSelection: false,
|
|
||||||
domClean: false,
|
|
||||||
profileRest: profileRest,
|
|
||||||
isUserChange: false,
|
|
||||||
// is this a "user change" type of call-stack
|
|
||||||
repChanged: false,
|
|
||||||
editEvent: newEditEvent(type),
|
|
||||||
startNewEvent: startNewEvent
|
|
||||||
};
|
|
||||||
var cleanExit = false;
|
var cleanExit = false;
|
||||||
var result;
|
var result;
|
||||||
try
|
|
||||||
{
|
|
||||||
result = action();
|
|
||||||
//console.log("Just did action for: "+type);
|
|
||||||
cleanExit = true;
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
caughtErrors.push(
|
|
||||||
{
|
|
||||||
error: e,
|
|
||||||
time: +new Date()
|
|
||||||
});
|
|
||||||
dmesg(e.toString());
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
var cs = currentCallStack;
|
var cs = currentCallStack;
|
||||||
//console.log("Finished action for: "+type);
|
|
||||||
|
// window.console.log("doing action: "+action);
|
||||||
|
|
||||||
|
result = action();
|
||||||
|
cleanExit = true;
|
||||||
|
|
||||||
if (cleanExit)
|
if (cleanExit)
|
||||||
{
|
{
|
||||||
submitOldEvent(cs.editEvent);
|
submitOldEvent(cs.editEvent);
|
||||||
|
@ -424,9 +416,10 @@ function OUTER(gscope)
|
||||||
else parenModule.notifyTick();
|
else parenModule.notifyTick();
|
||||||
}
|
}
|
||||||
recolorModule.recolorLines();
|
recolorModule.recolorLines();
|
||||||
if (cs.selectionAffected)
|
if (cs.selectionAffected && !(browser.safari && (type == "applyChangesToBase" || type == "idleWorkTimer")))
|
||||||
{
|
{
|
||||||
updateBrowserSelectionFromRep();
|
updateBrowserSelectionFromRep();
|
||||||
|
updateLineNumbers();
|
||||||
}
|
}
|
||||||
if ((cs.docTextChanged || cs.userChangedSelection) && cs.type != "applyChangesToBase")
|
if ((cs.docTextChanged || cs.userChangedSelection) && cs.type != "applyChangesToBase")
|
||||||
{
|
{
|
||||||
|
@ -443,12 +436,17 @@ function OUTER(gscope)
|
||||||
// non-clean exit
|
// non-clean exit
|
||||||
if (currentCallStack.type == "idleWorkTimer")
|
if (currentCallStack.type == "idleWorkTimer")
|
||||||
{
|
{
|
||||||
|
window.console.log("set idle work timer");
|
||||||
|
|
||||||
idleWorkTimer.atLeast(1000);
|
idleWorkTimer.atLeast(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentCallStack = null;
|
currentCallStack = null;
|
||||||
if (profiling) console.profileEnd();
|
if (profiling) console.profileEnd();
|
||||||
}
|
|
||||||
|
//}, 0);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
editorInfo.ace_inCallStack = inCallStack;
|
editorInfo.ace_inCallStack = inCallStack;
|
||||||
|
@ -916,6 +914,7 @@ function OUTER(gscope)
|
||||||
|
|
||||||
performSelectionChange([0, rep.lines.atIndex(0).lineMarker], [0, rep.lines.atIndex(0).lineMarker]);
|
performSelectionChange([0, rep.lines.atIndex(0).lineMarker], [0, rep.lines.atIndex(0).lineMarker]);
|
||||||
|
|
||||||
|
window.console.log("set idle work timer");
|
||||||
idleWorkTimer.atMost(100);
|
idleWorkTimer.atMost(100);
|
||||||
|
|
||||||
if (rep.alltext != atext.text)
|
if (rep.alltext != atext.text)
|
||||||
|
@ -1340,56 +1339,60 @@ function OUTER(gscope)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inCallStack("idleWorkTimer", function()
|
// inCallStack("idleWorkTimer", function()
|
||||||
{
|
// {
|
||||||
|
|
||||||
var isTimeUp = newTimeLimit(250);
|
// var isTimeUp = newTimeLimit(250);
|
||||||
|
|
||||||
//console.time("idlework");
|
// //console.time("idlework");
|
||||||
var finishedImportantWork = false;
|
// var finishedImportantWork = false;
|
||||||
var finishedWork = false;
|
// var finishedWork = false;
|
||||||
|
|
||||||
try
|
// try
|
||||||
{
|
// {
|
||||||
|
|
||||||
// isTimeUp() is a soft constraint for incorporateUserChanges,
|
// // isTimeUp() is a soft constraint for incorporateUserChanges,
|
||||||
// which always renormalizes the DOM, no matter how long it takes,
|
// // which always renormalizes the DOM, no matter how long it takes,
|
||||||
// but doesn't necessarily lex and highlight it
|
// // but doesn't necessarily lex and highlight it
|
||||||
incorporateUserChanges(isTimeUp);
|
// incorporateUserChanges(isTimeUp);
|
||||||
|
|
||||||
if (isTimeUp()) return;
|
// if (isTimeUp()) return;
|
||||||
|
|
||||||
updateLineNumbers(); // update line numbers if any time left
|
// updateLineNumbers(); // update line numbers if any time left
|
||||||
if (isTimeUp()) return;
|
// if (isTimeUp()) return;
|
||||||
|
|
||||||
var visibleRange = getVisibleCharRange();
|
// var visibleRange = getVisibleCharRange();
|
||||||
var docRange = [0, rep.lines.totalWidth()];
|
// var docRange = [0, rep.lines.totalWidth()];
|
||||||
//console.log("%o %o", docRange, visibleRange);
|
// //console.log("%o %o", docRange, visibleRange);
|
||||||
finishedImportantWork = true;
|
// finishedImportantWork = true;
|
||||||
finishedWork = true;
|
// finishedWork = true;
|
||||||
}
|
// }
|
||||||
finally
|
// finally
|
||||||
{
|
// {
|
||||||
//console.timeEnd("idlework");
|
// //console.timeEnd("idlework");
|
||||||
if (finishedWork)
|
// if (finishedWork)
|
||||||
{
|
// {
|
||||||
idleWorkTimer.atMost(1000);
|
// // window.console.log("set idle work timer");
|
||||||
}
|
// // COMMENTED OUT!
|
||||||
else if (finishedImportantWork)
|
// // idleWorkTimer.atMost(1000);
|
||||||
{
|
// }
|
||||||
// if we've finished highlighting the view area,
|
// else if (finishedImportantWork)
|
||||||
// more highlighting could be counter-productive,
|
// {
|
||||||
// e.g. if the user just opened a triple-quote and will soon close it.
|
// // if we've finished highlighting the view area,
|
||||||
idleWorkTimer.atMost(500);
|
// // more highlighting could be counter-productive,
|
||||||
}
|
// // e.g. if the user just opened a triple-quote and will soon close it.
|
||||||
else
|
// window.console.log("set idle work timer");
|
||||||
{
|
// idleWorkTimer.atMost(500);
|
||||||
var timeToWait = Math.round(isTimeUp.elapsed() / 2);
|
// }
|
||||||
if (timeToWait < 100) timeToWait = 100;
|
// else
|
||||||
idleWorkTimer.atMost(timeToWait);
|
// {
|
||||||
}
|
// window.console.log("set idle work timer");
|
||||||
}
|
// var timeToWait = Math.round(isTimeUp.elapsed() / 2);
|
||||||
});
|
// if (timeToWait < 100) timeToWait = 100;
|
||||||
|
// idleWorkTimer.atMost(timeToWait);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
//if (! top.AFTER) top.AFTER = [];
|
//if (! top.AFTER) top.AFTER = [];
|
||||||
//top.AFTER.push(magicdom.root.dom.innerHTML);
|
//top.AFTER.push(magicdom.root.dom.innerHTML);
|
||||||
|
@ -1753,6 +1756,8 @@ function OUTER(gscope)
|
||||||
if (browser.msie)
|
if (browser.msie)
|
||||||
{
|
{
|
||||||
// try to undo IE's pesky and overzealous linkification
|
// try to undo IE's pesky and overzealous linkification
|
||||||
|
window.console.log("try here!");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
n.createTextRange().execCommand("unlink", false, null);
|
n.createTextRange().execCommand("unlink", false, null);
|
||||||
|
@ -1983,10 +1988,12 @@ function OUTER(gscope)
|
||||||
else p2.literal(0, "nonopt");
|
else p2.literal(0, "nonopt");
|
||||||
lastEntry = entry;
|
lastEntry = entry;
|
||||||
p2.mark("spans");
|
p2.mark("spans");
|
||||||
|
|
||||||
getSpansForLine(entry, function(tokenText, tokenClass)
|
getSpansForLine(entry, function(tokenText, tokenClass)
|
||||||
{
|
{
|
||||||
info.appendSpan(tokenText, tokenClass);
|
info.appendSpan(tokenText, tokenClass);
|
||||||
}, lineStartOffset, isTimeUp());
|
}, lineStartOffset, isTimeUp());
|
||||||
|
|
||||||
//else if (entry.text.length > 0) {
|
//else if (entry.text.length > 0) {
|
||||||
//info.appendSpan(entry.text, 'dirty');
|
//info.appendSpan(entry.text, 'dirty');
|
||||||
//}
|
//}
|
||||||
|
@ -3103,7 +3110,9 @@ function OUTER(gscope)
|
||||||
// an object property doesn't
|
// an object property doesn't
|
||||||
setAssoc(lineElem, "unpasted", {});
|
setAssoc(lineElem, "unpasted", {});
|
||||||
};
|
};
|
||||||
|
|
||||||
var lineClass = 'ace-line';
|
var lineClass = 'ace-line';
|
||||||
|
|
||||||
result.appendSpan = function(txt, cls)
|
result.appendSpan = function(txt, cls)
|
||||||
{
|
{
|
||||||
if ((!txt) && cls)
|
if ((!txt) && cls)
|
||||||
|
@ -3111,7 +3120,7 @@ function OUTER(gscope)
|
||||||
// gain a whole-line style (currently to show insertion point in CSS)
|
// gain a whole-line style (currently to show insertion point in CSS)
|
||||||
lineClass = domline.addToLineClass(lineClass, cls);
|
lineClass = domline.addToLineClass(lineClass, cls);
|
||||||
}
|
}
|
||||||
// otherwise, ignore appendSpan, this is an empty line
|
// // otherwise, ignore appendSpan, this is an empty line
|
||||||
};
|
};
|
||||||
result.clearSpans = function()
|
result.clearSpans = function()
|
||||||
{
|
{
|
||||||
|
@ -3475,7 +3484,7 @@ function OUTER(gscope)
|
||||||
|
|
||||||
inCallStack("handleClick", function()
|
inCallStack("handleClick", function()
|
||||||
{
|
{
|
||||||
idleWorkTimer.atMost(200);
|
// idleWorkTimer.atMost(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
// only want to catch left-click
|
// only want to catch left-click
|
||||||
|
@ -3495,6 +3504,8 @@ function OUTER(gscope)
|
||||||
}
|
}
|
||||||
if (n && isLink(n))
|
if (n && isLink(n))
|
||||||
{
|
{
|
||||||
|
window.console.log("try here!");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var newWindow = window.open(n.href, '_blank');
|
var newWindow = window.open(n.href, '_blank');
|
||||||
|
@ -3739,7 +3750,6 @@ function OUTER(gscope)
|
||||||
|
|
||||||
function handleKeyEvent(evt)
|
function handleKeyEvent(evt)
|
||||||
{
|
{
|
||||||
// if (DEBUG && window.DONT_INCORP) return;
|
|
||||||
if (!isEditable) return;
|
if (!isEditable) return;
|
||||||
|
|
||||||
var type = evt.type;
|
var type = evt.type;
|
||||||
|
@ -3747,7 +3757,10 @@ function OUTER(gscope)
|
||||||
var keyCode = evt.keyCode;
|
var keyCode = evt.keyCode;
|
||||||
var which = evt.which;
|
var which = evt.which;
|
||||||
|
|
||||||
//dmesg("keyevent type: "+type+", which: "+which);
|
// TEST FOR KEYBOARD RESPONSE IPAD
|
||||||
|
//setTimeout(function(){
|
||||||
|
// if (DEBUG && window.DONT_INCORP) return;
|
||||||
|
|
||||||
// Don't take action based on modifier keys going up and down.
|
// Don't take action based on modifier keys going up and down.
|
||||||
// Modifier keys do not generate "keypress" events.
|
// Modifier keys do not generate "keypress" events.
|
||||||
// 224 is the command-key under Mac Firefox.
|
// 224 is the command-key under Mac Firefox.
|
||||||
|
@ -3893,12 +3906,14 @@ function OUTER(gscope)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
idleWorkTimer.atLeast(500);
|
// window.console.log("set idle work timer");
|
||||||
|
// idleWorkTimer.atLeast(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type == "keyup")
|
else if (type == "keyup")
|
||||||
{
|
{
|
||||||
var wait = 200;
|
var wait = 200;
|
||||||
|
// window.console.log("set idle work timer");
|
||||||
idleWorkTimer.atLeast(wait);
|
idleWorkTimer.atLeast(wait);
|
||||||
idleWorkTimer.atMost(wait);
|
idleWorkTimer.atMost(wait);
|
||||||
}
|
}
|
||||||
|
@ -3929,6 +3944,8 @@ function OUTER(gscope)
|
||||||
thisKeyDoesntTriggerNormalize = false;
|
thisKeyDoesntTriggerNormalize = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// }, 10); //setTimeout (performance??);
|
||||||
}
|
}
|
||||||
|
|
||||||
var thisKeyDoesntTriggerNormalize = false;
|
var thisKeyDoesntTriggerNormalize = false;
|
||||||
|
@ -4024,6 +4041,9 @@ function OUTER(gscope)
|
||||||
function hasIESelection()
|
function hasIESelection()
|
||||||
{
|
{
|
||||||
var browserSelection;
|
var browserSelection;
|
||||||
|
|
||||||
|
window.console.log("try here!");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
browserSelection = doc.selection;
|
browserSelection = doc.selection;
|
||||||
|
@ -4053,6 +4073,9 @@ function OUTER(gscope)
|
||||||
if (browser.msie)
|
if (browser.msie)
|
||||||
{
|
{
|
||||||
var browserSelection;
|
var browserSelection;
|
||||||
|
|
||||||
|
window.console.log("try here!");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
browserSelection = doc.selection;
|
browserSelection = doc.selection;
|
||||||
|
@ -4386,10 +4409,10 @@ function OUTER(gscope)
|
||||||
moveToElementText(s, n);
|
moveToElementText(s, n);
|
||||||
// work around for issue that caret at beginning of line
|
// work around for issue that caret at beginning of line
|
||||||
// somehow ends up at end of previous line
|
// somehow ends up at end of previous line
|
||||||
if (s.move('character', 1))
|
// if (s.move('character', 1))
|
||||||
{
|
// {
|
||||||
s.move('character', -1);
|
// s.move('character', -1);
|
||||||
}
|
// }
|
||||||
s.collapse(true); // to start
|
s.collapse(true); // to start
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4749,8 +4772,11 @@ function OUTER(gscope)
|
||||||
|
|
||||||
function setDesignMode(newVal)
|
function setDesignMode(newVal)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
// window.console.log("try here!");
|
||||||
|
|
||||||
|
// try
|
||||||
|
// {
|
||||||
function setIfNecessary(target, prop, val)
|
function setIfNecessary(target, prop, val)
|
||||||
{
|
{
|
||||||
if (String(target[prop]).toLowerCase() != val)
|
if (String(target[prop]).toLowerCase() != val)
|
||||||
|
@ -4774,11 +4800,11 @@ function OUTER(gscope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
// }
|
||||||
catch (e)
|
// catch (e)
|
||||||
{
|
// {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
var iePastedLines = null;
|
var iePastedLines = null;
|
||||||
|
@ -4824,10 +4850,16 @@ function OUTER(gscope)
|
||||||
function bindTheEventHandlers()
|
function bindTheEventHandlers()
|
||||||
{
|
{
|
||||||
bindEventHandler(window, "unload", teardown);
|
bindEventHandler(window, "unload", teardown);
|
||||||
bindEventHandler(document, "keydown", handleKeyEvent);
|
// bindEventHandler(document, "keydown", handleKeyEvent);
|
||||||
bindEventHandler(document, "keypress", handleKeyEvent);
|
// bindEventHandler(document, "keypress", handleKeyEvent);
|
||||||
|
|
||||||
|
// added
|
||||||
|
bindEventHandler(document, "paste", handleKeyEvent);
|
||||||
|
bindEventHandler(document, "cut", handleKeyEvent);
|
||||||
|
|
||||||
bindEventHandler(document, "keyup", handleKeyEvent);
|
bindEventHandler(document, "keyup", handleKeyEvent);
|
||||||
bindEventHandler(document, "click", handleClick);
|
bindEventHandler(document, "click", handleClick);
|
||||||
|
|
||||||
bindEventHandler(root, "blur", handleBlur);
|
bindEventHandler(root, "blur", handleBlur);
|
||||||
if (browser.msie)
|
if (browser.msie)
|
||||||
{
|
{
|
||||||
|
@ -4936,6 +4968,8 @@ function OUTER(gscope)
|
||||||
if (browser.msie)
|
if (browser.msie)
|
||||||
{
|
{
|
||||||
// cache CSS background images
|
// cache CSS background images
|
||||||
|
window.console.log("try here!");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
doc.execCommand("BackgroundImageCache", false, true);
|
doc.execCommand("BackgroundImageCache", false, true);
|
||||||
|
@ -5248,335 +5282,10 @@ function OUTER(gscope)
|
||||||
}
|
}
|
||||||
editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList;
|
editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList;
|
||||||
|
|
||||||
var mozillaFakeArrows = (browser.mozilla && (function()
|
var mozillaFakeArrows = "";
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})());
|
|
||||||
|
|
||||||
|
|
||||||
// stolen from jquery-1.2.1
|
// stolen from jquery-1.2.1
|
||||||
|
|
||||||
|
|
||||||
function fixEvent(event)
|
function fixEvent(event)
|
||||||
{
|
{
|
||||||
// store a copy of the original event object
|
// store a copy of the original event object
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue