/WebVox/res/raw/basic.user.js

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