Merge pull request #1640 from ether/caret-refactor
Caret refactor - I need help with tests...pull/1687/merge
commit
6a5e5cb6fd
|
@ -3722,6 +3722,9 @@ function Ace2Inner(){
|
|||
specialHandled = true;
|
||||
}
|
||||
if((evt.which == 33 || evt.which == 34) && type == 'keydown'){
|
||||
|
||||
evt.preventDefault(); // This is required, browsers will try to do normal default behavior on page up / down and the default behavior SUCKS
|
||||
|
||||
var oldVisibleLineRange = getVisibleLineRange();
|
||||
var topOffset = rep.selStart[0] - oldVisibleLineRange[0];
|
||||
if(topOffset < 0 ){
|
||||
|
@ -3732,29 +3735,38 @@ function Ace2Inner(){
|
|||
var isPageUp = evt.which === 33;
|
||||
|
||||
scheduler.setTimeout(function(){
|
||||
var newVisibleLineRange = getVisibleLineRange();
|
||||
var linesCount = rep.lines.length();
|
||||
var newVisibleLineRange = getVisibleLineRange(); // the visible lines IE 1,10
|
||||
var linesCount = rep.lines.length(); // total count of lines in pad IE 10
|
||||
var numberOfLinesInViewport = newVisibleLineRange[1] - newVisibleLineRange[0]; // How many lines are in the viewport right now?
|
||||
|
||||
var newCaretRow = rep.selStart[0];
|
||||
if(isPageUp){
|
||||
newCaretRow = oldVisibleLineRange[0];
|
||||
rep.selEnd[0] = rep.selEnd[0] - numberOfLinesInViewport; // move to the bottom line +1 in the viewport (essentially skipping over a page)
|
||||
rep.selStart[0] = rep.selStart[0] - numberOfLinesInViewport; // move to the bottom line +1 in the viewport (essentially skipping over a page)
|
||||
}
|
||||
|
||||
if(isPageDown){
|
||||
newCaretRow = newVisibleLineRange[0] + topOffset;
|
||||
if(isPageDown){ // if we hit page down
|
||||
if(rep.selEnd[0] >= oldVisibleLineRange[0]){ // If the new viewpoint position is actually further than where we are right now
|
||||
rep.selStart[0] = oldVisibleLineRange[1] -1; // dont go further in the page down than what's visible IE go from 0 to 50 if 50 is visible on screen but dont go below that else we miss content
|
||||
rep.selEnd[0] = oldVisibleLineRange[1] -1; // dont go further in the page down than what's visible IE go from 0 to 50 if 50 is visible on screen but dont go below that else we miss content
|
||||
}
|
||||
}
|
||||
|
||||
//ensure min and max
|
||||
if(newCaretRow < 0){
|
||||
newCaretRow = 0;
|
||||
if(rep.selEnd[0] < 0){
|
||||
rep.selEnd[0] = 0;
|
||||
}
|
||||
if(newCaretRow >= linesCount){
|
||||
newCaretRow = linesCount-1;
|
||||
if(rep.selStart[0] < 0){
|
||||
rep.selStart[0] = 0;
|
||||
}
|
||||
if(rep.selEnd[0] >= linesCount){
|
||||
rep.selEnd[0] = linesCount-1;
|
||||
}
|
||||
|
||||
rep.selStart[0] = newCaretRow;
|
||||
rep.selEnd[0] = newCaretRow;
|
||||
updateBrowserSelectionFromRep();
|
||||
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 | myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214
|
||||
// top.console.log(caretOffsetTop);
|
||||
setScrollY(caretOffsetTop); // set the scrollY offset of the viewport on the document
|
||||
|
||||
}, 200);
|
||||
}
|
||||
|
||||
|
@ -3762,32 +3774,44 @@ function Ace2Inner(){
|
|||
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 viewport = getViewPortTopBottom();
|
||||
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
|
||||
var lineHeight = $(myselection.focusNode.parentNode).parent().height(); // get the line height of the caret line
|
||||
var caretOffsetTopBottom = caretOffsetTop + lineHeight;
|
||||
var visibleLineRange = getVisibleLineRange(); // the visible lines IE 1,10
|
||||
|
||||
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?
|
||||
var caretIsNotVisible = (caretOffsetTop <= viewport.top || caretOffsetTopBottom >= viewport.bottom); // Is the Caret Visible to the user?
|
||||
if(caretIsNotVisible){ // 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
|
||||
if(evt.which == 37 || evt.which == 38){ // If left or up arrow
|
||||
var newY = caretOffsetTop; // That was easy!
|
||||
}
|
||||
if(evt.which == 39 || evt.which == 40){ // if down or right arrow
|
||||
// only move the viewport if we're at the bottom of the viewport, if we hit down any other time the viewport shouldn't change
|
||||
// NOTE: This behavior only fires if Chrome decides to break the page layout after a paste, it's annoying but nothing I can do
|
||||
var selection = getSelection();
|
||||
// top.console.log("line #", rep.selStart[0]); // the line our caret is on
|
||||
// top.console.log("firstvisible", visibleLineRange[0]); // the first visiblel ine
|
||||
// top.console.log("lastVisible", visibleLineRange[1]); // the last visible line
|
||||
|
||||
// Holding down arrow after a paste can lose the cursor -- This is the best fix I can find
|
||||
if(rep.selStart[0] >= visibleLineRange[1] || rep.selStart[0] < visibleLineRange[0] ){ // if we're not at the bottom of the viewport
|
||||
// top.console.log(viewport, lineHeight, myselection);
|
||||
// TODO: Make it so chrome doesnt need to redraw the page by only applying this technique if required
|
||||
var newY = caretOffsetTop;
|
||||
}else{ // we're at the bottom of the viewport so snap to a "new viewport"
|
||||
// top.console.log(viewport, lineHeight, myselection);
|
||||
var newY = caretOffsetTopBottom; // Allow continuous holding of down arrow to redraw the screen so we can see what we are going to highlight
|
||||
}
|
||||
}
|
||||
if(newY){
|
||||
setScrollY(newY); // set the scrollY offset of the viewport on the document
|
||||
}
|
||||
setScrollY(newY); // set the scroll height of the browser
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (type == "keydown")
|
||||
|
@ -5119,7 +5143,7 @@ function Ace2Inner(){
|
|||
setLineListType(mod[0], mod[1]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function doInsertUnorderedList(){
|
||||
doInsertList('bullet');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
describe("As the caret is moved is the UI properly updated?", function(){
|
||||
var padName;
|
||||
var numberOfRows = 50;
|
||||
|
||||
it("creates a pad", function(done) {
|
||||
padName = helper.newPad(done);
|
||||
this.timeout(60000);
|
||||
});
|
||||
|
||||
/* Tests to do
|
||||
* Keystroke up (38), down (40), left (37), right (39) with and without special keys IE control / shift
|
||||
* Page up (33) / down (34) with and without special keys
|
||||
* Page up on the first line shouldn't move the viewport
|
||||
* Down down on the last line shouldn't move the viewport
|
||||
* Down arrow on any other line except the last lines shouldn't move the viewport
|
||||
* Do all of the above tests after a copy/paste event
|
||||
*/
|
||||
|
||||
/* Challenges
|
||||
* How do we keep the authors focus on a line if the lines above the author are modified? We should only redraw the user to a location if they are typing and make sure shift and arrow keys aren't redrawing the UI else highlight - copy/paste would get broken
|
||||
* How can we simulate an edit event in the test framework?
|
||||
*/
|
||||
|
||||
// THIS DOESNT WORK AS IT DOESNT MOVE THE CURSOR!
|
||||
it("down arrow", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
$newFirstTextElement.focus();
|
||||
keyEvent(inner$, 37, false, false); // arrow down
|
||||
keyEvent(inner$, 37, false, false); // arrow down
|
||||
|
||||
done();
|
||||
});
|
||||
/*
|
||||
it("Creates N lines", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
|
||||
prepareDocument(numberOfRows, $newFirstTextElement); // N lines into the first div as a target
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
return inner$("div").first().text().length == 6;
|
||||
}).done(function(){ // Once the DOM has registered the items
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("Moves caret up a line", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
var originalCaretPosition = caretPosition(inner$);
|
||||
var originalPos = originalCaretPosition.y;
|
||||
var newCaretPos;
|
||||
keyEvent(inner$, 38, false, false); // arrow up
|
||||
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
var newCaretPosition = caretPosition(inner$);
|
||||
newCaretPos = newCaretPosition.y;
|
||||
return (newCaretPos < originalPos);
|
||||
}).done(function(){
|
||||
expect(newCaretPos).to.be.lessThan(originalPos);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("Moves caret down a line", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
var originalCaretPosition = caretPosition(inner$);
|
||||
var originalPos = originalCaretPosition.y;
|
||||
var newCaretPos;
|
||||
keyEvent(inner$, 40, false, false); // arrow down
|
||||
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
var newCaretPosition = caretPosition(inner$);
|
||||
newCaretPos = newCaretPosition.y;
|
||||
return (newCaretPos > originalPos);
|
||||
}).done(function(){
|
||||
expect(newCaretPos).to.be.moreThan(originalPos);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("Moves caret to top of doc", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
var originalCaretPosition = caretPosition(inner$);
|
||||
var originalPos = originalCaretPosition.y;
|
||||
var newCaretPos;
|
||||
|
||||
var i = 0;
|
||||
while(i < numberOfRows){ // press pageup key N times
|
||||
keyEvent(inner$, 33, false, false);
|
||||
i++;
|
||||
}
|
||||
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
var newCaretPosition = caretPosition(inner$);
|
||||
newCaretPos = newCaretPosition.y;
|
||||
return (newCaretPos < originalPos);
|
||||
}).done(function(){
|
||||
expect(newCaretPos).to.be.lessThan(originalPos);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("Moves caret right a position", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
var originalCaretPosition = caretPosition(inner$);
|
||||
var originalPos = originalCaretPosition.x;
|
||||
var newCaretPos;
|
||||
keyEvent(inner$, 39, false, false); // arrow right
|
||||
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
var newCaretPosition = caretPosition(inner$);
|
||||
newCaretPos = newCaretPosition.x;
|
||||
return (newCaretPos > originalPos);
|
||||
}).done(function(){
|
||||
expect(newCaretPos).to.be.moreThan(originalPos);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("Moves caret left a position", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
var originalCaretPosition = caretPosition(inner$);
|
||||
var originalPos = originalCaretPosition.x;
|
||||
var newCaretPos;
|
||||
keyEvent(inner$, 33, false, false); // arrow left
|
||||
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
var newCaretPosition = caretPosition(inner$);
|
||||
newCaretPos = newCaretPosition.x;
|
||||
return (newCaretPos < originalPos);
|
||||
}).done(function(){
|
||||
expect(newCaretPos).to.be.lessThan(originalPos);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("Moves caret to the next line using right arrow", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
var originalCaretPosition = caretPosition(inner$);
|
||||
var originalPos = originalCaretPosition.y;
|
||||
var newCaretPos;
|
||||
keyEvent(inner$, 39, false, false); // arrow right
|
||||
keyEvent(inner$, 39, false, false); // arrow right
|
||||
keyEvent(inner$, 39, false, false); // arrow right
|
||||
keyEvent(inner$, 39, false, false); // arrow right
|
||||
keyEvent(inner$, 39, false, false); // arrow right
|
||||
keyEvent(inner$, 39, false, false); // arrow right
|
||||
keyEvent(inner$, 39, false, false); // arrow right
|
||||
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
var newCaretPosition = caretPosition(inner$);
|
||||
newCaretPos = newCaretPosition.y;
|
||||
return (newCaretPos > originalPos);
|
||||
}).done(function(){
|
||||
expect(newCaretPos).to.be.moreThan(originalPos);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("Moves caret to the previous line using left arrow", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
var originalCaretPosition = caretPosition(inner$);
|
||||
var originalPos = originalCaretPosition.y;
|
||||
var newCaretPos;
|
||||
keyEvent(inner$, 33, false, false); // arrow left
|
||||
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
var newCaretPosition = caretPosition(inner$);
|
||||
newCaretPos = newCaretPosition.y;
|
||||
return (newCaretPos < originalPos);
|
||||
}).done(function(){
|
||||
expect(newCaretPos).to.be.lessThan(originalPos);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
/*
|
||||
it("Creates N rows, changes height of rows, updates UI by caret key events", function(done){
|
||||
var inner$ = helper.padInner$;
|
||||
var chrome$ = helper.padChrome$;
|
||||
var numberOfRows = 50;
|
||||
|
||||
//ace creates a new dom element when you press a keystroke, so just get the first text element again
|
||||
var $newFirstTextElement = inner$("div").first();
|
||||
var originalDivHeight = inner$("div").first().css("height");
|
||||
prepareDocument(numberOfRows, $newFirstTextElement); // N lines into the first div as a target
|
||||
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
return inner$("div").first().text().length == 6;
|
||||
}).done(function(){ // Once the DOM has registered the items
|
||||
inner$("div").each(function(index){ // Randomize the item heights (replicates images / headings etc)
|
||||
var random = Math.floor(Math.random() * (50)) + 20;
|
||||
$(this).css("height", random+"px");
|
||||
});
|
||||
|
||||
console.log(caretPosition(inner$));
|
||||
var newDivHeight = inner$("div").first().css("height");
|
||||
var heightHasChanged = originalDivHeight != newDivHeight; // has the new div height changed from the original div height
|
||||
expect(heightHasChanged).to.be(true); // expect the first line to be blank
|
||||
});
|
||||
|
||||
// Is this Element now visible to the pad user?
|
||||
helper.waitFor(function(){ // Wait for the DOM to register the new items
|
||||
return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); // Wait for the DOM to scroll into place
|
||||
}).done(function(){ // Once the DOM has registered the items
|
||||
inner$("div").each(function(index){ // Randomize the item heights (replicates images / headings etc)
|
||||
var random = Math.floor(Math.random() * (80 - 20 + 1)) + 20;
|
||||
$(this).css("height", random+"px");
|
||||
});
|
||||
|
||||
var newDivHeight = inner$("div").first().css("height");
|
||||
var heightHasChanged = originalDivHeight != newDivHeight; // has the new div height changed from the original div height
|
||||
expect(heightHasChanged).to.be(true); // expect the first line to be blank
|
||||
});
|
||||
var i = 0;
|
||||
while(i < numberOfRows){ // press down arrow
|
||||
console.log("dwn");
|
||||
keyEvent(inner$, 40, false, false);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Does scrolling back up the pad with the up arrow show the correct contents?
|
||||
helper.waitFor(function(){ // Wait for the new position to be in place
|
||||
try{
|
||||
return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); // Wait for the DOM to scroll into place
|
||||
}catch(e){
|
||||
return false;
|
||||
}
|
||||
}).done(function(){ // Once the DOM has registered the items
|
||||
|
||||
var i = 0;
|
||||
while(i < numberOfRows){ // press down arrow
|
||||
keyEvent(inner$, 33, false, false); // doesn't work
|
||||
i++;
|
||||
}
|
||||
|
||||
// Does scrolling back up the pad with the up arrow show the correct contents?
|
||||
helper.waitFor(function(){ // Wait for the new position to be in place
|
||||
try{
|
||||
return isScrolledIntoView(inner$("div:nth-child(0)"), inner$); // Wait for the DOM to scroll into place
|
||||
}catch(e){
|
||||
return false;
|
||||
}
|
||||
}).done(function(){ // Once the DOM has registered the items
|
||||
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
var i = 0;
|
||||
while(i < numberOfRows){ // press down arrow
|
||||
keyEvent(inner$, 33, false, false); // doesn't work
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
// Does scrolling back up the pad with the up arrow show the correct contents?
|
||||
helper.waitFor(function(){ // Wait for the new position to be in place
|
||||
return isScrolledIntoView(inner$("div:nth-child(1)"), inner$); // Wait for the DOM to scroll into place
|
||||
}).done(function(){ // Once the DOM has registered the items
|
||||
expect(true).to.be(true);
|
||||
done();
|
||||
});
|
||||
*/
|
||||
|
||||
});
|
||||
|
||||
function prepareDocument(n, target){ // generates a random document with random content on n lines
|
||||
var i = 0;
|
||||
while(i < n){ // for each line
|
||||
target.sendkeys(makeStr()); // generate a random string and send that to the editor
|
||||
target.sendkeys('{enter}'); // generator an enter keypress
|
||||
i++; // rinse n times
|
||||
}
|
||||
}
|
||||
|
||||
function keyEvent(target, charCode, ctrl, shift){ // sends a charCode to the window
|
||||
if(target.browser.mozilla){ // if it's a mozilla browser
|
||||
var evtType = "keypress";
|
||||
}else{
|
||||
var evtType = "keydown";
|
||||
}
|
||||
var e = target.Event(evtType);
|
||||
console.log(e);
|
||||
if(ctrl){
|
||||
e.ctrlKey = true; // Control key
|
||||
}
|
||||
if(shift){
|
||||
e.shiftKey = true; // Shift Key
|
||||
}
|
||||
e.which = charCode;
|
||||
e.keyCode = charCode;
|
||||
target("#innerdocbody").trigger(e);
|
||||
}
|
||||
|
||||
|
||||
function makeStr(){ // from http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 5; i++ )
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
return text;
|
||||
}
|
||||
|
||||
function isScrolledIntoView(elem, $){ // from http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
|
||||
var docViewTop = $(window).scrollTop();
|
||||
var docViewBottom = docViewTop + $(window).height();
|
||||
var elemTop = $(elem).offset().top; // how far the element is from the top of it's container
|
||||
var elemBottom = elemTop + $(elem).height(); // how far plus the height of the elem.. IE is it all in?
|
||||
elemBottom = elemBottom - 16; // don't ask, sorry but this is needed..
|
||||
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
|
||||
}
|
||||
|
||||
function caretPosition($){
|
||||
var doc = $.window.document;
|
||||
var pos = doc.getSelection();
|
||||
pos.y = pos.anchorNode.parentElement.offsetTop;
|
||||
pos.x = pos.anchorNode.parentElement.offsetLeft;
|
||||
console.log(pos);
|
||||
return pos;
|
||||
}
|
Loading…
Reference in New Issue