contents.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /*
  2. Inno Setup
  3. Copyright (C) 1997-2025 Jordan Russell
  4. Portions by Martijn Laan
  5. For conditions of distribution and use, see LICENSE.TXT.
  6. JavaScript code used by contents.htm
  7. */
  8. function get_absolute_top(obj)
  9. {
  10. var y = obj.offsetTop;
  11. while ((obj = obj.offsetParent)) {
  12. y += obj.offsetTop;
  13. }
  14. return y;
  15. }
  16. function get_viewport_element()
  17. {
  18. // On IE 6 in Standards mode & Firefox 1.5, properties like
  19. // scrollTop and clientHeight are set on document.documentElement.
  20. // On IE 5, they're on document.body; the ones on documentElement
  21. // are zero.
  22. if (document.documentElement.clientHeight) {
  23. return document.documentElement;
  24. } else {
  25. return document.body;
  26. }
  27. }
  28. function is_element_displayed(element)
  29. {
  30. do {
  31. if (element.style.display == "none") return false;
  32. } while (element != document.body && (element = element.parentNode));
  33. return true;
  34. }
  35. function ensure_elements_visible(elementTop, elementBottom)
  36. {
  37. if (!is_element_displayed(elementTop) || !is_element_displayed(elementBottom)) {
  38. return;
  39. }
  40. const scrollerElement = document.getElementById("tabbody-contents");
  41. // Inflate by 2 pixels to ensure that focus rectangles are fully visible
  42. let yTop = get_absolute_top(elementTop) - 2;
  43. let yBottom = get_absolute_top(elementBottom) + elementBottom.offsetHeight + 2;
  44. // Make yTop and yBottom relative to the top of the visible client area
  45. const scrollerTop = get_absolute_top(scrollerElement) + scrollerElement.scrollTop;
  46. yTop -= scrollerTop;
  47. yBottom -= scrollerTop;
  48. if (yTop < 0) {
  49. // Scroll up to make the top of elementTop visible
  50. scrollerElement.scrollBy(0, yTop);
  51. } else if (yBottom > scrollerElement.clientHeight) {
  52. // How far do we have to scroll down for elementBottom to be entirely visible?
  53. let delta = yBottom - scrollerElement.clientHeight;
  54. // Don't allow any part of elementTop to be scrolled off the top
  55. if (delta > yTop) delta = yTop;
  56. if (delta > 0) scrollerElement.scrollBy(0, delta);
  57. }
  58. }
  59. function toggle_node(id)
  60. {
  61. const contentElement = document.getElementById("nodecontent_" + id);
  62. const itemElement = contentElement.parentElement;
  63. const linkElement = itemElement.querySelector(":scope > a");
  64. const imageElement = linkElement.querySelector(":scope > img");
  65. const expanding = (contentElement.style.display === "none");
  66. contentElement.style.display = expanding ? "" : "none";
  67. linkElement.setAttribute("aria-expanded", expanding);
  68. imageElement.src = expanding ? "images/contentsheadopen.svg" : "images/contentsheadclosed.svg";
  69. imageElement.alt = expanding ? "\u25BC " : "\u25B6 ";
  70. if (expanding) {
  71. // Scroll expanded items into view. This is similar to calling scrollIntoView() but
  72. // doesn't do any scrolling if the items are already fully visible.
  73. ensure_elements_visible(itemElement, itemElement);
  74. }
  75. }
  76. function init_contents(toggleNode)
  77. {
  78. var i;
  79. if (toggleNode == 0) {
  80. for (i = 1; document.getElementById("nodecontent_" + i) != null; i++) {
  81. toggle_node(i);
  82. }
  83. } else {
  84. toggle_node(toggleNode);
  85. }
  86. var elements = document.getElementById("tabbody-contents").getElementsByTagName("a");
  87. for (i = 0; i < elements.length; i++) {
  88. elements[i].onfocus = node_focused;
  89. elements[i].onblur = node_blurred;
  90. }
  91. }
  92. var curSelectedNode = null;
  93. var curFocusedNode = null;
  94. function update_selected_node_class()
  95. {
  96. if (curSelectedNode) {
  97. var newClass = (curFocusedNode == curSelectedNode) ? "focusedlink" : "selectedlink";
  98. if (curSelectedNode.className != newClass) {
  99. curSelectedNode.className = newClass;
  100. }
  101. }
  102. }
  103. function set_selected_node(newSel)
  104. {
  105. if (curSelectedNode == newSel) return;
  106. if (curSelectedNode) {
  107. curSelectedNode.className = "";
  108. }
  109. curSelectedNode = newSel;
  110. if (curSelectedNode) {
  111. update_selected_node_class();
  112. // Expand parent nodes (may scroll)
  113. var p = curSelectedNode;
  114. while ((p = p.parentNode) && p.id != "tabbody-contents") {
  115. if (p.id && p.id.indexOf("nodecontent_") == 0 && p.style.display == "none") {
  116. toggle_node(p.id.substring(12));
  117. }
  118. }
  119. // Then scroll the node's A element into view
  120. ensure_elements_visible(curSelectedNode, curSelectedNode);
  121. }
  122. }
  123. function node_focused(evt)
  124. {
  125. curFocusedNode = evt ? evt.target : event.srcElement;
  126. if (curFocusedNode == curSelectedNode) {
  127. update_selected_node_class();
  128. } else {
  129. set_selected_node(curFocusedNode);
  130. }
  131. }
  132. function node_blurred(evt)
  133. {
  134. curFocusedNode = null;
  135. update_selected_node_class();
  136. }
  137. var topic_name_regexp = /(?:^|[/\\])topic_([a-z0-9_\-]+)\.htm$/;
  138. function topic_name_from_path(path)
  139. {
  140. var matches = path.match(topic_name_regexp);
  141. return matches ? matches[1] : "";
  142. }
  143. function sync_contents()
  144. {
  145. var bodyFrame = window.parent.frames["bodyframe"];
  146. if (!bodyFrame) return;
  147. var bodyTopic = topic_name_from_path(bodyFrame.window.location.pathname);
  148. if (bodyTopic == "") return;
  149. // If the currently selected node already points to bodyTopic, just return.
  150. // This check is needed to keep the selection from jumping to "[Run] section"
  151. // when "[UninstallRun] section" is clicked (both have the same target topic).
  152. if (curSelectedNode && topic_name_from_path(curSelectedNode.getAttribute("href")) == bodyTopic) {
  153. return;
  154. }
  155. var elements = document.getElementById("tabbody-contents").getElementsByTagName("a");
  156. var i;
  157. for (i = 0; i < elements.length; i++) {
  158. if (topic_name_from_path(elements[i].getAttribute("href")) == bodyTopic) {
  159. if (curSelectedNode != elements[i]) {
  160. // If we're changing the selection while a node is currently
  161. // focused -- which can happen if Back is pressed after
  162. // clicking/selecting a node -- we need to move the focus.
  163. // Otherwise, the focus rectangle would stay where it is,
  164. // while the highlight moved to a different node.
  165. if (curFocusedNode) elements[i].focus();
  166. set_selected_node(elements[i]);
  167. }
  168. break;
  169. }
  170. }
  171. }
  172. function select_tab(newTab)
  173. {
  174. const tabs = ["contents", "index"];
  175. for (let i = 0; i < tabs.length; i++) {
  176. if (tabs[i] != newTab) {
  177. document.getElementById("tab-" + tabs[i]).setAttribute("aria-selected", false);
  178. document.getElementById("tabbody-" + tabs[i]).hidden = true;
  179. }
  180. }
  181. document.getElementById("tab-" + newTab).setAttribute("aria-selected", true);
  182. document.getElementById("tabbody-" + newTab).hidden = false;
  183. if (newTab == "index") init_index_tab();
  184. }
  185. var indexTabInited = false;
  186. function init_index_tab()
  187. {
  188. if (indexTabInited) return;
  189. indexTabInited = true;
  190. var script = document.createElement("script");
  191. script.src = "contentsindex.js";
  192. script.type = "text/javascript";
  193. document.getElementsByTagName("head")[0].appendChild(script);
  194. // contentsindex.js calls init_index_tab_elements()
  195. }
  196. function init_index_tab_elements()
  197. {
  198. var html = "ERROR!";
  199. if (contentsIndexData) {
  200. var len = contentsIndexData.length;
  201. var htmlArray = new Array(len);
  202. var i, matches;
  203. var re = /^([^#:]+)(#[^:]+)?:(.+)$/
  204. for (i = 0; i < len; i++) {
  205. matches = contentsIndexData[i].match(re);
  206. if (!matches) break;
  207. htmlArray[i] = '<a href="topic_' + matches[1] + ".htm" +
  208. ((matches[2] !== undefined) ? matches[2] : "") +
  209. '" target="bodyframe">' + matches[3] + "</a><br />";
  210. }
  211. // Note: On IE6, joining an array is ~5x faster than using "html +=" to build a long string
  212. if (i == len) { // were all processed?
  213. html = htmlArray.join("");
  214. }
  215. }
  216. var indexBody = document.getElementById("tabbody-index");
  217. indexBody.onclick = index_tab_element_clicked;
  218. indexBody.innerHTML = html;
  219. }
  220. function index_tab_element_clicked(evt)
  221. {
  222. // If an index link is clicked and only the hash changes on bodyframe
  223. // (i.e. still same page), bodyframe doesn't receive any notification.
  224. // So we must manually tell it to update the highlight.
  225. var element = evt ? evt.target : event.srcElement;
  226. if (element.tagName.toLowerCase() == "a") {
  227. var href = element.getAttribute("href");
  228. if (href != null && href != "" && element.getAttribute("target") == "bodyframe") {
  229. var bodyFrame = window.parent.frames["bodyframe"];
  230. if (bodyFrame) {
  231. bodyFrame.set_href_and_highlight_anchor(href);
  232. }
  233. }
  234. }
  235. }