CInnerTreeIOS.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. *+------------------------------------------------------------------------+
  3. *| Licensed Materials - Property of IBM
  4. *| BI and PM: prmt
  5. *| (C) Copyright IBM Corp. 2002, 2017
  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. //IOS TREE CONTROL: We subclass the tree class to handle the fact that iOS' Voiceover intercepts key events and doesn't support some aria attributes
  13. // oPane: the HTML element where the tree will be rendered
  14. // oSubmit: the form control to submit selections to the server
  15. // bRequired: is this a required field [true|false]
  16. // bMultiSelect: can the user pick more than one value [true|false]
  17. // sRef: the name of this object
  18. // oImgTest: the image object used for validation handling
  19. // oErrorFeedback: object used to provide validation feedback
  20. // sCVId
  21. // sStyle
  22. // sTextDir
  23. // sPromptName: the name of the prompt associated with the tree. This is used as a cache key prefix
  24. CInnerTreeIOS.prototype = new CInnerTree;
  25. function CInnerTreeIOS(oPane, oSubmit, bRequired, bMultiSelect, sRef, oImgTest, oErrorFeedback, sCVId, sStyle, sTextDir, sPromptName, oPromptControl)
  26. {
  27. CInnerTree.call(this, oPane, oSubmit, bRequired, bMultiSelect, sRef, oImgTest, oErrorFeedback, sCVId, sStyle, sTextDir, sPromptName, oPromptControl);
  28. if(this.m_sDirection != "RTL") // Mobile iOS a11y: Remove the expando image from the read order
  29. {
  30. this.m_oToggleIMG.setAttribute("role", "presentation");
  31. }
  32. }
  33. // * CInnerTree.prototype = new CPromptControl();
  34. // Set this to the tree's parameter name if this is a prompt control tree
  35. // getPromptValues OM function will be called automatically if this is a prompting tree
  36. CInnerTreeIOS.prototype.drawLoading = function(node, nodeId)
  37. {
  38. var theElement = document.getElementById(nodeId);
  39. if (!theElement && node.getNodeType() == TREE_ROOT && this.getRootNodeShowing() == false)
  40. {
  41. theElement = this.m_oPane;
  42. }
  43. if (theElement != null)
  44. {
  45. var newDivContainer = document.createElement("DIV");
  46. newDivContainer.className = CLS_TREE_LEVEL;
  47. newDivContainer.id = this.getName() + node.getTreeRef() + "loading";
  48. newDivContainer.style.width = "100%";
  49. var newNobrContainer = document.createElement("NOBR");
  50. this.drawTreeLines(node, newNobrContainer, node.getLevel() + 1);
  51. if (!(node.getNodeType() == TREE_ROOT && node.getTree().getRootNodeShowing() == false && node.getMoreData() != true))
  52. {
  53. var newLIcon = document.createElement("IMG");
  54. newLIcon.align = gsCTREE_middle;
  55. newLIcon.src = (this.m_sDirection == "RTL" ? TREE_L_ONLY_RTL : TREE_L_ONLY);
  56. //mobile a11y
  57. if(this.m_sDirection != "RTL")
  58. {
  59. newLIcon.setAttribute("role", "presentation");
  60. }
  61. newNobrContainer.appendChild(newLIcon);
  62. }
  63. var newStateIcon = document.createElement("IMG");
  64. newStateIcon.align = gsCTREE_middle;
  65. newStateIcon.src = TREE_LOADING;
  66. newNobrContainer.appendChild(newStateIcon);
  67. var newLabelContainer = document.createElement("SPAN");
  68. newLabelContainer.className = CLS_TREE_NODE_UNSELECTED + K_PRMT_sSP + CLS_TREE_TEXT;
  69. //We need the localized string for "Loading..." here
  70. if (typeof PMT_TRE_TREE_LOADING != K_PRMT_sUNDEFINED)
  71. {
  72. var newStateText = document.createTextNode(PMT_TRE_TREE_LOADING);
  73. newLabelContainer.appendChild(newStateText);
  74. newLabelContainer.title = PMT_TRE_TREE_LOADING;
  75. }
  76. newNobrContainer.appendChild(newLabelContainer);
  77. newDivContainer.appendChild(newNobrContainer);
  78. theElement.appendChild(newDivContainer);
  79. }
  80. };
  81. CInnerTreeIOS.prototype.drawMoreData = function(node, nodeId)
  82. {
  83. var theElement = document.getElementById(nodeId);
  84. if (!theElement && node.getNodeType() == TREE_ROOT && this.getRootNodeShowing() == false)
  85. {
  86. theElement = this.m_oPane;
  87. }
  88. var childrenContainer = (theElement.getAttribute("role") == "tree"? theElement : theElement.firstChild.nextSibling);
  89. if (theElement != null && childrenContainer != null && (childrenContainer.getAttribute("role") == "group" || childrenContainer.getAttribute("role") == "tree"))
  90. {
  91. var newDivContainer = document.createElement("DIV");
  92. newDivContainer.className = CLS_TREE_LEVEL;
  93. newDivContainer.id = this.getName() + node.getTreeRef() + "moredata";
  94. newDivContainer.style.width = "100%";
  95. var newNobrContainer = document.createElement("NOBR");
  96. this.drawTreeLines(node, newNobrContainer, node.getLevel() + 1);
  97. var newLIcon = document.createElement("IMG");
  98. newLIcon.align = gsCTREE_middle;
  99. newLIcon.src = (this.m_sDirection == "RTL" ? TREE_L_ONLY_RTL : TREE_L_ONLY);
  100. //mobile a11y
  101. if(this.m_sDirection != "RTL")
  102. {
  103. newLIcon.setAttribute("role", "presentation");
  104. }
  105. newNobrContainer.appendChild(newLIcon);
  106. var newStateIcon = document.createElement("IMG");
  107. newStateIcon.align = gsCTREE_middle;
  108. newStateIcon.src = TREE_MORE_DATA;
  109. newStateIcon.setAttribute("role", "presentation");
  110. newNobrContainer.appendChild(newStateIcon);
  111. var newLabelContainer = document.createElement("SPAN");
  112. newLabelContainer.className = CLS_TREE_NODE_UNSELECTED + K_PRMT_sSP + CLS_TREE_TEXT_LINK;
  113. //We need the localized string for "More" here
  114. if (typeof PMT_TRE_MORE != K_PRMT_sUNDEFINED)
  115. {
  116. var newStateText = document.createTextNode(PMT_TRE_MORE);
  117. newLabelContainer.appendChild(newStateText);
  118. newLabelContainer.title = PMT_TRE_MORE;
  119. }
  120. newLabelContainer.id = this.getName() + node.getTreeRef() + "moredatalabel";
  121. newLabelContainer.setAttribute(gsCTREE_tree, this.getName());
  122. newLabelContainer.setAttribute(gsCTREE_treeRef, node.getTreeRef());
  123. newLabelContainer.tabIndex = 0;
  124. PRMTUtils.f_addEvent(newLabelContainer, "click", getMoreDataForTreeNode);
  125. PRMTUtils.f_addEvent(newLabelContainer, "keydown", getMoreDataForTreeNode);
  126. PRMTUtils.f_addEvent(newLabelContainer, "keypress", getMoreDataForTreeNode);
  127. newNobrContainer.appendChild(newLabelContainer);
  128. newDivContainer.appendChild(newNobrContainer);
  129. childrenContainer.appendChild(newDivContainer);
  130. }
  131. };
  132. CInnerTreeIOS.prototype.draw = function(node, oParentElement)
  133. {
  134. if (node.getTree().getDisabled() == true)
  135. {
  136. return false;
  137. }
  138. var treeRef = node.getTreeRef();
  139. var treeName = this.getName();
  140. //if the node is the root node, removes the old root node so that the entire tree is redrawn
  141. //this takes care of the case where a loading tree is replaced with real data
  142. if (node.getNodeType() == TREE_ROOT)
  143. {
  144. var rootNode = document.getElementById(treeName);
  145. if (rootNode != null) {
  146. oParentElement.removeChild(rootNode);
  147. }
  148. oParentElement.setAttribute(gsCTREE_treeRef, treeRef);
  149. oParentElement.setAttribute(gsCTREE_tree, treeName);
  150. }
  151. // clone outer container DIV (it's much faster than create a new one)
  152. var nodeElement = this.m_oDIVnewElement.cloneNode(true);
  153. PRMTUtils.f_addEvent( nodeElement, "dragstart", dragStartOnNode );
  154. PRMTUtils.f_addEvent( nodeElement, "drag", dragOnNode );
  155. PRMTUtils.f_addEvent( nodeElement, "dragend", dragEndOnNode );
  156. nodeElement.setAttribute(gsCTREE_treeRef, treeRef);
  157. nodeElement.setAttribute(gsCTREE_tree, treeName);
  158. if (treeRef == K_PRMT_sEMPTY) {
  159. nodeElement.setAttribute(K_PRMT_ARIA_ROLE, "listbox");
  160. }
  161. nodeElement.id = treeName + treeRef;
  162. if (!(node.getNodeType() == TREE_ROOT && this.getRootNodeShowing() == false))
  163. {
  164. this.drawNode(node, nodeElement);
  165. }
  166. // ******************************************************************
  167. // deal with children
  168. // ******************************************************************
  169. oParentElement.appendChild(nodeElement);
  170. this.drawNodeChildren (node, nodeElement);
  171. this.m_bHasBeenDrawn = true;
  172. };
  173. CInnerTreeIOS.prototype.detailToogleIcon = function(node, newToggleIcon) {
  174. // text for high contrast
  175. var newToggleText = this.m_oTextSPAN.cloneNode(true);
  176. newToggleText.className= K_PRMT_TREE_TOGGLE_TEXT;
  177. newToggleText.setAttribute(K_PRMT_ARIA_ROLE, K_PRMT_ARIA_ROLE_PRESENTATION);
  178. var toggleTextContent = "";
  179. if (node.canHaveChildren() == true)
  180. {
  181. newToggleIcon.setAttribute("aria-labelledby", node.id);
  182. if (node.isOpen() == true)
  183. {
  184. newToggleIcon.className = K_PRMT_TREE_TOGGLE_OPENED + this.m_sDirection;
  185. newToggleIcon.title = PMT_TRE_COLLAPSE;
  186. newToggleIcon.alt = PMT_TRE_COLLAPSE;
  187. //newToggleIcon.setAttribute(K_PRMT_TREE_STATE_EXPANDED, true);
  188. toggleTextContent = "-";
  189. }
  190. else
  191. {
  192. newToggleIcon.className= K_PRMT_TREE_TOGGLE_CLOSED + this.m_sDirection;
  193. newToggleIcon.title = PMT_TRE_EXPAND;
  194. newToggleIcon.alt = PMT_TRE_EXPAND;
  195. toggleTextContent = "+";
  196. }
  197. newToggleIcon.tabIndex = -1;
  198. newToggleIcon.onclick = this.toggle;
  199. newToggleText.onclick = this.toggle;
  200. }
  201. else
  202. {
  203. newToggleIcon.className= K_PRMT_TREE_TOGGLE_LEAF;
  204. newToggleIcon.src = PROMT_IMAGES + "blank.gif";
  205. newToggleIcon.alt = "";
  206. newToggleIcon.onclick = null;
  207. newToggleIcon.removeAttribute("aria-labelledby");
  208. newToggleIcon.removeAttribute("title");
  209. newToggleIcon.removeAttribute("tabindex");
  210. }
  211. var toggleTextNode= document.createTextNode(toggleTextContent);
  212. newToggleText.appendChild(toggleTextNode);
  213. newToggleIcon.setAttribute(gsCTREE_treeRef, gsCTREE_img);
  214. return [newToggleIcon, newToggleText];
  215. };
  216. /**
  217. * Draws the checkbox
  218. *
  219. */
  220. CInnerTreeIOS.prototype.drawNodeCheckboxIcon = function(node) {
  221. var checkBoxIcon = CInnerTree.prototype.drawNodeCheckboxIcon.call(this, node);
  222. checkBoxIcon.setAttribute("aria-hidden", "true");
  223. return checkBoxIcon;
  224. };
  225. /**
  226. * Draws the label <span>Node label</span>
  227. *
  228. */
  229. CInnerTreeIOS.prototype.drawNodeLabel = function(node, treeName, treeRef) {
  230. var nodeElementLabelContainer = this.m_oLabelSPAN.cloneNode(true);
  231. nodeElementLabelContainer.id = treeName + treeRef + gsCTREE_labelText;
  232. this.updateLabelClass(nodeElementLabelContainer, getClassName(node, this), node);
  233. nodeElementLabelContainer.setAttribute(gsCTREE_treeRef, treeRef);
  234. nodeElementLabelContainer.setAttribute(gsCTREE_dragRef, treeRef.toString());
  235. nodeElementLabelContainer.setAttribute(gsCTREE_dragTree, treeName);
  236. nodeElementLabelContainer.setAttribute(gsCTREE_tree, treeName);
  237. node.m_oLabelText = nodeElementLabelContainer;
  238. if (node.isFirst()) {
  239. nodeElementLabelContainer.tabIndex = 0;
  240. this.m_lastFocus = nodeElementLabelContainer;
  241. }
  242. var labelText = document.createTextNode( G_IsBidiEnabled && this.m_sTextDir ? PRMT_BidiUtils.enforceBidiDirection(node.getName(), this.m_sTextDir) : node.getName() );
  243. nodeElementLabelContainer.appendChild(labelText);
  244. var sStyle = this.getStyle();
  245. nodeElementLabelContainer.style.color= cssParser(sStyle,"color",true);
  246. nodeElementLabelContainer.style.backgroundColor= cssParser(sStyle,"background-color",true);
  247. nodeElementLabelContainer.style.fontFamily= cssParser(sStyle,"font-family",true);
  248. nodeElementLabelContainer.style.fontSize= cssParser(sStyle,"font-size",true);
  249. nodeElementLabelContainer.style.fontWeight= cssParser(sStyle,"font-weight",true);
  250. nodeElementLabelContainer.style.fontStyle= cssParser(sStyle,"font-style",true);
  251. nodeElementLabelContainer.style.fontVariant= cssParser(sStyle,"font-variant",true);
  252. nodeElementLabelContainer.style.textAlign= cssParser(sStyle,"text-align",true);
  253. nodeElementLabelContainer.style.textVariant= cssParser(sStyle,"text-variant",true);
  254. nodeElementLabelContainer.style.textIndent= cssParser(sStyle,"text-indent",true);
  255. nodeElementLabelContainer.style.textTransform= cssParser(sStyle,"text-transform",true);
  256. nodeElementLabelContainer.setAttribute(gsCTREE_treeRef, treeRef);
  257. nodeElementLabelContainer.setAttribute(gsCTREE_dragRef, treeRef.toString());
  258. nodeElementLabelContainer.setAttribute(gsCTREE_dragTree, treeName);
  259. nodeElementLabelContainer.setAttribute(gsCTREE_tree, treeName);
  260. nodeElementLabelContainer.setAttribute("onfocus","this.classname='" + CLS_TREE_NODE_HOVER + " " + CLS_TREE_TEXT + "';");
  261. nodeElementLabelContainer.setAttribute("onblur","this.classname='" + CLS_TREE_NODE_UNSELECTED + " " + CLS_TREE_TEXT + "';");
  262. return nodeElementLabelContainer;
  263. };
  264. /**
  265. * Process event from click in Toggle icon
  266. */
  267. CInnerTreeIOS.prototype.toggle = function(evt, uiNode)
  268. {
  269. //get the event in a cross-browser fashion
  270. evt = (evt) ? evt : ((event) ? event : null);
  271. //cancel any text selection
  272. clearSelection();
  273. //prevent the event from bubbling to other elements
  274. PRMTUtils.F_StopEvent(evt);
  275. //get UI Node
  276. uiNode = (uiNode? getUINode(null, uiNode) : getUINode(evt));
  277. var uiNodeTreeRef = uiNode.getAttribute(gsCTREE_treeRef).toString();
  278. //get the tree object
  279. var tree = uiNode.getAttribute(gsCTREE_tree).toString();
  280. //get tree Node
  281. var node = getTreeNode(tree, uiNodeTreeRef);
  282. var v_oTree = node.getTree();
  283. if (v_oTree && v_oTree.canToggleNode(node))
  284. {
  285. v_oTree.toggleExpandCollapse(uiNode, node, 0);
  286. }
  287. };
  288. CInnerTreeIOS.prototype.toggleExpandCollapse = function(uiNode, node, toggleDirection)
  289. {
  290. var result = false;
  291. var tree = uiNode.getAttribute(gsCTREE_tree).toString();
  292. //toggle folders
  293. var isExpanded = null;
  294. if ( node.isClosed() && toggleDirection != K_PRMT_TREE_COLLAPSE )
  295. {
  296. isExpanded = true;
  297. this.expandNode(uiNode, node);
  298. result = true;
  299. }
  300. else if ( (node.isOpen()) && toggleDirection != K_PRMT_TREE_EXPAND )
  301. {
  302. isExpanded = false;
  303. this.collapseNode(uiNode, node);
  304. result = true;
  305. }
  306. if (isExpanded){
  307. this.setCacheValue(this.getExpandedNodeCacheKey(node), "true");
  308. }else{
  309. this.removeCacheValue(this.getExpandedNodeCacheKey(node));
  310. }
  311. updateToggleIcon(node, tree);
  312. // update aria-expanded
  313. if ( isExpanded != null ) {
  314. this.updateAriaLabelContainer(node, tree, isExpanded);
  315. }
  316. return result;
  317. };
  318. /**
  319. * Update the label container<span> aria-expanded attribute
  320. */
  321. CInnerTreeIOS.prototype.updateAriaLabelContainer = function(oNode, sTreeName, ariaExpanded)
  322. {
  323. var labelContainer = document.getElementById(sTreeName + oNode.getTreeRef() + gsCTREE_labelText);
  324. if (labelContainer && oNode.canHaveChildren())
  325. {
  326. labelContainer.setAttribute(K_PRMT_TREE_STATE_EXPANDED, ariaExpanded);
  327. } else if(!oNode.canHaveChildren())
  328. {
  329. oNode.removeAttribute(K_PRMT_TREE_STATE_EXPANDED);
  330. }
  331. };