From 81f0ef73abfb492841e83a4b87a293a2d2d71ebc Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 17 Mar 2013 22:15:18 +0000 Subject: [PATCH 1/9] beginning of FE tests for caret tracking which is easily broken when you add weird line heights to pads --- tests/frontend/specs/caret.js | 114 ++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tests/frontend/specs/caret.js diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js new file mode 100644 index 00000000..fd479d5d --- /dev/null +++ b/tests/frontend/specs/caret.js @@ -0,0 +1,114 @@ +describe("As the caret is moved is the UI properly updated?", function(){ + //create a new pad before each test run + beforeEach(function(cb){ + helper.newPad(cb); + 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 + */ + + 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"); + }); + + 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 + }); + + // 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("+numberOfRows+")"), inner$); // Wait for the DOM to scroll into place + }).done(function(){ // Once the DOM has registered the items + var i = 0; + while(i < numberOfRows){ // press up arrow N times + keyEvent(inner$, 38, false, false); + i++; + } + + helper.waitFor(function(){ // Wait for the new position to be in place + return isScrolledIntoView(inner$("div:nth-child(0)"), inner$); // Wait for the DOM to scroll into place + }).done(function(){ // Once we're at the top of the document + 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); + if(ctrl){ + e.ctrlKey = true; // Control key + } + if(shift){ + e.shiftKey = true; // Shift Key + } + e.which = 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; + var elemBottom = elemTop + $(elem).height(); + return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); +} From 99ac407f089e83db3b1608bb4a9c7869f438c4c4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 17 Mar 2013 23:16:23 +0000 Subject: [PATCH 2/9] working caret position function --- tests/frontend/specs/caret.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index fd479d5d..56964885 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -10,8 +10,14 @@ describe("As the caret is moved is the UI properly updated?", function(){ * Page up (33) / down (34) with and without special keys */ + /* 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 the fsk do I get + * + */ + it("Creates N rows, changes height of rows, updates UI by caret key events", function(done) { - var inner$ = helper.padInner$; + var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; var numberOfRows = 50; @@ -29,6 +35,7 @@ describe("As the caret is moved is the UI properly updated?", function(){ $(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 @@ -112,3 +119,12 @@ function isScrolledIntoView(elem, $){ // from http://stackoverflow.com/questions var elemBottom = elemTop + $(elem).height(); 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; +} From 9f54a65c88176abb850e39dcbff07fda159538b6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 18 Mar 2013 17:40:18 +0000 Subject: [PATCH 3/9] refactored arrow keys now work after paste in chrome --- src/static/js/ace2_inner.js | 88 ++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 2dc6408b..905a7331 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3722,29 +3722,44 @@ 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? + + top.console.log(rep); + top.console.log("old vis", oldVisibleLineRange); - var newCaretRow = rep.selStart[0]; if(isPageUp){ - newCaretRow = oldVisibleLineRange[0]; + if(rep.selStart[0] == oldVisibleLineRange[0]+1 || rep.selStart[0] == oldVisibleLineRange[0] || rep.selStart[0] == oldVisibleLineRange[0] -1){ // if we're at the top of the document + rep.selEnd[0] = oldVisibleLineRange[0] - numberOfLinesInViewport; + } + else if(rep.selEnd[0] < (oldVisibleLineRange[0]+1)){ // If it's mostly near the bottom of a document + rep.selEnd[0] = oldVisibleLineRange[0]; // dont go further in the page up 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.selStart[0] = oldVisibleLineRange[0]; // dont go further in the page up 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 + } } - if(isPageDown){ - newCaretRow = newVisibleLineRange[0] + topOffset; + if(isPageDown){ // if we hit page down + if(rep.selEnd[0] > oldVisibleLineRange[0]){ + // top.console.log("new bottom", oldVisibleLineRange[1]); + 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.selEnd[0] >= linesCount){ + rep.selEnd[0] = linesCount-1; } - - rep.selStart[0] = newCaretRow; - rep.selEnd[0] = newCaretRow; +top.console.log(rep) 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; // get the carets selection offset in px IE 214 + setScrollY(caretOffsetTop); // set the scrollY offset of the viewport on the document + }, 200); } @@ -3752,32 +3767,43 @@ 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); + 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") @@ -5109,7 +5135,7 @@ function Ace2Inner(){ setLineListType(mod[0], mod[1]); }); } - + function doInsertUnorderedList(){ doInsertList('bullet'); } From 27e9f918640aebb3771ee9f212a6e0f66a456061 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 18 Mar 2013 18:03:37 +0000 Subject: [PATCH 4/9] page up, down etc all working, still no shift page up/down for highlight but that never worked anyways --- src/static/js/ace2_inner.js | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 905a7331..f8ed758d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3712,6 +3712,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 ){ @@ -3726,22 +3729,13 @@ function Ace2Inner(){ 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? - top.console.log(rep); - top.console.log("old vis", oldVisibleLineRange); - if(isPageUp){ - if(rep.selStart[0] == oldVisibleLineRange[0]+1 || rep.selStart[0] == oldVisibleLineRange[0] || rep.selStart[0] == oldVisibleLineRange[0] -1){ // if we're at the top of the document - rep.selEnd[0] = oldVisibleLineRange[0] - numberOfLinesInViewport; - } - else if(rep.selEnd[0] < (oldVisibleLineRange[0]+1)){ // If it's mostly near the bottom of a document - rep.selEnd[0] = oldVisibleLineRange[0]; // dont go further in the page up 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.selStart[0] = oldVisibleLineRange[0]; // dont go further in the page up 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] = 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){ // if we hit page down - if(rep.selEnd[0] > oldVisibleLineRange[0]){ - // top.console.log("new bottom", oldVisibleLineRange[1]); + 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 } @@ -3754,10 +3748,10 @@ function Ace2Inner(){ if(rep.selEnd[0] >= linesCount){ rep.selEnd[0] = linesCount-1; } -top.console.log(rep) 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; // get the carets selection offset in px IE 214 + 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); @@ -3792,6 +3786,7 @@ top.console.log(rep) // 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); From 3562672a757d4b0db85cda4113b5687183e67513 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 18 Mar 2013 18:44:01 +0000 Subject: [PATCH 5/9] stop start point going negative --- src/static/js/ace2_inner.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index f8ed758d..f091dc0c 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3745,6 +3745,9 @@ function Ace2Inner(){ if(rep.selEnd[0] < 0){ rep.selEnd[0] = 0; } + if(rep.selStart[0] < 0){ + rep.selStart[0] = 0; + } if(rep.selEnd[0] >= linesCount){ rep.selEnd[0] = linesCount-1; } From fb9d46fc51a4f8f2e2d9a4b3e01fc1821a1aac03 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 18 Mar 2013 20:08:58 +0000 Subject: [PATCH 6/9] document the required tests --- tests/frontend/specs/caret.js | 75 ++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index 56964885..9622df8a 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -8,15 +8,18 @@ describe("As the caret is moved is the UI properly updated?", function(){ /* 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 the fsk do I get - * + * How can we simulate an edit event in the test framework? */ - it("Creates N rows, changes height of rows, updates UI by caret key events", function(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; @@ -24,9 +27,9 @@ describe("As the caret is moved is the UI properly updated?", function(){ //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 @@ -40,7 +43,9 @@ describe("As the caret is moved is the UI properly updated?", function(){ 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 @@ -54,25 +59,64 @@ describe("As the caret is moved is the UI properly updated?", function(){ 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 - return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); // Wait for the DOM to scroll into 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 up arrow N times - keyEvent(inner$, 38, false, false); + 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++; } - helper.waitFor(function(){ // Wait for the new position to be in place - return isScrolledIntoView(inner$("div:nth-child(0)"), inner$); // Wait for the DOM to scroll into place - }).done(function(){ // Once we're at the top of the document - expect(true).to.be(true); - done(); - }); + + // 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(); }); + }); }); @@ -115,8 +159,9 @@ function makeStr(){ // from http://stackoverflow.com/questions/1349404/generate- 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; - var elemBottom = elemTop + $(elem).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)); } From ee6a7d0b0c93232ef94de134387a2238062c7b00 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 18 Mar 2013 22:09:51 +0000 Subject: [PATCH 7/9] most test pass but important ones failed --- tests/frontend/specs/caret.js | 100 +++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 13 deletions(-) diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index 9622df8a..7432fda1 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -1,7 +1,9 @@ describe("As the caret is moved is the UI properly updated?", function(){ - //create a new pad before each test run - beforeEach(function(cb){ - helper.newPad(cb); + var padName; + var numberOfRows = 50; + + it("creates a pad", function(done) { + padName = helper.newPad(done); this.timeout(60000); }); @@ -19,6 +21,87 @@ describe("As the caret is moved is the UI properly updated?", function(){ * 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$; + keyEvent(inner$, 40, false, false); // arrow up + 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("Creates N rows, changes height of rows, updates UI by caret key events", function(done){ var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; @@ -29,7 +112,6 @@ describe("As the caret is moved is the UI properly updated?", function(){ 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 @@ -43,9 +125,7 @@ describe("As the caret is moved is the UI properly updated?", function(){ 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 @@ -59,16 +139,12 @@ describe("As the caret is moved is the UI properly updated?", function(){ 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 @@ -99,7 +175,6 @@ console.log("dwn"); }); }); -*/ var i = 0; while(i < numberOfRows){ // press down arrow @@ -115,9 +190,8 @@ console.log("dwn"); expect(true).to.be(true); done(); }); +*/ - - }); }); function prepareDocument(n, target){ // generates a random document with random content on n lines From 13ee96dce0adc1a479d1237e7b5aeee406937200 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 18 Mar 2013 22:14:41 +0000 Subject: [PATCH 8/9] more tests but still fundamental flaw with arrow keys --- tests/frontend/specs/caret.js | 79 +++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index 7432fda1..9d9da460 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -100,6 +100,85 @@ describe("As the caret is moved is the UI properly updated?", function(){ }); }); + 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){ From 11341eb0954d319e127b80ead980d035ecbc637a Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 19 Mar 2013 12:52:14 +0000 Subject: [PATCH 9/9] add a test to show weird behavior --- tests/frontend/specs/caret.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index 9d9da460..b33f5168 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -24,10 +24,14 @@ describe("As the caret is moved is the UI properly updated?", function(){ // THIS DOESNT WORK AS IT DOESNT MOVE THE CURSOR! it("down arrow", function(done){ var inner$ = helper.padInner$; - keyEvent(inner$, 40, false, false); // arrow up + 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$; @@ -289,6 +293,7 @@ function keyEvent(target, charCode, ctrl, shift){ // sends a charCode to the win var evtType = "keydown"; } var e = target.Event(evtType); + console.log(e); if(ctrl){ e.ctrlKey = true; // Control key } @@ -296,6 +301,7 @@ function keyEvent(target, charCode, ctrl, shift){ // sends a charCode to the win e.shiftKey = true; // Shift Key } e.which = charCode; + e.keyCode = charCode; target("#innerdocbody").trigger(e); }