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 Some utilities for defining what groups are. 7 */ 8 9 10 goog.provide('cvox.GroupUtil'); 11 12 goog.require('cvox.AriaUtil'); 13 goog.require('cvox.DomUtil'); 14 15 16 /** 17 * If a node contains more characters than this, it should not be treated 18 * as a leaf node by the smart navigation algorithm. 19 * 20 * This number was determined by looking at the average number of 21 * characters in a paragraph: 22 * http://www.fullondesign.co.uk/design/usability/ 23 * 285-how-many-characters-per-a-page-is-normal.htm 24 * and then trying it out on a few popular websites (CNN, BBC, 25 * Google Search, etc.) and making sure it made sense. 26 * @type {number} 27 * @private 28 * @const 29 */ 30 cvox.GroupUtil.MAX_CHARCOUNT_ = 1500; 31 32 33 /** 34 * If a node contains any of these elements, it should not be treated 35 * as a leaf node by the smart navigation algorithm. 36 * @type {string} 37 * @private 38 * @const 39 */ 40 cvox.GroupUtil.BREAKOUT_SELECTOR_ = 'blockquote,' + 41 'button,' + 42 'code,' + 43 'form,' + 44 'frame,' + 45 'h1,' + 46 'h2,' + 47 'h3,' + 48 'h4,' + 49 'h5,' + 50 'h6,' + 51 'hr,' + 52 'iframe,' + 53 'input,' + 54 'object,' + 55 'ol,' + 56 'p,' + 57 'pre,' + 58 'select,' + 59 'table,' + 60 'tr,' + 61 'ul,' + 62 'math,' + 63 // This takes care of MathJax expressions. 64 'span.math,' + 65 // TODO (sorge) Do we want to group all math or only display math? 66 // '[mode="display"],' + 67 // Aria widget roles 68 '[role~="alert ' + 69 'alertdialog ' + 70 'button ' + 71 'checkbox ' + 72 'combobox ' + 73 'dialog ' + 74 'log ' + 75 'marquee ' + 76 'menubar ' + 77 'progressbar ' + 78 'radio ' + 79 'radiogroup ' + 80 'scrollbar ' + 81 'slider ' + 82 'spinbutton ' + 83 'status ' + 84 'tab ' + 85 'tabpanel ' + 86 'textbox ' + 87 'toolbar ' + 88 'tooltip ' + 89 'treeitem ' + 90 // Aria structure roles 91 'article ' + 92 'document ' + 93 'group ' + 94 'heading ' + 95 'img ' + 96 'list ' + 97 'math ' + 98 'region ' + 99 'row ' + 100 'separator"]'; 101 102 103 /** 104 * Returns true if this is a leaf node for groups. 105 * true for a node => true for all child nodes 106 * true if node has no children 107 * @param {!Node} node The node to check. 108 * @return {boolean} true if this is at the "leaf node" level or lower 109 * for this granularity. 110 */ 111 cvox.GroupUtil.isLeafNode = function(node) { 112 // TODO (stoarca): Write test to make sure that this function satisfies 113 // the restriction given above. 114 if (node.tagName == 'LABEL') { 115 return cvox.DomUtil.isLeafNode(node); 116 } 117 if (cvox.DomUtil.isLeafNode(node)) { 118 return true; 119 } 120 121 if (!cvox.DomUtil.isSemanticElt(node)) { 122 var breakingNodes = node.querySelectorAll( 123 cvox.GroupUtil.BREAKOUT_SELECTOR_); 124 125 for (var i = 0; i < breakingNodes.length; ++i) { 126 if (cvox.DomUtil.hasContent(breakingNodes[i])) { 127 return false; 128 } 129 } 130 } 131 132 if (cvox.AriaUtil.isCompositeControl(node) && 133 !cvox.DomUtil.isFocusable(node)) { 134 return false; 135 } 136 137 var content = cvox.DomUtil.collapseWhitespace( 138 cvox.DomUtil.getValue(node) + ' ' + 139 cvox.DomUtil.getName(node)); 140 if (content.length > cvox.GroupUtil.MAX_CHARCOUNT_) { 141 return false; 142 } 143 144 if (content.replace(/\s/g, '') === '') { 145 // Text only contains whitespace 146 return false; 147 } 148 149 return true; 150 }; 151