ViewerA11YHelper.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. /*
  2. *+------------------------------------------------------------------------+
  3. *| Licensed Materials - Property of IBM
  4. *| IBM Cognos Products: Viewer
  5. *| (C) Copyright IBM Corp. 2001, 2014
  6. *|
  7. *| US Government Users Restricted Rights - Use, duplication or
  8. *| disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  9. *|
  10. *+------------------------------------------------------------------------+
  11. */
  12. function ViewerA11YHelper(oCV) {
  13. this.m_oCV = oCV;
  14. }
  15. ViewerA11YHelper.prototype.onFocus = function(evt) {
  16. var targetNode = getCrossBrowserNode(evt);
  17. targetNode = ViewerA11YHelper.findChildOfTableCell(targetNode);
  18. this.updateCellAccessibility(targetNode, false);
  19. };
  20. ViewerA11YHelper.prototype.onKeyDown = function(evt) {
  21. //get the event in a cross-browser fashion
  22. evt = (evt) ? evt : ((event) ? event : null);
  23. var srcNode = getCrossBrowserNode(evt);
  24. // In IE, if the user clicked on the white space instead of the text in a cell, the srcNode
  25. // will point to the TD or TH. Get the span within the TD or TH
  26. if (ViewerA11YHelper.isTableCell(srcNode)) {
  27. for (var i=0; i < srcNode.childNodes.length; i++) {
  28. if (srcNode.childNodes[i].nodeName.toLowerCase() == "span") {
  29. srcNode = srcNode.childNodes[i];
  30. break;
  31. }
  32. }
  33. }
  34. // if the event didn't come from an element we'd select then let it bubble up
  35. if (!this.isValidNodeToSelect(srcNode)) {
  36. return true;
  37. }
  38. srcNode = ViewerA11YHelper.findChildOfTableCell(srcNode);
  39. if (srcNode) {
  40. if (evt.keyCode == "39") { // right arrow
  41. if (this.m_oCV.getState() && this.m_oCV.getState().getFindState() && evt.ctrlKey && evt.shiftKey ) { // Ctrl+Shilf+ right arrow
  42. this.m_oCV.executeAction("FindNext");
  43. } else {
  44. this.moveRight(srcNode);
  45. }
  46. return stopEventBubble(evt);
  47. }
  48. else if (evt.keyCode == "37") { // left arrow
  49. this.moveLeft(srcNode);
  50. return stopEventBubble(evt);
  51. }
  52. else if (evt.keyCode == "38") { // up arrow
  53. this.moveUp(srcNode);
  54. return stopEventBubble(evt);
  55. }
  56. else if (evt.keyCode == "40") { // down arrow
  57. this.moveDown(srcNode);
  58. return stopEventBubble(evt);
  59. }
  60. else if (evt.keyCode == "13") { // enter
  61. if (this.m_oCV.isBux) {
  62. if( this.m_oCV.getViewerWidget().isSelectionFilterEnabled() ){
  63. this.m_oCV.getViewerWidget().preprocessPageClicked( false /*invokingContextMenu*/, evt);
  64. if( this.m_oCV.getSelectionController().pageClicked(evt) !== false ){
  65. this.m_oCV.JAWSTalk( RV_RES.IDS_JS_SELECTION_FILTER_INFO_JAWS );
  66. this.m_oCV.getViewerWidget().updateToolbar();
  67. }
  68. } else {
  69. this.m_oCV.getSelectionController().pageClicked(evt);
  70. var selectionAction = this.m_oCV.getActionFactory().load("Selection");
  71. selectionAction.onKeyDown(evt);
  72. }
  73. this.m_oCV.getViewerWidget().onSelectionChange();
  74. } else {
  75. this.m_oCV.de(evt);
  76. }
  77. }
  78. else if (evt.keyCode == "32") { // space
  79. if( this.m_oCV.isBux )
  80. {
  81. this.m_oCV.getViewerWidget().preprocessPageClicked( false /*invokingContextMenu*/);
  82. if( this.m_oCV.getSelectionController().pageClicked(evt) !== false && this.m_oCV.getViewerWidget().isSelectionFilterEnabled() )
  83. {
  84. this.m_oCV.JAWSTalk( RV_RES.IDS_JS_SELECTION_FILTER_INFO_JAWS );
  85. }
  86. this.m_oCV.getViewerWidget().updateToolbar();
  87. this.m_oCV.getViewerWidget().onSelectionChange();
  88. } else {
  89. this.m_oCV.getSelectionController().pageClicked(evt);
  90. }
  91. return stopEventBubble(evt);
  92. }
  93. else if(evt.keyCode == "46" && this.m_oCV.isBux) { // delete key
  94. if (typeof this.m_oCV.envParams != "undefined" &&
  95. typeof this.m_oCV.envParams["ui.action"] != "undefined" &&
  96. this.m_oCV.envParams["ui.action"] != "view" &&
  97. !this.m_oCV.isLimitedInteractiveMode())
  98. {
  99. var deleteAction = this.m_oCV.getActionFactory().load("Delete");
  100. if(!this.m_oCV.isBlacklisted("Delete") && deleteAction.canDelete())
  101. {
  102. deleteAction.execute();
  103. return stopEventBubble(evt);
  104. }
  105. }
  106. }
  107. else if (this.m_oCV.isBux && evt.ctrlKey == true && evt.shiftKey == true && evt.keyCode == "49") { // Ctrl + shift + !
  108. var lid = this.m_oCV.getSelectionController().getSelectionObjectFactory().getLayoutElementId(srcNode);
  109. if (lid != "") {
  110. // get the lid without the Viewer id appended to it.
  111. lid = lid.split(this.m_oCV.getId())[0];
  112. var containerIdx = -1;
  113. var oRAPReportInfo = this.m_oCV.getRAPReportInfo();
  114. if (oRAPReportInfo) {
  115. var container = oRAPReportInfo.getContainer(lid);
  116. if (typeof container.layoutIndex != "undefined") {
  117. containerIdx = container.layoutIndex;
  118. }
  119. }
  120. var infoBarHeaderButton = document.getElementById("infoBarHeaderButton" + containerIdx + this.m_oCV.getId());
  121. if (infoBarHeaderButton !== null) {
  122. this.m_oCV.setCurrentNodeFocus(getCrossBrowserNode(evt));
  123. infoBarHeaderButton.focus();
  124. }
  125. }
  126. return stopEventBubble(evt);
  127. }
  128. else if (!this.m_oCV.isBux && evt.shiftKey == true && evt.keyCode == "121") { // Shift - F10 (context menu) -- standalone viewer
  129. //Only cover Shift + F10. Menu button is already covered by the body's onContextMenu callback.
  130. var ocv = this.m_oCV;
  131. var openContextMenu = function() {
  132. //Some browsers don't populate the evt.clientX/Y fields on keyboard
  133. //events, which the display content menu function requires.
  134. if (typeof evt.clientX == "undefined" || typeof evt.clientY == "undefined") {
  135. var coords = clientToScreenCoords(evt.target, document.body);
  136. evt.clientX = coords.leftCoord;
  137. evt.clientY = coords.topCoord;
  138. }
  139. ocv.dcm(evt, true);
  140. };
  141. if(isFF()) {
  142. //In FF, need to invoke this code after this thread's execution is done.
  143. setTimeout(openContextMenu, 0);
  144. } else {
  145. //Other browsers, should invoke right away
  146. //(Espcially IE, where evt's state is cleared after)
  147. openContextMenu.call();
  148. }
  149. //Swallow event so browser's context menu is not shown
  150. return stopEventBubble(evt);
  151. }
  152. else if (this.m_oCV.isBux && (evt.keyCode == "93" || (evt.shiftKey == true && evt.keyCode == "121"))) { // Shift - F10 / menu button (context menu) -- BUX
  153. var viewerWidget = this.m_oCV.getViewerWidget();
  154. var selectionController = this.m_oCV.getSelectionController();
  155. viewerWidget.preprocessPageClicked( true /*invokingContextMenu*/);
  156. selectionController.pageClicked(evt);
  157. viewerWidget.updateToolbar();
  158. viewerWidget.onContextMenu(evt);
  159. }
  160. }
  161. };
  162. ViewerA11YHelper.prototype.isValidNodeToSelect = function(node) {
  163. return this.getValidNodeToSelect(node) ? true : false;
  164. };
  165. ViewerA11YHelper.prototype.getValidNodeToSelect = function(node) {
  166. if (node && node.style && node.style.visibility != "hidden" && node.style.display != "none") {
  167. var sNodeName = node.nodeName.toLowerCase();
  168. if (
  169. (sNodeName == "span" && (!node.getAttribute("class") || node.getAttribute("class").indexOf("expandButton") === -1)) ||
  170. (sNodeName == "div" && node.getAttribute("flashchartcontainer") == "true") ||
  171. (sNodeName == "div" && node.getAttribute("chartcontainer") == "true") ||
  172. (sNodeName == "img" && (!node.id || node.id.indexOf("sortimg") !== 0 ))
  173. ) {
  174. return node;
  175. }
  176. if (ViewerA11YHelper.isSemanticNode(node)) {
  177. var child = node.childNodes && node.childNodes.length ? node.childNodes[0] : null;
  178. if(child) {
  179. return this.getValidNodeToSelect(child);
  180. }
  181. }
  182. }
  183. return null;
  184. };
  185. ViewerA11YHelper.isSemanticNode = function(node) {
  186. if(!ViewerA11YHelper.isSemanticNode._semanticNodeNames) {
  187. ViewerA11YHelper.isSemanticNode._semanticNodeNames = [
  188. "strong", "em", "h1", "h2", "h3", "h4", "h5", "h6"
  189. ];
  190. }
  191. var sNodeName = node.nodeName.toLowerCase();
  192. for(var i = 0; i < ViewerA11YHelper.isSemanticNode._semanticNodeNames.length; i++) {
  193. if(sNodeName === ViewerA11YHelper.isSemanticNode._semanticNodeNames[i]) {
  194. return true;
  195. }
  196. }
  197. return false;
  198. };
  199. ViewerA11YHelper.isTableCell = function(node) {
  200. var sNodeName = node.nodeName.toLowerCase();
  201. return sNodeName === "td" || sNodeName === "th";
  202. };
  203. ViewerA11YHelper.findChildOfTableCell = function(startNode) {
  204. var srcNode = startNode;
  205. while(srcNode && srcNode.parentNode) {
  206. if (ViewerA11YHelper.getTableCell(srcNode)) {
  207. break;
  208. }
  209. srcNode = srcNode.parentNode;
  210. }
  211. return srcNode;
  212. };
  213. ViewerA11YHelper.getTableCell = function(node) {
  214. var parent = node.parentNode;
  215. if(ViewerA11YHelper.isTableCell(parent)) {
  216. return parent;
  217. }
  218. //Treat a semantic node under the <td> as parent of the <td>
  219. if (ViewerA11YHelper.isSemanticNode(parent) && ViewerA11YHelper.isTableCell(parent.parentNode)) {
  220. return parent.parentNode;
  221. }
  222. return null;
  223. };
  224. ViewerA11YHelper.prototype.moveRight = function(srcNode) {
  225. var nextNode = this.getNextNonTextSibling(srcNode);
  226. nextNode = this.getValidNodeToSelect(nextNode);
  227. // case where we have multiple spans inside a td
  228. if (nextNode) {
  229. this.setFocusToNode(nextNode);
  230. return true;
  231. }
  232. var tdNode = ViewerA11YHelper.getTableCell(srcNode);
  233. tdNode = this.getPfMainOutputCell(tdNode);
  234. while (tdNode.nextSibling) {
  235. if (this.moveToTD(tdNode.nextSibling)) {
  236. return true;
  237. }
  238. tdNode = tdNode.nextSibling;
  239. }
  240. var trNode = tdNode.parentNode;
  241. while (trNode.nextSibling) {
  242. var nextTR = trNode.nextSibling;
  243. if (this.moveToTD(nextTR.childNodes[0])) {
  244. return true;
  245. }
  246. trNode = trNode.nextSibling;
  247. }
  248. return false;
  249. };
  250. ViewerA11YHelper.prototype.moveLeft = function(srcNode) {
  251. var previousNode = this.getPreviousNonTextSibling(srcNode);
  252. previousNode = this.getValidNodeToSelect(previousNode);
  253. // case where we have multiple spans inside a td
  254. if (previousNode) {
  255. this.setFocusToNode(previousNode);
  256. return true;
  257. }
  258. var tdNode = ViewerA11YHelper.getTableCell(srcNode);
  259. tdNode = this.getPfMainOutputCell(tdNode);
  260. while (tdNode.previousSibling) {
  261. if (this.moveToTDFromTheRight(tdNode.previousSibling)) {
  262. return true;
  263. }
  264. tdNode = tdNode.previousSibling;
  265. }
  266. var trNode = tdNode.parentNode;
  267. while (trNode.previousSibling) {
  268. var previousTR = trNode.previousSibling;
  269. if (this.moveToTDFromTheRight(previousTR.lastChild)) {
  270. return true;
  271. }
  272. trNode = trNode.previousSibling;
  273. }
  274. return false;
  275. };
  276. ViewerA11YHelper.prototype.moveDown = function(srcNode) {
  277. var tdNode = ViewerA11YHelper.getTableCell(srcNode);
  278. tdNode = this.getPfMainOutputCell(tdNode);
  279. var srcColSpan = this.getColumnIndex(tdNode);
  280. srcColSpan += this.getColSpanFromRowSpans(tdNode);
  281. // if the current node has a rowSpan, we need to jump over a bunch of TR's
  282. var trNode = tdNode.parentNode;
  283. if (tdNode.rowSpan && tdNode.rowSpan > 1) {
  284. var nodeRowSpan = tdNode.rowSpan;
  285. for (var rowSpanIndex=1; rowSpanIndex < nodeRowSpan; rowSpanIndex++) {
  286. trNode = trNode.nextSibling;
  287. }
  288. }
  289. var bTriedNextColumn = false;
  290. while(trNode) {
  291. if (trNode.nextSibling) { // get the next TR
  292. trNode = trNode.nextSibling;
  293. } else if (tdNode.nextSibling && !bTriedNextColumn) { // move to the next column
  294. trNode = trNode.parentNode.firstChild;
  295. bTriedNextColumn = true;
  296. srcColSpan++;
  297. } else { // last span is selected
  298. return false;
  299. }
  300. if (this.doMoveUpDown(trNode, srcColSpan)) {
  301. return true;
  302. }
  303. }
  304. return false;
  305. };
  306. ViewerA11YHelper.prototype.moveUp = function(srcNode) {
  307. var tdNode = ViewerA11YHelper.getTableCell(srcNode);
  308. tdNode = this.getPfMainOutputCell(tdNode);
  309. var trNode = tdNode.parentNode;
  310. var srcColSpan = this.getColumnIndex(tdNode);
  311. srcColSpan += this.getColSpanFromRowSpans(tdNode);
  312. var bTriedPreviousColumn = false;
  313. while(trNode) {
  314. if (trNode.previousSibling) { // get the next TR
  315. trNode = trNode.previousSibling;
  316. } else if (tdNode.previousSibling && !bTriedPreviousColumn) { // move to the next column
  317. trNode = trNode.parentNode.lastChild;
  318. bTriedPreviousColumn = true;
  319. srcColSpan--;
  320. } else { // last span is selected
  321. return false;
  322. }
  323. if (this.doMoveUpDown(trNode, srcColSpan)) {
  324. return true;
  325. }
  326. }
  327. return false;
  328. };
  329. ViewerA11YHelper.prototype.getNextNonTextSibling = function(node) {
  330. while (node.nextSibling) {
  331. node = node.nextSibling;
  332. if (node.nodeName.toLowerCase() != '#text') {
  333. return node;
  334. }
  335. }
  336. if (ViewerA11YHelper.isSemanticNode(node.parentNode)) {
  337. return this.getNextNonTextSibling(node.parentNode);
  338. }
  339. return null;
  340. };
  341. ViewerA11YHelper.prototype.doMoveUpDown = function(trNode, srcColSpan) {
  342. if (trNode != null) {
  343. var currentColumn = trNode.firstChild;
  344. var pos = this.getColSpanFromRowSpans(currentColumn);
  345. while (currentColumn) {
  346. if (pos == srcColSpan) {
  347. return this.moveToTDFromTheRight(currentColumn);
  348. } else if (pos > srcColSpan) {
  349. break;
  350. }
  351. var nodeColSpan = 0;
  352. if (currentColumn.colSpan) {
  353. nodeColSpan = currentColumn.colSpan;
  354. } else {
  355. nodeColSpan++;
  356. }
  357. pos += nodeColSpan;
  358. currentColumn = currentColumn.nextSibling;
  359. }
  360. }
  361. };
  362. ViewerA11YHelper.prototype.moveToTDFromTheRight = function(td) {
  363. td = this.getPfVisibleCell(td);
  364. var childNodes = td.childNodes;
  365. for (var iChildIndex=childNodes.length-1; iChildIndex >= 0; iChildIndex--) {
  366. var node = this.getValidNodeToSelect(childNodes[iChildIndex]);
  367. if (node) {
  368. // sometimes have a span inside a span
  369. if (node.childNodes && node.childNodes[0] && node.childNodes[0].nodeName.toLowerCase() == "span") {
  370. node = node.childNodes[0];
  371. }
  372. if (node.tabIndex != -1 && node.tabIndex != 0) {
  373. node.tabIndex = -1;
  374. }
  375. this.setFocusToNode(node);
  376. return true;
  377. }
  378. }
  379. return false;
  380. };
  381. ViewerA11YHelper.prototype.moveToTD = function(td) {
  382. td = this.getPfVisibleCell(td);
  383. var childNodes = td.childNodes;
  384. for (var iChildIndex=0; iChildIndex < childNodes.length; iChildIndex++) {
  385. var node = this.getValidNodeToSelect(childNodes[iChildIndex]);
  386. if (node) {
  387. // sometimes have a span inside a span
  388. if (node.childNodes && node.childNodes[0] && node.childNodes[0].nodeName.toLowerCase() == "span") {
  389. node = node.childNodes[0];
  390. }
  391. if (node.tabIndex != -1 && node.tabIndex != 0) {
  392. node.tabIndex = -1;
  393. }
  394. this.setFocusToNode(node);
  395. return true;
  396. }
  397. }
  398. return false;
  399. };
  400. ViewerA11YHelper.prototype.setFocusToNode = function(node) {
  401. this.m_oCV.setCurrentNodeFocus(node);
  402. this.updateCellAccessibility(node, false);
  403. node.focus();
  404. if(this.m_oCV.m_pinFreezeManager) {
  405. var container = this.m_oCV.m_pinFreezeManager.nodeToContainer(node);
  406. if(container) {
  407. container.updateScroll(node);
  408. }
  409. }
  410. };
  411. /**
  412. * Given an element, return the main it is based on. This may be itself.
  413. */
  414. ViewerA11YHelper.prototype.getPfMainOutputCell = function(element) {
  415. var main = null
  416. var slid = element.getAttribute("pfslid");
  417. if(slid) {
  418. var lid = PinFreezeContainer.getLidFromSlid(slid);
  419. if(lid && this.m_oCV.m_pinFreezeManager) {
  420. lid = this.m_oCV.m_pinFreezeManager.removeNamespace(lid);
  421. var container = this.m_oCV.m_pinFreezeManager.getContainer(lid);
  422. if(container) {
  423. main = container.getMain(element);
  424. }
  425. }
  426. }
  427. return main ? main : element;
  428. };
  429. ViewerA11YHelper.prototype.getPreviousNonTextSibling = function(node) {
  430. while (node.previousSibling) {
  431. node = node.previousSibling;
  432. if (node.nodeName.toLowerCase() != '#text') {
  433. return node;
  434. }
  435. }
  436. if (ViewerA11YHelper.isSemanticNode(node.parentNode)) {
  437. return this.getPreviousNonTextSibling(node.parentNode);
  438. }
  439. return null;
  440. };
  441. /**
  442. * Returns the column index of the node with all the colspans.
  443. * This function excludes any td's that have a rowspan
  444. */
  445. ViewerA11YHelper.prototype.getColumnIndex = function(node) {
  446. var colIndex = 0;
  447. while (node.previousSibling) {
  448. node = node.previousSibling;
  449. if (node.rowSpan == 1) {
  450. if (node.colSpan) {
  451. colIndex += node.colSpan;
  452. } else {
  453. colIndex++;
  454. }
  455. }
  456. }
  457. return colIndex;
  458. };
  459. /**
  460. * Given an element, return the visible copy of it. This may be itself.
  461. */
  462. ViewerA11YHelper.prototype.getPfVisibleCell = function(element) {
  463. var copy = null;
  464. var slid = element.getAttribute("pfslid");
  465. if(slid) {
  466. var lid = PinFreezeContainer.getLidFromSlid(slid);
  467. if(lid && this.m_oCV.m_pinFreezeManager) {
  468. lid = this.m_oCV.m_pinFreezeManager.removeNamespace(lid);
  469. var container = this.m_oCV.m_pinFreezeManager.getContainer(lid);
  470. if(container) {
  471. copy = container.getCopy(element);
  472. }
  473. }
  474. }
  475. return copy ? copy : element;
  476. };
  477. ViewerA11YHelper.prototype.updateCellAccessibility = function(srcNode, force) {
  478. if (!srcNode) {
  479. return false;
  480. }
  481. var canDrillDown = false;
  482. var canDrillUp = false;
  483. var canDrillThrough = false;
  484. var ctxNode = srcNode.getAttribute("ctx") != null ? srcNode : srcNode.parentNode;
  485. if (srcNode.getAttribute("flashChartContainer") != "true") {
  486. if (ctxNode.getAttribute("ctx") != null) {
  487. if (this.m_oCV.isBux) {
  488. var action = this.m_oCV.getAction("DrillUpDown");
  489. action.updateDrillability(this.m_oCV, ctxNode);
  490. canDrillDown = action.canDrillDown();
  491. canDrillUp = action.canDrillUp();
  492. } else {
  493. var ctxAttribute = ctxNode.getAttribute("ctx");
  494. var ctxID = ctxAttribute.indexOf(':') == -1 ? ctxAttribute : ctxAttribute.substring(0, ctxAttribute.indexOf(":"));
  495. var selCon = this.m_oCV.getSelectionController();
  496. canDrillDown = selCon.canDrillDown(ctxID);
  497. canDrillUp = selCon.canDrillUp(ctxID);
  498. }
  499. }
  500. canDrillThrough = srcNode.parentNode.getAttribute("dtTargets") ? true : false;
  501. }
  502. var isImage = srcNode.nodeName.toLowerCase() == "img";
  503. var isColumnTitle = srcNode.parentNode.getAttribute("type") == "columnTitle";
  504. if ( !isImage && (force || ((srcNode.getAttribute("aria-labelledby") != null || isColumnTitle || this.m_oCV.isAccessibleMode())))) {
  505. var innerHTML = "";
  506. // crosstab corner
  507. if (srcNode.parentNode.getAttribute("cc") == "true") {
  508. innerHTML += " " + RV_RES.IDS_JS_CROSSTAB_CORNER;
  509. }
  510. if (srcNode.innerHTML.length === 0) {
  511. srcNode.innerHTML = "&nbsp;";
  512. innerHTML += " " + RV_RES.IDS_JS_EMPTY_CELL;
  513. }
  514. if (canDrillDown && canDrillUp) {
  515. innerHTML += " " + RV_RES.IDS_JS_DRILL_DOWN_UP_JAWS;
  516. } else if (canDrillDown) {
  517. innerHTML += " " + RV_RES.IDS_JS_DRILL_DOWN_JAWS;
  518. } else if (canDrillUp) {
  519. innerHTML += " " + RV_RES.IDS_JS_DRILL_UP_JAWS;
  520. }
  521. if (canDrillThrough) {
  522. innerHTML += " " + RV_RES.IDS_JS_DRILL_THROUGH_JAWS;
  523. }
  524. if (srcNode.altText && srcNode.altText.length > 0) {
  525. innerHTML = srcNode.altText;
  526. } else if (srcNode.getAttribute("flashChartContainer") == "true") {
  527. innerHTML = RV_RES.IDS_JS_CHART_IMAGE;
  528. }
  529. if( this.m_oCV.isBux ) {
  530. var sibling = srcNode.previousSibling;
  531. if (sibling) {
  532. var wid = sibling.getAttribute("widgetid");
  533. if (wid && wid.indexOf("comment")) {
  534. innerHTML += " " + RV_RES.IDS_JS_ANNOTATION_JAWS;
  535. }
  536. }
  537. if (srcNode.getAttribute("rp_name") || srcNode.parentNode.getAttribute("rp_name")) {
  538. innerHTML += " " + RV_RES.IDS_JS_LABEL_HAS_BEEN_RENAMED;
  539. }
  540. if (srcNode.nextSibling && srcNode.nextSibling.getAttribute("class") == "sortIconVisible") {
  541. innerHTML += " " + srcNode.nextSibling.getAttribute("alt");
  542. }
  543. }
  544. // is there any extra information that JAWS needs to speak out
  545. if (innerHTML.length > 0) {
  546. this.addAriaLabelledByOnCell(srcNode, innerHTML);
  547. }
  548. }
  549. if (canDrillUp || canDrillDown || canDrillThrough) {
  550. this.addDrillAccessibilityAttributes(srcNode, canDrillThrough);
  551. }
  552. if(srcNode.attachEvent) {
  553. srcNode.attachEvent("onblur", this.onBlur);
  554. } else {
  555. srcNode.addEventListener("blur", this.onBlur, false);
  556. }
  557. if ((isIE() && srcNode.getAttribute("tabIndex") != 0) || isImage) {
  558. srcNode.setAttribute("modifiedTabIndex", "true");
  559. srcNode.setAttribute("oldTabIndex", srcNode.getAttribute("tabIndex"));
  560. srcNode.setAttribute("tabIndex", 0);
  561. }
  562. };
  563. ViewerA11YHelper.prototype.addAriaLabelledByOnCell = function(srcNode, labelledBy) {
  564. // can have multiple spans inside a td, get the position to help make the id unique
  565. var srcNodePos = 0;
  566. var tempNode = srcNode;
  567. while (tempNode.previousSibling) {
  568. srcNodePos++;
  569. tempNode = tempNode.previousSibling;
  570. }
  571. var hiddenSpanId = srcNode.getAttribute("ariaHiddenSpanId");
  572. // if we already have a hidden span, use it
  573. if (hiddenSpanId && document.getElementById(hiddenSpanId)) {
  574. document.getElementById(hiddenSpanId).innerHTML = labelledBy;
  575. }
  576. else {
  577. if (!srcNode.parentNode.id && !srcNode.id) {
  578. srcNode.parentNode.id = Math.random();
  579. }
  580. var newSpan = document.createElement("span");
  581. newSpan.style.visibility = "hidden";
  582. newSpan.style.display = "none";
  583. newSpan.id = (srcNode.id == "" ? srcNode.parentNode.id : srcNode.id) + "_" + srcNodePos;
  584. newSpan.innerHTML = labelledBy;
  585. srcNode.parentNode.appendChild(newSpan);
  586. var ariaLabelledBy = "";
  587. if (srcNode.getAttribute("aria-labelledby") != null) {
  588. ariaLabelledBy += srcNode.getAttribute("aria-labelledby");
  589. } else {
  590. if (srcNode.id == "") {
  591. srcNode.id = srcNode.parentNode.id + "_main_" + srcNodePos;
  592. }
  593. ariaLabelledBy += srcNode.id;
  594. }
  595. ariaLabelledBy += " " + newSpan.id;
  596. srcNode.setAttribute("aria-labelledby", ariaLabelledBy);
  597. srcNode.setAttribute("ariaHiddenSpanId", newSpan.id);
  598. }
  599. };
  600. ViewerA11YHelper.prototype.addDrillAccessibilityAttributes = function(srcNode, canDrillThrough) {
  601. if (!srcNode.getAttribute("oldClassName")) {
  602. // drill throughs already have a link
  603. if (!canDrillThrough) {
  604. srcNode.setAttribute("oldClassName", srcNode.className);
  605. srcNode.className = "dl " + srcNode.className;
  606. }
  607. if (!srcNode.getAttribute("role")) {
  608. srcNode.setAttribute("role", "link");
  609. }
  610. }
  611. };
  612. ViewerA11YHelper.prototype.onBlur = function(evt) {
  613. var srcNode = null;
  614. if(isIE()) {
  615. srcNode = getNodeFromEvent(evt, true);
  616. } else {
  617. srcNode = this;
  618. }
  619. srcNode = ViewerA11YHelper.findChildOfTableCell(srcNode);
  620. if (srcNode) {
  621. if (srcNode.getAttribute("oldClassName")) {
  622. srcNode.className = srcNode.getAttribute("oldClassName");
  623. srcNode.removeAttribute("oldClassName");
  624. }
  625. if (srcNode.getAttribute("modifiedTabIndex") == "true") {
  626. srcNode.removeAttribute("modifiedTabIndex");
  627. srcNode.removeAttribute("tabIndex");
  628. if (srcNode.getAttribute("oldTabIndex")) {
  629. srcNode.setAttribute("tabIndex", srcNode.getAttribute("oldTabIndex"));
  630. }
  631. srcNode.removeAttribute("oldTabIndex");
  632. }
  633. // blank out any extra info for JAWS when we leave the cell.
  634. var ariaSpanId = srcNode.getAttribute("ariaHiddenSpanId");
  635. if (ariaSpanId)
  636. {
  637. var ariaSpanEle = document.getElementById(ariaSpanId);
  638. if (ariaSpanEle)
  639. {
  640. ariaSpanEle.innerHTML = "";
  641. }
  642. }
  643. }
  644. };
  645. /**
  646. * Method that walks the tree from the given TD and calculates
  647. * the colspans that are from TD's with rowspan's on them
  648. */
  649. ViewerA11YHelper.prototype.getColSpanFromRowSpans = function(tdNode) {
  650. var nodeColSpan = 0;
  651. var trNode = tdNode.parentNode;
  652. var trChildCount = 0;
  653. while (trNode) {
  654. var rowChildNode = trNode.firstChild;
  655. // if there's a diff in the #of columns, we must of found a new TD with a rowspan
  656. var colCountDiff = this.getColumnCount(trNode) - trChildCount;
  657. while (rowChildNode && rowChildNode.rowSpan > 1 && colCountDiff > 0 && rowChildNode != tdNode) {
  658. nodeColSpan += rowChildNode.colSpan;
  659. rowChildNode = rowChildNode.nextSibling;
  660. colCountDiff--;
  661. }
  662. // never decrease the column count, only increase it
  663. if (trNode.childNodes.length > trChildCount) {
  664. trChildCount = this.getColumnCount(trNode);
  665. }
  666. // get the previous TR.. keep walking the DOM
  667. trNode = trNode.previousSibling;
  668. }
  669. return nodeColSpan;
  670. };
  671. /**
  672. * Gets the column count for the given TR which includes all the colspans
  673. */
  674. ViewerA11YHelper.prototype.getColumnCount = function(trNode) {
  675. var columnCount = 0;
  676. var node = trNode.firstChild;
  677. while (node) {
  678. columnCount += node.colSpan;
  679. node = node.nextSibling;
  680. }
  681. return columnCount;
  682. };
  683. /**
  684. * Sets up the aria labelledBy for cells outside of a data container (list/crosstab)
  685. */
  686. ViewerA11YHelper.prototype.addLabelledByForItemsOutsideOfContainers = function() {
  687. // Only needs to be done when accesibility preference is set
  688. if (!this.m_oCV.isAccessibleMode()) {
  689. return;
  690. }
  691. var content = document.getElementById("RVContent" + this.m_oCV.getId());
  692. if (!content) {
  693. return;
  694. }
  695. // Get all the spans that have a tabinex of 0. This should be a small list
  696. var focusableSpans = getElementsByAttribute(content, "span", "tabindex", "0");
  697. if (!focusableSpans) {
  698. return;
  699. }
  700. for (var i=0; i < focusableSpans.length; i++) {
  701. var span = focusableSpans[i];
  702. this.updateCellAccessibility(span, false);
  703. }
  704. };