/*
Inno Setup
Copyright (C) 1997-2025 Jordan Russell
Portions by Martijn Laan
For conditions of distribution and use, see LICENSE.TXT.
JavaScript code used by contents.htm
*/
function get_absolute_top(obj)
{
var y = obj.offsetTop;
while ((obj = obj.offsetParent)) {
y += obj.offsetTop;
}
return y;
}
function get_viewport_element()
{
// On IE 6 in Standards mode & Firefox 1.5, properties like
// scrollTop and clientHeight are set on document.documentElement.
// On IE 5, they're on document.body; the ones on documentElement
// are zero.
if (document.documentElement.clientHeight) {
return document.documentElement;
} else {
return document.body;
}
}
function is_element_displayed(element)
{
do {
if (element.hidden) return false;
} while ((element = element.parentElement));
return true;
}
function ensure_elements_visible(elementTop, elementBottom)
{
if (!is_element_displayed(elementTop) || !is_element_displayed(elementBottom)) {
return;
}
const scrollerElement = document.getElementById("tabbody-contents");
// Inflate by 2 pixels to ensure that focus rectangles are fully visible
let yTop = get_absolute_top(elementTop) - 2;
let yBottom = get_absolute_top(elementBottom) + elementBottom.offsetHeight + 2;
// Make yTop and yBottom relative to the top of the visible client area
const scrollerTop = get_absolute_top(scrollerElement) + scrollerElement.scrollTop;
yTop -= scrollerTop;
yBottom -= scrollerTop;
if (yTop < 0) {
// Scroll up to make the top of elementTop visible
scrollerElement.scrollBy(0, yTop);
} else if (yBottom > scrollerElement.clientHeight) {
// How far do we have to scroll down for elementBottom to be entirely visible?
let delta = yBottom - scrollerElement.clientHeight;
// Don't allow any part of elementTop to be scrolled off the top
if (delta > yTop) delta = yTop;
if (delta > 0) scrollerElement.scrollBy(0, delta);
}
}
function toggle_node(id)
{
const contentElement = document.getElementById("nodecontent_" + id);
const itemElement = contentElement.parentElement;
const linkElement = itemElement.querySelector(":scope > a");
const imageElement = linkElement.querySelector(":scope > img");
const expanding = !!contentElement.hidden;
contentElement.hidden = !expanding;
linkElement.setAttribute("aria-expanded", expanding);
imageElement.src = expanding ? "images/contentsheadopen.svg" : "images/contentsheadclosed.svg";
imageElement.alt = expanding ? "\u25BC " : "\u25B6 ";
if (expanding) {
// Scroll expanded items into view. This is similar to calling scrollIntoView() but
// doesn't do any scrolling if the items are already fully visible.
ensure_elements_visible(itemElement, itemElement);
}
}
function init_contents(toggleNode)
{
var i;
if (toggleNode == 0) {
for (i = 1; document.getElementById("nodecontent_" + i) != null; i++) {
toggle_node(i);
}
} else {
toggle_node(toggleNode);
}
}
var curSelectedNode = null;
function set_selected_node(newSel)
{
if (curSelectedNode == newSel) return;
if (curSelectedNode) {
curSelectedNode.removeAttribute("aria-selected");
}
curSelectedNode = newSel;
if (curSelectedNode) {
curSelectedNode.setAttribute("aria-selected", true);
// Expand parent nodes (may scroll)
let p = curSelectedNode;
while ((p = p.parentElement) && p.id !== "tabbody-contents") {
if (p.id && p.id.startsWith("nodecontent_") && p.hidden) {
toggle_node(p.id.substring(12));
}
}
// Then scroll the node's A element into view
ensure_elements_visible(curSelectedNode, curSelectedNode);
// If the focus is currently inside the Contents tab panel (and not inside the
// topic body frame), then ensure the new selected node is focused. This matters
// when Back is clicked in the browser; we want the selection and the focus
// rectangle to both move back to the previous node.
if (document.getElementById("tabbody-contents").contains(document.activeElement)) {
curSelectedNode.focus();
}
}
}
var topic_name_regexp = /(?:^|[/\\])topic_([a-z0-9_\-]+)\.htm$/;
function topic_name_from_path(path)
{
var matches = path.match(topic_name_regexp);
return matches ? matches[1] : "";
}
function sync_contents(bodyTopic)
{
if (!bodyTopic) return;
const elements = document.getElementById("tabbody-contents").getElementsByTagName("a");
for (let i = 0; i < elements.length; i++) {
if (topic_name_from_path(elements[i].getAttribute("href")) === bodyTopic) {
set_selected_node(elements[i]);
break;
}
}
}
function select_tab(newTab)
{
const tabs = ["contents", "index"];
for (let i = 0; i < tabs.length; i++) {
if (tabs[i] != newTab) {
document.getElementById("tab-" + tabs[i]).setAttribute("aria-selected", false);
document.getElementById("tabbody-" + tabs[i]).hidden = true;
}
}
document.getElementById("tab-" + newTab).setAttribute("aria-selected", true);
document.getElementById("tabbody-" + newTab).hidden = false;
if (newTab == "index") init_index_tab();
}
var indexTabInited = false;
function init_index_tab()
{
if (indexTabInited) return;
indexTabInited = true;
var script = document.createElement("script");
script.src = "contentsindex.js";
script.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(script);
// contentsindex.js calls init_index_tab_elements()
}
function init_index_tab_elements()
{
var html = "ERROR!";
if (contentsIndexData) {
var len = contentsIndexData.length;
var htmlArray = new Array(len);
var i, matches;
var re = /^([^#:]+)(#[^:]+)?:(.+)$/
for (i = 0; i < len; i++) {
matches = contentsIndexData[i].match(re);
if (!matches) break;
htmlArray[i] = '' + matches[3] + "
";
}
// Note: On IE6, joining an array is ~5x faster than using "html +=" to build a long string
if (i == len) { // were all processed?
html = htmlArray.join("");
}
}
document.getElementById("tabbody-index").innerHTML = html;
}
window.addEventListener("message", (event) => {
//console.log("contents.js message received:", event.data);
if (typeof event.data === "string" && event.data.startsWith("ishelp_sync_contents:")) {
sync_contents(event.data.substring(21));
}
});