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. 4 5 /** 6 * @fileoverview Utilities for finding DOM nodes and CursorSelection's. 7 */ 8 9 10 goog.provide('cvox.FindUtil'); 11 12 goog.require('cvox.BareObjectWalker'); 13 goog.require('cvox.CursorSelection'); 14 15 16 /** 17 * @type {!cvox.BareObjectWalker} 18 * @private 19 */ 20 cvox.FindUtil.objectWalker_ = new cvox.BareObjectWalker(); 21 22 23 /** 24 * Finds the next selection that matches the predicate function starting from 25 * sel. Undefined if the nodes in sel are not attached to the document. 26 * @param {!cvox.CursorSelection} sel The selection from which to start. 27 * @param {function(Array.<Node>):Node} predicate A function taking a 28 * unique ancestor tree and outputting Node if the ancestor tree matches 29 * the desired node to find. 30 * @param {boolean=} opt_initialNode Whether to start the search from node 31 * (true), or the next node (false); defaults to false. 32 * @return {cvox.CursorSelection} The selection that was found. 33 * null if end of document reached. 34 */ 35 cvox.FindUtil.findNext = function(sel, predicate, opt_initialNode) { 36 var r = sel.isReversed(); 37 var cur = new cvox.CursorSelection(sel.absStart(), sel.absStart()) 38 .setReversed(r); 39 40 // We may have been sync'ed into a subtree of the current predicate match. 41 // Find our ancestor that matches the predicate. 42 var ancestor; 43 if (ancestor = predicate(cvox.DomUtil.getAncestors(cur.start.node))) { 44 cur = cvox.CursorSelection.fromNode(ancestor).setReversed(r); 45 if (opt_initialNode) { 46 return cur; 47 } 48 } 49 50 while (cur) { 51 // Use ObjectWalker's traversal which guarantees us a stable iteration of 52 // the DOM including returning null at page bounds. 53 cur = cvox.FindUtil.objectWalker_.next(cur); 54 var retNode = null; 55 if (!cur || 56 (retNode = predicate(cvox.DomUtil.getAncestors(cur.start.node)))) { 57 return retNode ? cvox.CursorSelection.fromNode(retNode) : null; 58 } 59 60 // Iframes require inter-frame messaging. 61 if (cur.start.node.tagName == 'IFRAME') { 62 return cur; 63 } 64 } 65 return null; 66 }; 67