| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
| <style> |
| div.test { |
| -webkit-user-modify: read-write; |
| padding: 4px; |
| border: 1px dashed lightblue; |
| margin: 4px 4px 4px 24px; |
| outline: none; |
| font-family: Lucida Grande; |
| counter-increment: test-number; |
| } |
| div.test:before { content: counter(test-number); position: absolute; left: 8px; font-size: x-small; text-align: right; width: 20px; } |
| div.test span { background-color: #def; } |
| div.test img { width: 1em; height: 1em; background-color: lightgreen; } |
| div.test img + img { background-color: lightblue; } |
| div.test div { border: 1px dashed pink; padding: 3px; height: 2em; } |
| </style> |
| <script> |
| var messages = []; |
| |
| function log(message) |
| { |
| messages.push(message); |
| } |
| |
| function flushLog() |
| { |
| document.getElementById("console").appendChild(document.createTextNode(messages.join(""))); |
| } |
| |
| function caretCoordinates() |
| { |
| if (!window.textInputController) |
| return { x: 0, y :0 }; |
| var caretRect = textInputController.firstRectForCharacterRange(textInputController.selectedRange()[0], 0); |
| return { x: caretRect[0], y: caretRect[1] }; |
| } |
| |
| function positionsMovingInDirection(sel, direction) |
| { |
| var positions = []; |
| while (true) { |
| positions.push({ node: sel.anchorNode, offset: sel.anchorOffset, point: caretCoordinates() }); |
| sel.modify("move", direction, "character"); |
| if (positions[positions.length - 1].node == sel.anchorNode && positions[positions.length - 1].offset == sel.anchorOffset) |
| break; |
| }; |
| return positions; |
| } |
| |
| function fold(string) |
| { |
| var result = ""; |
| for (var i = 0; i < string.length; ++i) { |
| var char = string.charCodeAt(i); |
| if (char >= 0x0660) // Arabic numeral 0 |
| char = char - 0x660 + '0'.charCodeAt(0); |
| else if (char >= 0x0627) // Alif |
| char = char - 0x0627 + 'A'.charCodeAt(0); |
| else if (char >= 0x05d0) |
| char -= 0x058f; |
| else if (char == 10) { |
| result += "\\n"; |
| continue; |
| } |
| result += String.fromCharCode(char); |
| } |
| return result; |
| } |
| |
| function logPositions(positions) |
| { |
| for (var i = 0; i < positions.length; ++i) { |
| if (i) { |
| if (positions[i].node != positions[i - 1].node) |
| log("]"); |
| log(", "); |
| } |
| if (!i || positions[i].node != positions[i - 1].node) |
| log((positions[i].node instanceof Text ? '"' + fold(positions[i].node.data) + '"' : "<" + positions[i].node.tagName + ">") + "["); |
| log(positions[i].offset); |
| } |
| log("]"); |
| } |
| |
| function checkReverseOrder(positions, reversePositions) |
| { |
| var mismatch = positions.length != reversePositions.length; |
| while (!mismatch) { |
| var pos = positions.pop(); |
| if (!pos) |
| break; |
| var reversePos = reversePositions.shift(); |
| if (pos.node != reversePos.node || pos.offset != reversePos.offset) |
| mismatch = true; |
| } |
| |
| if (mismatch) |
| log("WARNING: Moving to the left did not visit the same positions in reverse order as moving to the right.\n"); |
| } |
| |
| function checkCoordinatesMovingRightDown(positions) |
| { |
| for (var i = 1; i < positions.length; ++i) { |
| if (positions[i].point.y > positions[i - 1].point.y || positions[i].point.x < positions[i - 1].point.x && positions[i].point.y >= positions[i - 1].point.y) |
| log("WARNING: Moved in the wrong direction in step " + i + ": from (" + positions[i - 1].point.x + ", " + positions[i - 1].point.y + ") to (" + positions[i].point.x + ", " + positions[i].point.y + ").\n"); |
| } |
| } |
| |
| function checkCoordinatesMovingLeftDown(positions) |
| { |
| for (var i = 1; i < positions.length; ++i) { |
| if (positions[i].point.y > positions[i - 1].point.y || positions[i].point.x > positions[i - 1].point.x && positions[i].point.y >= positions[i - 1].point.y) |
| log("WARNING: Moved in the wrong direction in step " + i + ": from (" + positions[i - 1].point.x + ", " + positions[i - 1].point.y + ") to (" + positions[i].point.x + ", " + positions[i].point.y + ").\n"); |
| } |
| } |
| |
| function checkCoordinatesMovingRightUp(positions) |
| { |
| for (var i = 1; i < positions.length; ++i) { |
| if (positions[i].point.y < positions[i - 1].point.y || positions[i].point.x < positions[i - 1].point.x && positions[i].point.y <= positions[i - 1].point.y) |
| log("WARNING: Moved in the wrong direction in step " + i + ": from (" + positions[i - 1].point.x + ", " + positions[i - 1].point.y + ") to (" + positions[i].point.x + ", " + positions[i].point.y + ").\n"); |
| } |
| } |
| |
| function checkCoordinatesMovingLeftUp(positions) |
| { |
| for (var i = 1; i < positions.length; ++i) { |
| if (positions[i].point.y < positions[i - 1].point.y || positions[i].point.x > positions[i - 1].point.x && positions[i].point.y <= positions[i - 1].point.y) |
| log("WARNING: Moved in the wrong direction in step " + i + ": from (" + positions[i - 1].point.x + ", " + positions[i - 1].point.y + ") to (" + positions[i].point.x + ", " + positions[i].point.y + ").\n"); |
| } |
| } |
| |
| function runTest() |
| { |
| if (window.internals) |
| internals.setContinuousSpellCheckingEnabled(false); // For performance. |
| |
| var tests = document.getElementsByClassName("test"); |
| var sel = getSelection(); |
| for (var i = 0; i < tests.length; ++i) { |
| var positionsMovingRight; |
| var positionsMovingLeft; |
| |
| log("Test " + (i + 1) + ", LTR:\n Moving right: "); |
| sel.setPosition(tests[i], 0); |
| positionsMovingRight = positionsMovingInDirection(sel, "right"); |
| logPositions(positionsMovingRight); |
| log("\n"); |
| checkCoordinatesMovingRightDown(positionsMovingRight); |
| |
| log(" Moving left: "); |
| positionsMovingLeft = positionsMovingInDirection(sel, "left"); |
| logPositions(positionsMovingLeft); |
| log("\n"); |
| checkCoordinatesMovingLeftUp(positionsMovingLeft); |
| |
| checkReverseOrder(positionsMovingLeft, positionsMovingRight); |
| |
| tests[i].style.direction = "rtl"; |
| |
| log("Test " + (i + 1) + ", RTL:\n Moving left: "); |
| sel.setPosition(tests[i], 0); |
| positionsMovingLeft = positionsMovingInDirection(sel, "left"); |
| logPositions(positionsMovingLeft); |
| log("\n"); |
| checkCoordinatesMovingLeftDown(positionsMovingLeft); |
| |
| log(" Moving right: "); |
| positionsMovingRight = positionsMovingInDirection(sel, "right"); |
| logPositions(positionsMovingRight); |
| log("\n"); |
| checkCoordinatesMovingRightUp(positionsMovingRight); |
| |
| checkReverseOrder(positionsMovingLeft, positionsMovingRight); |
| |
| } |
| document.getElementById("testGroup").style.display = "none"; |
| } |
| |
| onload = function() { |
| try { |
| runTest(); |
| } finally { |
| flushLog(); |
| } |
| }; |
| |
| if (window.testRunner) |
| testRunner.dumpAsText(); |
| </script> |
| </head> |
| <body> |
| <div id="testGroup"> |
| |
| <div class="test"> |
| abc |
| </div> |
| |
| <div class="test"> |
| אבג |
| </div> |
| |
| <div class="test"><br>abc |
| </div> |
| |
| <div class="test"><br>אבג |
| </div> |
| |
| <div class="test"> |
| abcאבגdef |
| </div> |
| |
| <div class="test"> |
| אבגabcדהו |
| </div> |
| |
| <div class="test"> |
| abcאבגדהו |
| </div> |
| |
| <div class="test"> |
| אבגabcdef |
| </div> |
| |
| <div class="test">١٢٣ابة</div> |
| |
| <div class="test">ابة١٢٣</div> |
| |
| <div class="test"> |
| <span>abc</span>אבגdef |
| </div> |
| |
| <div class="test"> |
| <span>אבג</span>abcדהו |
| </div> |
| |
| <div class="test">abcאבג123דהוdef |
| </div> |
| |
| <div class="test">abcאבג123 |
| </div> |
| |
| <div class="test">abcאבג123def |
| </div> |
| |
| <div class="test">אבג123דהוabcזחט456יכל |
| </div> |
| |
| <div class="test" style="width: 120px;"> |
| before אחרי אנציקלופדיה |
| </div> |
| |
| <div class="test" style="width: 120px;"> |
| לפני after encyclopedia |
| </div> |
| |
| <div class="test" contenteditable style="width: 120px;"> |
| before אחרי אנציקלופדיה |
| </div> |
| |
| <div class="test" contenteditable style="width: 120px;"> |
| לפני after encyclopedia |
| </div> |
| |
| <div class="test" style="width: 100px;"> |
| This is יותר צר מיתר the boxes. |
| </div> |
| |
| <div contenteditable class="test" style="width: 100px;"> |
| This is יותר צר מיתר the boxes. |
| </div> |
| |
| <div class="test"> |
| Lorem |
| <div></div> |
| ipsum |
| </div> |
| |
| <div class="test"> |
| צלחת |
| <div></div> |
| מצנפת |
| </div> |
| |
| <div class="test"> |
| abcdefאבג<img>דהו |
| </div> |
| |
| <div class="test"> |
| אבגדהוabc<img>def |
| </div> |
| |
| <div class="test"> |
| abc<input>אבג<img><img>דהוghi |
| </div> |
| |
| <div class="test"> |
| אבג<input>abc<img><img>defדהו |
| </div> |
| |
| <div class="test"> |
| abcאבג<span>דהו</span> |
| </div> |
| |
| <div class="test"> |
| אבגabc<span>def</span> |
| </div> |
| |
| <div class="test"> |
| ab<span>cאבגdef</span> |
| </div> |
| |
| <div class="test"> |
| אב<span>גabcדהו</span> |
| </div> |
| |
| <div class="test"> |
| abc<span>אבגdef</span> |
| </div> |
| |
| <div class="test"> |
| אבג<span>abcדהו</span> |
| </div> |
| |
| <div class="test"> |
| abcאdef |
| </div> |
| |
| <div class="test"> |
| אבגaדהו |
| </div> |
| |
| <div class="test"> |
| abcאבג<span>def</span> |
| </div> |
| |
| <div class="test"> |
| אבגabc<span>דהו</span> |
| </div> |
| |
| <div class="test"> |
| abcא<span>בגdef</span> |
| </div> |
| |
| <div class="test"> |
| אבגa<span>bcדהו</span> |
| </div> |
| |
| <div class="test" style="white-space: pre;">abc<!-- --> |
| <!-- -->def</div> |
| |
| <div class="test" style="white-space: pre;">אבג<!-- --> |
| <!-- -->דהו</div> |
| |
| <div class="test"> |
| <span dir="rtl">abcקקק123נננdef</span> |
| </div> |
| |
| </div> |
| |
| <pre id="console"></pre> |
| </body> |