/WebVox/res/raw/basic.user.js
JavaScript | 363 lines | 323 code | 23 blank | 17 comment | 122 complexity | 3ba988c3877b79cc1269747bd8c05a6f MD5 | raw file
1// ==UserScript== 2// @name CVOX 3// @description Generic CVOX Script 4// @author Charles L. Chen 5// @include http://* 6// ==/UserScript== 7 8var currentElem = null; 9var prevElem = null; 10var inputFocused = false; 11 12function getNextLeafNode(){ 13 prevElem = currentElem; 14 if (currentElem == null){ 15 currentElem = document.body; 16 } else { 17 while (!currentElem.nextSibling){ 18 currentElem = currentElem.parentNode; 19 if (currentElem == document.body){ 20 currentElem = null; 21 return currentElem; 22 } 23 } 24 currentElem = currentElem.nextSibling; 25 } 26 while (currentElem.firstChild){ 27 currentElem = currentElem.firstChild; 28 } 29 return currentElem; 30} 31 32function getPrevLeafNode(){ 33 prevElem = currentElem; 34 if (currentElem == null){ 35 currentElem = document.body; 36 } else { 37 while (!currentElem.previousSibling){ 38 currentElem = currentElem.parentNode; 39 if (currentElem == document.body){ 40 currentElem = null; 41 return currentElem; 42 } 43 } 44 currentElem = currentElem.previousSibling; 45 } 46 while (currentElem.lastChild){ 47 currentElem = currentElem.lastChild; 48 } 49 return currentElem; 50} 51 52function containsText(textStr){ 53 if (textStr === null){ 54 return false; 55 } 56 if (textStr.length < 1){ 57 return false; 58 } 59 var myregexp = new RegExp("[a-zA-Z0-9]"); 60 return myregexp.test(textStr); 61} 62 63function getLineage(targetNode){ 64 var lineage = new Array(); 65 while (targetNode){ 66 lineage.push(targetNode); 67 targetNode = targetNode.parentNode; 68 } 69 lineage.reverse(); 70 while(lineage.length && !lineage[0].tagName && !lineage[0].nodeValue){ 71 lineage.shift(); 72 } 73 return lineage; 74} 75 76//Compares Lineage A with Lineage B and returns 77//the index value in B at which B diverges from A. 78//If there is no divergence, the result will be -1. 79//Note that if B is the same as A except B has more nodes 80//even after A has ended, that is considered a divergence. 81//The first node that B has which A does not have will 82//be treated as the divergence point. 83// 84function compareLineages(lina, linb){ 85 var i = 0; 86 while( lina[i] && linb[i] && (lina[i] == linb[i]) ){ 87 i++; 88 } 89 if ( !lina[i] && !linb[i] ){ 90 i = -1; 91 } 92 return i; 93} 94 95function hasTagInLineage(elem, tag){ 96 var node = elem; 97 while(node){ 98 if (node.tagName && node.tagName == tag){ 99 return true; 100 } 101 node = node.parentNode; 102 } 103 return false; 104} 105 106function getText(elem){ 107 if (elem.tagName && elem.tagName == 'IMG'){ 108 return elem.alt; 109 } 110 return elem.textContent; 111} 112 113var readingMode = 1; // 0 = detailed, 1 = skim, 2 = headings only 114 115function isSkippable(elem){ 116 if (hasTagInLineage(elem, 'SCRIPT')){ 117 return true; 118 } 119 if (readingMode === 0){ 120 return false; 121 } else if (readingMode == 1){ 122 if (hasTagInLineage(elem, 'P')){ 123 return false; 124 } 125 if (hasTagInLineage(elem, 'H1')){ 126 return false; 127 } 128 if (hasTagInLineage(elem, 'H2')){ 129 return false; 130 } 131 if (hasTagInLineage(elem, 'H3')){ 132 return false; 133 } 134 if (hasTagInLineage(elem, 'H4')){ 135 return false; 136 } 137 if (hasTagInLineage(elem, 'H5')){ 138 return false; 139 } 140 if (hasTagInLineage(elem, 'H6')){ 141 return false; 142 } 143 if (hasTagInLineage(elem, 'OL')){ 144 return false; 145 } 146 if (hasTagInLineage(elem, 'UL')){ 147 return false; 148 } 149 if (hasTagInLineage(elem, 'DL')){ 150 return false; 151 } 152 if (hasTagInLineage(elem, 'BLOCKQUOTE')){ 153 return false; 154 } 155 if (hasTagInLineage(elem, 'DIV')){ 156 return false; 157 } 158 return true; 159 } else if (readingMode == 2) { 160 if (hasTagInLineage(elem, 'H1')){ 161 return false; 162 } 163 if (hasTagInLineage(elem, 'H2')){ 164 return false; 165 } 166 if (hasTagInLineage(elem, 'H3')){ 167 return false; 168 } 169 if (hasTagInLineage(elem, 'H4')){ 170 return false; 171 } 172 if (hasTagInLineage(elem, 'H5')){ 173 return false; 174 } 175 if (hasTagInLineage(elem, 'H6')){ 176 return false; 177 } 178 return true; 179 } 180} 181 182function getInfoOnCurrentElem(){ 183 var currentLineage = getLineage(currentElem); 184 var divergence = compareLineages(getLineage(prevElem), currentLineage); 185 var infoStr = ""; 186 for (var i=divergence, elem; elem = currentLineage[i]; i++){ 187 if (elem.tagName){ 188 if (elem.tagName == 'H1'){ 189 infoStr = infoStr + 'H 1. '; 190 } else if (elem.tagName == 'H2'){ 191 infoStr = infoStr + 'H 2. '; 192 } else if (elem.tagName == 'H3'){ 193 infoStr = infoStr + 'H 3. '; 194 } else if (elem.tagName == 'H4'){ 195 infoStr = infoStr + 'H 4. '; 196 } else if (elem.tagName == 'H5'){ 197 infoStr = infoStr + 'H 5. '; 198 } else if (elem.tagName == 'H6'){ 199 infoStr = infoStr + 'H 6. '; 200 } else if (elem.tagName == 'A'){ 201 infoStr = infoStr + 'Link. '; 202 } 203 } 204 } 205 return infoStr; 206} 207 208function readNext(){ 209 getNextLeafNode(); 210 var textContent = getText(currentElem); 211 while (!containsText(textContent)){ 212 getNextLeafNode(); 213 if (currentElem === null){ 214 speak("End of document", 0, null); 215 return false; 216 } 217 textContent = getText(currentElem); 218 } 219 if (isSkippable(currentElem)){ 220 return readNext(); 221 } else { 222 speak(getInfoOnCurrentElem() + ' ' + textContent, 0, null); 223 scrollToElem(currentElem); 224 return true; 225 } 226} 227 228function readPrev(){ 229 getPrevLeafNode(); 230 var textContent = getText(currentElem); 231 while (!containsText(textContent)){ 232 getPrevLeafNode(); 233 if (currentElem === null){ 234 speak("Beginning of document", 0, null); 235 return false; 236 } 237 textContent = getText(currentElem); 238 } 239 if (isSkippable(currentElem)){ 240 return readPrev(); 241 } else { 242 speak(getInfoOnCurrentElem() + ' ' + textContent, 0, null); 243 scrollToElem(currentElem); 244 return true; 245 } 246} 247 248function scrollToElem(targetNode){ 249 while (!targetNode.offsetParent){ 250 targetNode = targetNode.parentNode; 251 } 252 var left = 0; 253 var top = 0; 254 while (targetNode.offsetParent) { 255 left += targetNode.offsetLeft; 256 top += targetNode.offsetTop; 257 targetNode = targetNode.offsetParent; 258 } 259 left += targetNode.offsetLeft; 260 top += targetNode.offsetTop; 261 window.scrollTo(left, top); 262} 263 264function speak(textStr, queueMode, paramsArray){ 265 window.ttsHelper.speak(textStr, queueMode); 266 //alert(textStr); 267} 268 269function isSpeaking(){ 270 return window.ttsHelper.isSpeaking(); 271} 272 273var keepGoing = true; 274 275function autoRead(){ 276 if (!isSpeaking()){ 277 keepGoing = readNext(); 278 } 279 if (keepGoing){ 280 window.setTimeout(autoRead, 1000); 281 } 282} 283 284//TODO: Come up with a working escape sequence! 285function keyPressHandler(evt){ 286 var keyCode = evt.keyCode; 287 if (inputFocused){ 288 return true; 289 } 290 if (keyCode == 97) { // a 291 keepGoing = true; 292 autoRead(); 293 return false; 294 } 295 if (keyCode == 106) { // j 296 readNext(); 297 return false; 298 } 299 if (keyCode == 107) { // k 300 readPrev(); 301 return false; 302 } 303 if (keyCode == 104) { // h 304 readingMode++; 305 if (readingMode > 2){ 306 readingMode = 0; 307 } 308 if (readingMode == 0){ 309 speak("All", 0, null); 310 } else if (readingMode == 1){ 311 speak("Quick", 0, null); 312 } else if (readingMode == 2){ 313 speak("Headings", 0, null); 314 } 315 return false; 316 } 317 return true; 318} 319 320function keyDownHandler(evt){ 321 keepGoing = false; 322 window.ttsHelper.stop(); 323 var keyCode = evt.keyCode; 324 if (inputFocused){ 325 return true; 326 } 327 if ((keyCode == 32) || (keyCode == 13)) { // space or Enter (G1 enter key does not work) 328 var targetElem = currentElem; 329 while (targetElem){ 330 if (targetElem.tagName == 'A'){ 331 document.location = targetElem.href; 332 return false; 333 } 334 targetElem = targetElem.parentNode; 335 } 336 return true; 337 } 338 return true; 339} 340 341function focusHandler(evt){ 342 if (evt.target.tagName && 343 evt.target.tagName == 'INPUT'){ 344 inputFocused = true; 345 } 346 return true; 347} 348 349function blurHandler(evt){ 350 inputFocused = false; 351 return true; 352} 353 354if (document.location.toString().indexOf('http://www.google.com/m?') === 0){ 355 // Do nothing, rely on mgws script 356} else 357if (document.location.toString().indexOf('http://www.google.com/search?') != 0){ 358 document.addEventListener('keypress', keyPressHandler, true); 359 document.addEventListener('keydown', keyDownHandler, true); 360 document.addEventListener('focus', focusHandler, true); 361 document.addEventListener('blur', blurHandler, true); 362 speak(document.title, 0, null); 363}