1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * @fileoverview Low-level DOM traversal utility functions to find the
7 * next (or previous) character, word, sentence, line, or paragraph,
8 * in a completely stateless manner without actually manipulating the
12 goog
.provide('cvox.TraverseUtil');
14 goog
.require('cvox.Cursor');
15 goog
.require('cvox.DomPredicates');
16 goog
.require('cvox.DomUtil');
19 * Utility functions for stateless DOM traversal.
22 cvox
.TraverseUtil = function() {};
25 * Gets the text representation of a node. This allows us to substitute
26 * alt text, names, or titles for html elements that provide them.
27 * @param {Node} node A DOM node.
28 * @return {string} A text string representation of the node.
30 cvox
.TraverseUtil
.getNodeText = function(node
) {
31 if (node
.constructor == Text
) {
39 * Return true if a node should be treated as a leaf node, because
40 * its children are properties of the object that shouldn't be traversed.
42 * TODO(dmazzoni): replace this with a predicate that detects nodes with
43 * ARIA roles and other objects that have their own description.
44 * For now we just detect a couple of common cases.
46 * @param {Node} node A DOM node.
47 * @return {boolean} True if the node should be treated as a leaf node.
49 cvox
.TraverseUtil
.treatAsLeafNode = function(node
) {
50 return node
.childNodes
.length
== 0 ||
51 node
.nodeName
== 'SELECT' ||
52 node
.getAttribute('role') == 'listbox' ||
53 node
.nodeName
== 'OBJECT';
57 * Return true only if a single character is whitespace.
58 * From https://developer.mozilla.org/en/Whitespace_in_the_DOM,
59 * whitespace is defined as one of the characters
65 * @param {string} c A string containing a single character.
66 * @return {boolean} True if the character is whitespace, otherwise false.
68 cvox
.TraverseUtil
.isWhitespace = function(c
) {
69 return (c
== ' ' || c
== '\n' || c
== '\r' || c
== '\t');
73 * Set the selection to the range between the given start and end cursors.
74 * @param {cvox.Cursor} start The desired start of the selection.
75 * @param {cvox.Cursor} end The desired end of the selection.
76 * @return {Selection} the selection object.
78 cvox
.TraverseUtil
.setSelection = function(start
, end
) {
79 var sel
= window
.getSelection();
80 sel
.removeAllRanges();
81 var range
= document
.createRange();
82 range
.setStart(start
.node
, start
.index
);
83 range
.setEnd(end
.node
, end
.index
);
89 // TODO(dtseng): Combine with cvox.DomUtil.hasContent.
91 * Check if this DOM node has the attribute aria-hidden='true', which should
92 * hide it from screen readers.
93 * @param {Node} node An HTML DOM node.
94 * @return {boolean} Whether or not the html node should be traversed.
96 cvox
.TraverseUtil
.isHidden = function(node
) {
97 if (node
instanceof HTMLElement
&&
98 node
.getAttribute('aria-hidden') == 'true') {
101 switch (node
.tagName
) {
110 * Moves the cursor forwards until it has crossed exactly one character.
111 * @param {cvox.Cursor} cursor The cursor location where the search should
112 * start. On exit, the cursor will be immediately to the right of the
113 * character returned.
114 * @param {Array<Element>} elementsEntered Any HTML elements entered.
115 * @param {Array<Element>} elementsLeft Any HTML elements left.
116 * @return {?string} The character found, or null if the bottom of the
117 * document has been reached.
119 cvox
.TraverseUtil
.forwardsChar = function(
120 cursor
, elementsEntered
, elementsLeft
) {
122 // Move down until we get to a leaf node.
123 var childNode
= null;
124 if (!cvox
.TraverseUtil
.treatAsLeafNode(cursor
.node
)) {
125 for (var i
= cursor
.index
; i
< cursor
.node
.childNodes
.length
; i
++) {
126 var node
= cursor
.node
.childNodes
[i
];
127 if (cvox
.TraverseUtil
.isHidden(node
)) {
128 if (node
instanceof HTMLElement
) {
129 elementsEntered
.push(node
);
133 if (cvox
.DomUtil
.isVisible(node
, {checkAncestors
: false})) {
140 cursor
.node
= childNode
;
142 cursor
.text
= cvox
.TraverseUtil
.getNodeText(cursor
.node
);
143 if (cursor
.node
instanceof HTMLElement
) {
144 elementsEntered
.push(cursor
.node
);
149 // Return the next character from this leaf node.
150 if (cursor
.index
< cursor
.text
.length
)
151 return cursor
.text
[cursor
.index
++];
153 // Move to the next sibling, going up the tree as necessary.
154 while (cursor
.node
!= null) {
155 // Try to move to the next sibling.
156 var siblingNode
= null;
157 for (var node
= cursor
.node
.nextSibling
;
159 node
= node
.nextSibling
) {
160 if (cvox
.TraverseUtil
.isHidden(node
)) {
161 if (node
instanceof HTMLElement
) {
162 elementsEntered
.push(node
);
166 if (cvox
.DomUtil
.isVisible(node
, {checkAncestors
: false})) {
172 if (cursor
.node
instanceof HTMLElement
) {
173 elementsLeft
.push(cursor
.node
);
176 cursor
.node
= siblingNode
;
177 cursor
.text
= cvox
.TraverseUtil
.getNodeText(siblingNode
);
180 if (cursor
.node
instanceof HTMLElement
) {
181 elementsEntered
.push(cursor
.node
);
187 // Otherwise, move to the parent.
188 if (cursor
.node
.parentNode
&&
189 cursor
.node
.parentNode
.constructor != HTMLBodyElement
) {
190 if (cursor
.node
instanceof HTMLElement
) {
191 elementsLeft
.push(cursor
.node
);
193 cursor
.node
= cursor
.node
.parentNode
;
204 * Moves the cursor backwards until it has crossed exactly one character.
205 * @param {cvox.Cursor} cursor The cursor location where the search should
206 * start. On exit, the cursor will be immediately to the left of the
207 * character returned.
208 * @param {Array<Element>} elementsEntered Any HTML elements entered.
209 * @param {Array<Element>} elementsLeft Any HTML elements left.
210 * @return {?string} The previous character, or null if the top of the
211 * document has been reached.
213 cvox
.TraverseUtil
.backwardsChar = function(
214 cursor
, elementsEntered
, elementsLeft
) {
216 // Move down until we get to a leaf node.
217 var childNode
= null;
218 if (!cvox
.TraverseUtil
.treatAsLeafNode(cursor
.node
)) {
219 for (var i
= cursor
.index
- 1; i
>= 0; i
--) {
220 var node
= cursor
.node
.childNodes
[i
];
221 if (cvox
.TraverseUtil
.isHidden(node
)) {
222 if (node
instanceof HTMLElement
) {
223 elementsEntered
.push(node
);
227 if (cvox
.DomUtil
.isVisible(node
, {checkAncestors
: false})) {
234 cursor
.node
= childNode
;
235 cursor
.text
= cvox
.TraverseUtil
.getNodeText(cursor
.node
);
236 if (cursor
.text
.length
)
237 cursor
.index
= cursor
.text
.length
;
239 cursor
.index
= cursor
.node
.childNodes
.length
;
240 if (cursor
.node
instanceof HTMLElement
) {
241 elementsEntered
.push(cursor
.node
);
246 // Return the previous character from this leaf node.
247 if (cursor
.text
.length
> 0 && cursor
.index
> 0) {
248 return cursor
.text
[--cursor
.index
];
251 // Move to the previous sibling, going up the tree as necessary.
253 // Try to move to the previous sibling.
254 var siblingNode
= null;
255 for (var node
= cursor
.node
.previousSibling
;
257 node
= node
.previousSibling
) {
258 if (cvox
.TraverseUtil
.isHidden(node
)) {
259 if (node
instanceof HTMLElement
) {
260 elementsEntered
.push(node
);
264 if (cvox
.DomUtil
.isVisible(node
, {checkAncestors
: false})) {
270 if (cursor
.node
instanceof HTMLElement
) {
271 elementsLeft
.push(cursor
.node
);
274 cursor
.node
= siblingNode
;
275 cursor
.text
= cvox
.TraverseUtil
.getNodeText(siblingNode
);
276 if (cursor
.text
.length
)
277 cursor
.index
= cursor
.text
.length
;
279 cursor
.index
= cursor
.node
.childNodes
.length
;
281 if (cursor
.node
instanceof HTMLElement
) {
282 elementsEntered
.push(cursor
.node
);
287 // Otherwise, move to the parent.
288 if (cursor
.node
.parentNode
&&
289 cursor
.node
.parentNode
.constructor != HTMLBodyElement
) {
290 if (cursor
.node
instanceof HTMLElement
) {
291 elementsLeft
.push(cursor
.node
);
293 cursor
.node
= cursor
.node
.parentNode
;
304 * Finds the next character, starting from endCursor. Upon exit, startCursor
305 * and endCursor will surround the next character. If skipWhitespace is
306 * true, will skip until a real character is found. Otherwise, it will
307 * attempt to select all of the whitespace between the initial position
308 * of endCursor and the next non-whitespace character.
309 * @param {!cvox.Cursor} startCursor On exit, points to the position before
311 * @param {!cvox.Cursor} endCursor The position to start searching for the next
312 * char. On exit, will point to the position past the char.
313 * @param {Array<Element>} elementsEntered Any HTML elements entered.
314 * @param {Array<Element>} elementsLeft Any HTML elements left.
315 * initial and final cursor position will be pushed onto this array.
316 * @param {boolean} skipWhitespace If true, will keep scanning until a
317 * non-whitespace character is found.
318 * @return {?string} The next char, or null if the bottom of the
319 * document has been reached.
321 cvox
.TraverseUtil
.getNextChar = function(
322 startCursor
, endCursor
, elementsEntered
, elementsLeft
, skipWhitespace
) {
324 // Save the starting position and get the first character.
325 startCursor
.copyFrom(endCursor
);
326 var c
= cvox
.TraverseUtil
.forwardsChar(
327 endCursor
, elementsEntered
, elementsLeft
);
331 // Keep track of whether the first character was whitespace.
332 var initialWhitespace
= cvox
.TraverseUtil
.isWhitespace(c
);
334 // Keep scanning until we find a non-whitespace or non-skipped character.
335 while ((cvox
.TraverseUtil
.isWhitespace(c
)) ||
336 (cvox
.TraverseUtil
.isHidden(endCursor
.node
))) {
337 c
= cvox
.TraverseUtil
.forwardsChar(
338 endCursor
, elementsEntered
, elementsLeft
);
342 if (skipWhitespace
|| !initialWhitespace
) {
343 // If skipWhitepace is true, or if the first character we encountered
344 // was not whitespace, return that non-whitespace character.
345 startCursor
.copyFrom(endCursor
);
350 for (var i
= 0; i
< elementsEntered
.length
; i
++) {
351 if (cvox
.TraverseUtil
.isHidden(elementsEntered
[i
])) {
352 // We need to make sure that startCursor and endCursor aren't
353 // surrounding a skippable node.
355 startCursor
.copyFrom(endCursor
);
360 // Otherwise, return all of the whitespace before that last character.
367 * Finds the previous character, starting from startCursor. Upon exit,
368 * startCursor and endCursor will surround the previous character.
369 * If skipWhitespace is true, will skip until a real character is found.
370 * Otherwise, it will attempt to select all of the whitespace between
371 * the initial position of endCursor and the next non-whitespace character.
372 * @param {!cvox.Cursor} startCursor The position to start searching for the
373 * char. On exit, will point to the position before the char.
374 * @param {!cvox.Cursor} endCursor The position to start searching for the next
375 * char. On exit, will point to the position past the char.
376 * @param {Array<Element>} elementsEntered Any HTML elements entered.
377 * @param {Array<Element>} elementsLeft Any HTML elements left.
378 * initial and final cursor position will be pushed onto this array.
379 * @param {boolean} skipWhitespace If true, will keep scanning until a
380 * non-whitespace character is found.
381 * @return {?string} The previous char, or null if the top of the
382 * document has been reached.
384 cvox
.TraverseUtil
.getPreviousChar = function(
385 startCursor
, endCursor
, elementsEntered
, elementsLeft
, skipWhitespace
) {
387 // Save the starting position and get the first character.
388 endCursor
.copyFrom(startCursor
);
389 var c
= cvox
.TraverseUtil
.backwardsChar(
390 startCursor
, elementsEntered
, elementsLeft
);
394 // Keep track of whether the first character was whitespace.
395 var initialWhitespace
= cvox
.TraverseUtil
.isWhitespace(c
);
397 // Keep scanning until we find a non-whitespace or non-skipped character.
398 while ((cvox
.TraverseUtil
.isWhitespace(c
)) ||
399 (cvox
.TraverseUtil
.isHidden(startCursor
.node
))) {
400 c
= cvox
.TraverseUtil
.backwardsChar(
401 startCursor
, elementsEntered
, elementsLeft
);
405 if (skipWhitespace
|| !initialWhitespace
) {
406 // If skipWhitepace is true, or if the first character we encountered
407 // was not whitespace, return that non-whitespace character.
408 endCursor
.copyFrom(startCursor
);
412 for (var i
= 0; i
< elementsEntered
.length
; i
++) {
413 if (cvox
.TraverseUtil
.isHidden(elementsEntered
[i
])) {
415 endCursor
.copyFrom(startCursor
);
420 // Otherwise, return all of the whitespace before that last character.
427 * Finds the next word, starting from endCursor. Upon exit, startCursor
428 * and endCursor will surround the next word. A word is defined to be
429 * a string of 1 or more non-whitespace characters in the same DOM node.
430 * @param {cvox.Cursor} startCursor On exit, will point to the beginning of the
432 * @param {cvox.Cursor} endCursor The position to start searching for the next
433 * word. On exit, will point to the end of the word returned.
434 * @param {Array<Element>} elementsEntered Any HTML elements entered.
435 * @param {Array<Element>} elementsLeft Any HTML elements left.
436 * @return {?string} The next word, or null if the bottom of the
437 * document has been reached.
439 cvox
.TraverseUtil
.getNextWord = function(startCursor
, endCursor
,
440 elementsEntered
, elementsLeft
) {
442 // Find the first non-whitespace or non-skipped character.
443 var cursor
= endCursor
.clone();
444 var c
= cvox
.TraverseUtil
.forwardsChar(cursor
, elementsEntered
, elementsLeft
);
447 while ((cvox
.TraverseUtil
.isWhitespace(c
)) ||
448 (cvox
.TraverseUtil
.isHidden(cursor
.node
))) {
449 c
= cvox
.TraverseUtil
.forwardsChar(cursor
, elementsEntered
, elementsLeft
);
454 // Set startCursor to the position immediately before the first
455 // character in our word. It's safe to decrement |index| because
456 // forwardsChar guarantees that the cursor will be immediately to the
457 // right of the returned character on exit.
458 startCursor
.copyFrom(cursor
);
461 // Keep building up our word until we reach a whitespace character or
462 // would cross a tag. Don't actually return any tags crossed, because this
463 // word goes up until the tag boundary but not past it.
464 endCursor
.copyFrom(cursor
);
468 c
= cvox
.TraverseUtil
.forwardsChar(cursor
, newEntered
, newLeft
);
472 while (!cvox
.TraverseUtil
.isWhitespace(c
) &&
473 newEntered
.length
== 0 &&
476 endCursor
.copyFrom(cursor
);
477 c
= cvox
.TraverseUtil
.forwardsChar(cursor
, newEntered
, newLeft
);
487 * Finds the previous word, starting from startCursor. Upon exit, startCursor
488 * and endCursor will surround the previous word. A word is defined to be
489 * a string of 1 or more non-whitespace characters in the same DOM node.
490 * @param {cvox.Cursor} startCursor The position to start searching for the
491 * previous word. On exit, will point to the beginning of the
493 * @param {cvox.Cursor} endCursor On exit, will point to the end of the
495 * @param {Array<Element>} elementsEntered Any HTML elements entered.
496 * @param {Array<Element>} elementsLeft Any HTML elements left.
497 * @return {?string} The previous word, or null if the bottom of the
498 * document has been reached.
500 cvox
.TraverseUtil
.getPreviousWord = function(startCursor
, endCursor
,
501 elementsEntered
, elementsLeft
) {
502 // Find the first non-whitespace or non-skipped character.
503 var cursor
= startCursor
.clone();
504 var c
= cvox
.TraverseUtil
.backwardsChar(
505 cursor
, elementsEntered
, elementsLeft
);
508 while ((cvox
.TraverseUtil
.isWhitespace(c
) ||
509 (cvox
.TraverseUtil
.isHidden(cursor
.node
)))) {
510 c
= cvox
.TraverseUtil
.backwardsChar(cursor
, elementsEntered
, elementsLeft
);
515 // Set endCursor to the position immediately after the first
516 // character we've found (the last character of the word, since we're
517 // searching backwards).
518 endCursor
.copyFrom(cursor
);
521 // Keep building up our word until we reach a whitespace character or
522 // would cross a tag. Don't actually return any tags crossed, because this
523 // word goes up until the tag boundary but not past it.
524 startCursor
.copyFrom(cursor
);
528 c
= cvox
.TraverseUtil
.backwardsChar(cursor
, newEntered
, newLeft
);
531 while (!cvox
.TraverseUtil
.isWhitespace(c
) &&
532 newEntered
.length
== 0 &&
533 newLeft
.length
== 0) {
535 startCursor
.copyFrom(cursor
);
537 c
= cvox
.TraverseUtil
.backwardsChar(cursor
, newEntered
, newLeft
);
547 * Given elements entered and left, and break tags, returns true if the
548 * current word should break.
549 * @param {Array<Element>} elementsEntered Any HTML elements entered.
550 * @param {Array<Element>} elementsLeft Any HTML elements left.
551 * @param {Object<boolean>} breakTags Associative array of tags that should
553 * @return {boolean} True if elementsEntered or elementsLeft include an
554 * element with one of these tags.
556 cvox
.TraverseUtil
.includesBreakTagOrSkippedNode = function(
557 elementsEntered
, elementsLeft
, breakTags
) {
558 for (var i
= 0; i
< elementsEntered
.length
; i
++) {
559 if (cvox
.TraverseUtil
.isHidden(elementsEntered
[i
])) {
562 var style
= window
.getComputedStyle(elementsEntered
[i
], null);
563 if ((style
&& style
.display
!= 'inline') ||
564 breakTags
[elementsEntered
[i
].tagName
]) {
568 for (i
= 0; i
< elementsLeft
.length
; i
++) {
569 var style
= window
.getComputedStyle(elementsLeft
[i
], null);
570 if ((style
&& style
.display
!= 'inline') ||
571 breakTags
[elementsLeft
[i
].tagName
]) {
580 * Finds the next sentence, starting from endCursor. Upon exit,
581 * startCursor and endCursor will surround the next sentence.
583 * @param {cvox.Cursor} startCursor On exit, marks the beginning of the
585 * @param {cvox.Cursor} endCursor The position to start searching for the next
586 * sentence. On exit, will point to the end of the returned string.
587 * @param {Array<Element>} elementsEntered Any HTML elements entered.
588 * @param {Array<Element>} elementsLeft Any HTML elements left.
589 * @param {Object<boolean>} breakTags Associative array of tags that should
590 * break the sentence.
591 * @return {?string} The next sentence, or null if the bottom of the
592 * document has been reached.
594 cvox
.TraverseUtil
.getNextSentence = function(
595 startCursor
, endCursor
, elementsEntered
, elementsLeft
, breakTags
) {
596 return cvox
.TraverseUtil
.getNextString(
597 startCursor
, endCursor
, elementsEntered
, elementsLeft
,
598 function(str
, word
, elementsEntered
, elementsLeft
) {
599 if (str
.substr(-1) == '.')
601 return cvox
.TraverseUtil
.includesBreakTagOrSkippedNode(
602 elementsEntered
, elementsLeft
, breakTags
);
607 * Finds the previous sentence, starting from startCursor. Upon exit,
608 * startCursor and endCursor will surround the previous sentence.
610 * @param {cvox.Cursor} startCursor The position to start searching for the next
611 * sentence. On exit, will point to the start of the returned string.
612 * @param {cvox.Cursor} endCursor On exit, the end of the returned string.
613 * @param {Array<Element>} elementsEntered Any HTML elements entered.
614 * @param {Array<Element>} elementsLeft Any HTML elements left.
615 * @param {Object<boolean>} breakTags Associative array of tags that should
616 * break the sentence.
617 * @return {?string} The previous sentence, or null if the bottom of the
618 * document has been reached.
620 cvox
.TraverseUtil
.getPreviousSentence = function(
621 startCursor
, endCursor
, elementsEntered
, elementsLeft
, breakTags
) {
622 return cvox
.TraverseUtil
.getPreviousString(
623 startCursor
, endCursor
, elementsEntered
, elementsLeft
,
624 function(str
, word
, elementsEntered
, elementsLeft
) {
625 if (word
.substr(-1) == '.')
627 return cvox
.TraverseUtil
.includesBreakTagOrSkippedNode(
628 elementsEntered
, elementsLeft
, breakTags
);
633 * Finds the next line, starting from endCursor. Upon exit,
634 * startCursor and endCursor will surround the next line.
636 * @param {cvox.Cursor} startCursor On exit, marks the beginning of the line.
637 * @param {cvox.Cursor} endCursor The position to start searching for the next
638 * line. On exit, will point to the end of the returned string.
639 * @param {Array<Element>} elementsEntered Any HTML elements entered.
640 * @param {Array<Element>} elementsLeft Any HTML elements left.
641 * @param {Object<boolean>} breakTags Associative array of tags that should
643 * @return {?string} The next line, or null if the bottom of the
644 * document has been reached.
646 cvox
.TraverseUtil
.getNextLine = function(
647 startCursor
, endCursor
, elementsEntered
, elementsLeft
, breakTags
) {
648 var range
= document
.createRange();
649 var currentRect
= null;
650 var rightMostRect
= null;
651 var prevCursor
= endCursor
.clone();
652 return cvox
.TraverseUtil
.getNextString(
653 startCursor
, endCursor
, elementsEntered
, elementsLeft
,
654 function(str
, word
, elementsEntered
, elementsLeft
) {
655 range
.setStart(startCursor
.node
, startCursor
.index
);
656 range
.setEnd(endCursor
.node
, endCursor
.index
);
657 var currentRect
= range
.getBoundingClientRect();
658 if (!rightMostRect
) {
659 rightMostRect
= currentRect
;
662 // Break at new lines except when within a link.
663 if (currentRect
.bottom
!= rightMostRect
.bottom
&&
664 !cvox
.DomPredicates
.linkPredicate(cvox
.DomUtil
.getAncestors(
666 endCursor
.copyFrom(prevCursor
);
670 rightMostRect
= currentRect
;
671 prevCursor
.copyFrom(endCursor
);
673 return cvox
.TraverseUtil
.includesBreakTagOrSkippedNode(
674 elementsEntered
, elementsLeft
, breakTags
);
679 * Finds the previous line, starting from startCursor. Upon exit,
680 * startCursor and endCursor will surround the previous line.
682 * @param {cvox.Cursor} startCursor The position to start searching for the next
683 * line. On exit, will point to the start of the returned string.
684 * @param {cvox.Cursor} endCursor On exit, the end of the returned string.
685 * @param {Array<Element>} elementsEntered Any HTML elements entered.
686 * @param {Array<Element>} elementsLeft Any HTML elements left.
687 * @param {Object<boolean>} breakTags Associative array of tags that should
689 * @return {?string} The previous line, or null if the bottom of the
690 * document has been reached.
692 cvox
.TraverseUtil
.getPreviousLine = function(
693 startCursor
, endCursor
, elementsEntered
, elementsLeft
, breakTags
) {
694 var range
= document
.createRange();
695 var currentRect
= null;
696 var leftMostRect
= null;
697 var prevCursor
= startCursor
.clone();
698 return cvox
.TraverseUtil
.getPreviousString(
699 startCursor
, endCursor
, elementsEntered
, elementsLeft
,
700 function(str
, word
, elementsEntered
, elementsLeft
) {
701 range
.setStart(startCursor
.node
, startCursor
.index
);
702 range
.setEnd(endCursor
.node
, endCursor
.index
);
703 var currentRect
= range
.getBoundingClientRect();
705 leftMostRect
= currentRect
;
708 // Break at new lines except when within a link.
709 if (currentRect
.top
!= leftMostRect
.top
&&
710 !cvox
.DomPredicates
.linkPredicate(cvox
.DomUtil
.getAncestors(
711 startCursor
.node
))) {
712 startCursor
.copyFrom(prevCursor
);
716 leftMostRect
= currentRect
;
717 prevCursor
.copyFrom(startCursor
);
719 return cvox
.TraverseUtil
.includesBreakTagOrSkippedNode(
720 elementsEntered
, elementsLeft
, breakTags
);
725 * Finds the next paragraph, starting from endCursor. Upon exit,
726 * startCursor and endCursor will surround the next paragraph.
728 * @param {cvox.Cursor} startCursor On exit, marks the beginning of the
730 * @param {cvox.Cursor} endCursor The position to start searching for the next
731 * paragraph. On exit, will point to the end of the returned string.
732 * @param {Array<Element>} elementsEntered Any HTML elements entered.
733 * @param {Array<Element>} elementsLeft Any HTML elements left.
734 * @return {?string} The next paragraph, or null if the bottom of the
735 * document has been reached.
737 cvox
.TraverseUtil
.getNextParagraph = function(startCursor
, endCursor
,
738 elementsEntered
, elementsLeft
) {
739 return cvox
.TraverseUtil
.getNextString(
740 startCursor
, endCursor
, elementsEntered
, elementsLeft
,
741 function(str
, word
, elementsEntered
, elementsLeft
) {
742 for (var i
= 0; i
< elementsEntered
.length
; i
++) {
743 if (cvox
.TraverseUtil
.isHidden(elementsEntered
[i
])) {
746 var style
= window
.getComputedStyle(elementsEntered
[i
], null);
747 if (style
&& style
.display
!= 'inline') {
751 for (i
= 0; i
< elementsLeft
.length
; i
++) {
752 var style
= window
.getComputedStyle(elementsLeft
[i
], null);
753 if (style
&& style
.display
!= 'inline') {
762 * Finds the previous paragraph, starting from startCursor. Upon exit,
763 * startCursor and endCursor will surround the previous paragraph.
765 * @param {cvox.Cursor} startCursor The position to start searching for the next
766 * paragraph. On exit, will point to the start of the returned string.
767 * @param {cvox.Cursor} endCursor On exit, the end of the returned string.
768 * @param {Array<Element>} elementsEntered Any HTML elements entered.
769 * @param {Array<Element>} elementsLeft Any HTML elements left.
770 * @return {?string} The previous paragraph, or null if the bottom of the
771 * document has been reached.
773 cvox
.TraverseUtil
.getPreviousParagraph = function(
774 startCursor
, endCursor
, elementsEntered
, elementsLeft
) {
775 return cvox
.TraverseUtil
.getPreviousString(
776 startCursor
, endCursor
, elementsEntered
, elementsLeft
,
777 function(str
, word
, elementsEntered
, elementsLeft
) {
778 for (var i
= 0; i
< elementsEntered
.length
; i
++) {
779 if (cvox
.TraverseUtil
.isHidden(elementsEntered
[i
])) {
782 var style
= window
.getComputedStyle(elementsEntered
[i
], null);
783 if (style
&& style
.display
!= 'inline') {
787 for (i
= 0; i
< elementsLeft
.length
; i
++) {
788 var style
= window
.getComputedStyle(elementsLeft
[i
], null);
789 if (style
&& style
.display
!= 'inline') {
798 * Customizable function to return the next string of words in the DOM, based
799 * on provided functions to decide when to break one string and start
800 * the next. This can be used to get the next sentence, line, paragraph,
801 * or potentially other granularities.
803 * Finds the next contiguous string, starting from endCursor. Upon exit,
804 * startCursor and endCursor will surround the next string.
806 * The breakBefore function takes four parameters, and
807 * should return true if the string should be broken before the proposed
809 * str The string so far.
810 * word The next word to be added.
811 * elementsEntered The elements entered in reaching this next word.
812 * elementsLeft The elements left in reaching this next word.
814 * @param {cvox.Cursor} startCursor On exit, will point to the beginning of the
816 * @param {cvox.Cursor} endCursor The position to start searching for the next
817 * string. On exit, will point to the end of the returned string.
818 * @param {Array<Element>} elementsEntered Any HTML elements entered.
819 * @param {Array<Element>} elementsLeft Any HTML elements left.
820 * @param {function(string, string, Array<Element>, Array<Element>)}
821 * breakBefore Function that takes the string so far, next word to be
822 * added, and elements entered and left, and returns true if the string
823 * should be ended before adding this word.
824 * @return {?string} The next string, or null if the bottom of the
825 * document has been reached.
827 cvox
.TraverseUtil
.getNextString = function(
828 startCursor
, endCursor
, elementsEntered
, elementsLeft
, breakBefore
) {
829 // Get the first word and set the start cursor to the start of the
831 var wordStartCursor
= endCursor
.clone();
832 var wordEndCursor
= endCursor
.clone();
836 var word
= cvox
.TraverseUtil
.getNextWord(
837 wordStartCursor
, wordEndCursor
, newEntered
, newLeft
);
840 startCursor
.copyFrom(wordStartCursor
);
842 // Always add the first word when the string is empty, and then keep
843 // adding more words as long as breakBefore returns false
844 while (!str
|| !breakBefore(str
, word
, newEntered
, newLeft
)) {
845 // Append this word, set the end cursor to the end of this word, and
846 // update the returned list of nodes crossed to include ones we crossed
847 // in reaching this word.
851 elementsEntered
= elementsEntered
.concat(newEntered
);
852 elementsLeft
= elementsLeft
.concat(newLeft
);
853 endCursor
.copyFrom(wordEndCursor
);
855 // Get the next word and go back to the top of the loop.
858 word
= cvox
.TraverseUtil
.getNextWord(
859 wordStartCursor
, wordEndCursor
, newEntered
, newLeft
);
868 * Customizable function to return the previous string of words in the DOM,
869 * based on provided functions to decide when to break one string and start
870 * the next. See getNextString, above, for more details.
872 * Finds the previous contiguous string, starting from startCursor. Upon exit,
873 * startCursor and endCursor will surround the next string.
875 * @param {cvox.Cursor} startCursor The position to start searching for the
876 * previous string. On exit, will point to the beginning of the
878 * @param {cvox.Cursor} endCursor On exit, will point to the end of the
880 * @param {Array<Element>} elementsEntered Any HTML elements entered.
881 * @param {Array<Element>} elementsLeft Any HTML elements left.
882 * @param {function(string, string, Array<Element>, Array<Element>)}
883 * breakBefore Function that takes the string so far, the word to be
884 * added, and nodes crossed, and returns true if the string should be
885 * ended before adding this word.
886 * @return {?string} The next string, or null if the top of the
887 * document has been reached.
889 cvox
.TraverseUtil
.getPreviousString = function(
890 startCursor
, endCursor
, elementsEntered
, elementsLeft
, breakBefore
) {
891 // Get the first word and set the end cursor to the end of the
893 var wordStartCursor
= startCursor
.clone();
894 var wordEndCursor
= startCursor
.clone();
898 var word
= cvox
.TraverseUtil
.getPreviousWord(
899 wordStartCursor
, wordEndCursor
, newEntered
, newLeft
);
902 endCursor
.copyFrom(wordEndCursor
);
904 // Always add the first word when the string is empty, and then keep
905 // adding more words as long as breakBefore returns false
906 while (!str
|| !breakBefore(str
, word
, newEntered
, newLeft
)) {
907 // Prepend this word, set the start cursor to the start of this word, and
908 // update the returned list of nodes crossed to include ones we crossed
909 // in reaching this word.
913 elementsEntered
= elementsEntered
.concat(newEntered
);
914 elementsLeft
= elementsLeft
.concat(newLeft
);
915 startCursor
.copyFrom(wordStartCursor
);
917 // Get the previous word and go back to the top of the loop.
920 word
= cvox
.TraverseUtil
.getPreviousWord(
921 wordStartCursor
, wordEndCursor
, newEntered
, newLeft
);