toc-loader.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. define(["options", "jquery", "nav"], function (options, $, navConfig) {
  2. /**
  3. * The path of the output directory, relative to the current HTML file.
  4. * @type {String}
  5. */
  6. var path2root = null;
  7. $(document).ready ( function() {
  8. // Register the click handler for the TOC
  9. var topicRefExpandBtn = $(".wh_publication_toc .wh-expand-btn");
  10. topicRefExpandBtn.click(toggleTocExpand);
  11. /* Toggle expand/collapse on enter and space */
  12. topicRefExpandBtn.keypress(handleKeyEvent);
  13. });
  14. /**
  15. * Retrieves the path of the output directory, relative to the current HTML file.
  16. *
  17. * @returns {*} the path of the output directory, relative to the current HTML file.
  18. */
  19. function getPathToRoot() {
  20. if (path2root == null) {
  21. path2root = $('meta[name="wh-path2root"]').attr("content");
  22. if (path2root == null || path2root == undefined) {
  23. path2root = "";
  24. }
  25. }
  26. return path2root;
  27. };
  28. /*
  29. * Toggles expand/collapse on enter and space
  30. */
  31. function handleKeyEvent(event) {
  32. // Enter & Spacebar events
  33. if ( event.which === 13 || event.which === 32) {
  34. event.preventDefault();
  35. toggleTocExpand.call(this);
  36. }
  37. }
  38. function toggleTocExpand() {
  39. var topicRef = $(this).closest(".topicref");
  40. var state = topicRef.attr(navConfig.attrs.state);
  41. var parentLi = $(this).closest('li');
  42. var titleLink = $(this).siblings(".title").children("a");
  43. var titleLinkID = titleLink.attr("id");
  44. if (state == null) {
  45. // Do nothing
  46. } else if (state == navConfig.states.pending) {
  47. // Do nothing
  48. } else if (state == navConfig.states.notReady) {
  49. topicRef.attr(navConfig.attrs.state, navConfig.states.pending);
  50. parentLi.attr('aria-expanded', 'true');
  51. $(this).attr("aria-labelledby", navConfig.btnIds.pending + " " + titleLinkID);
  52. retrieveChildNodes(topicRef);
  53. } else if (state == navConfig.states.expanded) {
  54. topicRef.attr(navConfig.attrs.state, navConfig.states.collapsed);
  55. $(this).attr("aria-labelledby", navConfig.btnIds.expand + " " + titleLinkID);
  56. parentLi.attr('aria-expanded', 'false');
  57. } else if (state == navConfig.states.collapsed) {
  58. topicRef.attr(navConfig.attrs.state, navConfig.states.expanded);
  59. $(this).attr("aria-labelledby", navConfig.btnIds.collapse + " " + titleLinkID);
  60. parentLi.attr('aria-expanded', 'true');
  61. }
  62. };
  63. /**
  64. * Loads the JS file containing the list of child nodes for the current topic node.
  65. * Builds the list of child topics element nodes based on the retrieved data.
  66. *
  67. * @param topicRefSpan The topicref 'span' element of the current node from TOC / Menu.
  68. */
  69. function retrieveChildNodes(topicRefSpan) {
  70. var tocId = $(topicRefSpan).attr(navConfig.attrs.tocID);
  71. if (tocId != null) {
  72. var jsonHref = navConfig.jsonBaseDir + "/" + tocId;
  73. require(
  74. [jsonHref],
  75. function(data) {
  76. if (data != null) {
  77. var topics = data.topics;
  78. var topicLi = topicRefSpan.closest('li');
  79. var topicsUl = createTopicsList(topics);
  80. var topicsUlParent = $('<ul role="group"/>');
  81. topicsUl.forEach(function(topic){
  82. topicsUlParent.append(topic);
  83. });
  84. topicLi.append(topicsUlParent);
  85. var titleLink = topicRefSpan.find(".title > a");
  86. var titleLinkID = titleLink.attr("id");
  87. var expandBtn = topicRefSpan.children('.wh-expand-btn');
  88. expandBtn.attr("aria-labelledby", navConfig.btnIds.collapse + " " + titleLinkID);
  89. topicRefSpan.attr(navConfig.attrs.state, navConfig.states.expanded);
  90. } else {
  91. topicRefSpan.attr(navConfig.attrs.state, navConfig.states.leaf);
  92. }
  93. }
  94. );
  95. }
  96. }
  97. /**
  98. * Creates the <code>ul</code> element containing the child topic nodes of the current topic.
  99. *
  100. * @param topics The array of containing info about the child topics.
  101. *
  102. * @returns {*|jQuery|HTMLElement} the <code>li</code> elements representing the child topic nodes of the current topic.
  103. */
  104. function createTopicsList(topics) {
  105. var topicsArray = [];
  106. topics.forEach(function(topic) {
  107. var topicLi = createTopicLi(topic);
  108. topicsArray.push(topicLi);
  109. });
  110. return topicsArray;
  111. };
  112. /**
  113. * Creates the <code>li</code> element containing a topic node.
  114. *
  115. * @param topic The info about the topic node.
  116. *
  117. * @returns {*|jQuery|HTMLElement} the <code>li</code> element containing a topic node.
  118. */
  119. function createTopicLi(topic) {
  120. var li = $("<li>");
  121. li.attr('role', 'treeitem');
  122. if (hasChildren(topic)) {
  123. li.attr('aria-expanded', 'false');
  124. }
  125. // .topicref span
  126. var topicRefSpan = createTopicRefSpan(topic);
  127. // append the topicref node in parent
  128. li.append(topicRefSpan);
  129. return li;
  130. };
  131. /**
  132. * Creates the <span> element containing the title and the link to the topic associated to a node in the menu or the TOC.
  133. *
  134. * @param topic The JSON object containing the info about the associated node.
  135. *
  136. * @returns {*|jQuery|HTMLElement} the topic title 'span' element.
  137. */
  138. function createTopicRefSpan(topic) {
  139. var isExternalReference = topic.scope == 'external';
  140. // .topicref span
  141. var topicRefSpan = $("<span>");
  142. topicRefSpan.addClass("topicref");
  143. if (topic.outputclass != null) {
  144. topicRefSpan.addClass(topic.outputclass);
  145. }
  146. // WH-1820 Copy the Ditaval "pass through" attributes.
  147. var dataAttributes = topic.attributes;
  148. if (typeof dataAttributes !== 'undefined') {
  149. var attrsNames = Object.keys(dataAttributes);
  150. attrsNames.forEach(function(attr) {
  151. topicRefSpan.attr(attr, dataAttributes[attr]);
  152. });
  153. }
  154. topicRefSpan.attr(navConfig.attrs.tocID, topic.tocID);
  155. // Current node state
  156. var containsChildren = hasChildren(topic);
  157. if (containsChildren) {
  158. // This state means that the child topics should be retrieved later.
  159. topicRefSpan.attr(navConfig.attrs.state, navConfig.states.notReady);
  160. } else {
  161. topicRefSpan.attr(navConfig.attrs.state, navConfig.states.leaf);
  162. }
  163. var expandBtn = $("<span>", {
  164. class: "wh-expand-btn",
  165. role: "button"
  166. });
  167. if(containsChildren) {
  168. expandBtn.attr("aria-labelledby", navConfig.btnIds.expand + " " + getTopicLinkID(topic));
  169. expandBtn.attr("tabindex", "0");
  170. }
  171. expandBtn.click(toggleTocExpand);
  172. expandBtn.keypress(handleKeyEvent);
  173. topicRefSpan.append(expandBtn);
  174. // Topic ref link
  175. var linkHref = '';
  176. if (topic.href != null && topic.href != 'javascript:void(0)') {
  177. if (!isExternalReference) {
  178. linkHref += getPathToRoot();
  179. }
  180. linkHref += topic.href;
  181. }
  182. var link = $("<a>", {
  183. href: linkHref,
  184. html: topic.title
  185. });
  186. // WH-2368 Update the relative links
  187. var pathToRoot = getPathToRoot();
  188. var linksInLink = link.find("a[href]");
  189. linksInLink.each(function () {
  190. var href = $(this).attr("href");
  191. if (!(href.startsWith("http:") || href.startsWith("https:"))) {
  192. $(this).attr("href", pathToRoot + href);
  193. }
  194. });
  195. var imgsInLink = link.find("img[src]");
  196. imgsInLink.each(function () {
  197. var src = $(this).attr("src");
  198. if (!(src.startsWith("http:") || src.startsWith("https:"))) {
  199. $(this).attr("src", pathToRoot + src);
  200. }
  201. });
  202. link.attr("id", getTopicLinkID(topic));
  203. if (isExternalReference) {
  204. link.attr("target", "_blank");
  205. }
  206. var titleSpan = $("<span>", {
  207. class: "title"
  208. });
  209. titleSpan.append(link);
  210. // Topic ref short description
  211. if (topic.shortdesc != null) {
  212. var tooltipSpan = $("<span>", {
  213. class: "wh-tooltip",
  214. html: topic.shortdesc
  215. });
  216. /* WH-1518: Check if the tooltip has content. */
  217. if (tooltipSpan.find('.shortdesc:empty').length == 0) {
  218. // Update the relative links
  219. var links = tooltipSpan.find("a[href]");
  220. links.each(function () {
  221. var href = $(this).attr("href");
  222. if (!(href.startsWith("http:") || href.startsWith("https:"))) {
  223. $(this).attr("href", pathToRoot + href);
  224. }
  225. });
  226. var imgs = tooltipSpan.find("img[src]");
  227. imgs.each(function () {
  228. var src = $(this).attr("src");
  229. if (!(src.startsWith("http:") || src.startsWith("https:"))) {
  230. $(this).attr("src", pathToRoot + src);
  231. }
  232. });
  233. titleSpan.append(tooltipSpan);
  234. }
  235. }
  236. topicRefSpan.append(titleSpan);
  237. return topicRefSpan;
  238. }
  239. function getTopicLinkID(topic) {
  240. return topic.tocID + "-link";
  241. }
  242. function hasChildren(topic) {
  243. // If the "topics" property is not specified then it means that children should be loaded from the
  244. // module referenced in the "next" property
  245. var children = topic.topics;
  246. var hasChildren;
  247. if (children != null && children.length == 0) {
  248. hasChildren = false;
  249. } else {
  250. hasChildren = true;
  251. }
  252. return hasChildren;
  253. }
  254. });