define(["options", "jquery", "nav"], function (options, $, navConfig) { /** * The path of the output directory, relative to the current HTML file. * @type {String} */ var path2root = null; // Register the hover handler for the Menu $(document).ready ( function() { // Add mouse listener and set the wai-aria attributes to the new value, depending on the expanded/collapsed state $(document).on("mouseenter", ".wh_top_menu li", menuItemHovered); // Mouse exit $(document).on("mouseleave", ".wh_top_menu li", function () { var stateNode = $(this).children(".topicref"); var currentState = stateNode.attr(navConfig.attrs.state); if (currentState !== navConfig.states.leaf) { $(this).attr("aria-expanded", "false"); stateNode.attr(navConfig.attrs.state, navConfig.states.collapsed); } }); }); /** * Retrieves the path of the output directory, relative to the current HTML file. * * @returns {*} the path of the output directory, relative to the current HTML file. */ function getPathToRoot() { if (path2root == null) { path2root = $('meta[name="wh-path2root"]').attr("content"); if (path2root == null || path2root == undefined) { path2root = ""; } } return path2root; }; /** * Loads the JS file containing the list of child nodes for the current topic node. * Builds the list of child topics element nodes based on the retrieved data. * * @param topicRefSpan The topicref 'span' element of the current node from TOC / Menu. */ function retrieveChildNodes(topicRefSpan) { var tocId = $(topicRefSpan).attr(navConfig.attrs.tocID); if (tocId != null) { var jsonHref = navConfig.jsonBaseDir + "/" + tocId; require( [jsonHref], function(data) { if (data != null) { var topics = data.topics; var topicLi = topicRefSpan.closest('li'); var topicsLiList = createTopicsList(topics); var loadingDotsUl = topicLi.children("ul.loading"); // Remove the loading dots from the menu. loadingDotsUl.find('li').remove(); loadingDotsUl.removeClass('loading'); topicsLiList.forEach(function(topicLi){ loadingDotsUl.append(topicLi); }); topicRefSpan.attr(navConfig.attrs.state, navConfig.states.expanded); } else { topicRefSpan.attr(navConfig.attrs.state, navConfig.states.leaf); } } ); } } /** * Creates the ul element containing the child topic nodes of the current topic. * * @param topics The array of containing info about the child topics. * * @returns {*|jQuery|HTMLElement} the li elements representing the child topic nodes of the current topic. */ function createTopicsList(topics) { var topicsArray = []; topics.forEach(function(topic) { if (!topic.menu.isHidden) { var topicLi = createTopicLi(topic); topicsArray.push(topicLi); } }); return topicsArray; }; /** * Creates the li element containing a topic node. * * @param topic The info about the topic node. * * @returns {*|jQuery|HTMLElement} the li element containing a topic node. */ function createTopicLi(topic) { var li = $("
  • "); if (topic.menu.hasChildren) { li.addClass("has-children"); li.attr("aria-haspopup", "true"); li.attr("aria-expanded", "false"); } var topicImage = topic.menu.image; if (topicImage != null && topicImage.href != null) { var menuImageSpan = createMenuImageSpan(topic); li.append(menuImageSpan); } // .topicref span var topicRefSpan = createTopicRefSpan(topic); // append the topicref node in parent li.append(topicRefSpan); return li; }; /** * Creates the element containing the image icon for the current node in the menu. * @param topic The JSON object containing the info about the associated node. * * @returns {*|jQuery|HTMLElement} the image 'span' element. */ function createMenuImageSpan(topic) { var topicImage = topic.menu.image; // Image span var imgSpan = $("", {class : "topicImg"}); var isExternalReference = topicImage.scope == 'external'; var imageHref = ''; if (!isExternalReference) { imageHref += getPathToRoot(); } imageHref += topicImage.href; var img = $("", { src : imageHref, alt : topic.title }); if (topicImage.height != null) { img.attr("height", topicImage.height); } if (topicImage.width != null) { img.attr("width", topicImage.width); } imgSpan.append(img); return imgSpan; } /** * Creates the element containing the title and the link to the topic associated to a node in the menu or the TOC. * * @param topic The JSON object containing the info about the associated node. * * @returns {*|jQuery|HTMLElement} the topic title 'span' element. */ function createTopicRefSpan(topic) { var isExternalReference = topic.scope == 'external'; // .topicref span var topicRefSpan = $(""); topicRefSpan.addClass("topicref"); if (topic.outputclass != null) { topicRefSpan.addClass(topic.outputclass); } // WH-1820 Copy the Ditaval "pass through" attributes. var dataAttributes = topic.attributes; if (typeof dataAttributes !== 'undefined') { var attrsNames = Object.keys(dataAttributes); attrsNames.forEach(function(attr) { topicRefSpan.attr(attr, dataAttributes[attr]); }); } topicRefSpan.attr(navConfig.attrs.tocID, topic.tocID); topicRefSpan.attr("id", topic.tocID + "-mi"); // Current node state var containsChildren = hasChildren(topic); if (containsChildren) { // This state means that the child topics should be retrieved later. topicRefSpan.attr(navConfig.attrs.state, navConfig.states.notReady); } else { topicRefSpan.attr(navConfig.attrs.state, navConfig.states.leaf); } // Topic ref link var linkHref = ''; if (topic.href != null && topic.href != 'javascript:void(0)') { if (!isExternalReference) { linkHref += getPathToRoot(); } linkHref += topic.href; } var link = $("", { href: linkHref, html: topic.title }); // WH-2368 Update the relative links var pathToRoot = getPathToRoot(); var linksInLink = link.find("a[href]"); linksInLink.each(function () { var href = $(this).attr("href"); if (!(href.startsWith("http:") || href.startsWith("https:"))) { $(this).attr("href", pathToRoot + href); } }); var imgsInLink = link.find("img[src]"); imgsInLink.each(function () { var src = $(this).attr("src"); if (!(src.startsWith("http:") || src.startsWith("https:"))) { $(this).attr("src", pathToRoot + src); } }); if (isExternalReference) { link.attr("target", "_blank"); } var titleSpan = $("", { class: "title" }); titleSpan.append(link); topicRefSpan.append(titleSpan); return topicRefSpan; } function hasChildren(topic) { // If the "topics" property is not specified then it means that children should be loaded from the // module referenced in the "next" property var children = topic.topics; var hasChildren; if (children != null && children.length == 0) { hasChildren = false; } else if (topic.menu != null) { hasChildren = topic.menu.hasChildren; } else { hasChildren = true; } return hasChildren; } function menuItemHovered() { var topicRefSpan = $(this).children('.topicref'); var state = topicRefSpan.attr(navConfig.attrs.state); if (state === navConfig.states.pending) { // Do nothing } else if (state === navConfig.states.notReady) { topicRefSpan.attr(navConfig.attrs.state, navConfig.states.pending); var menuItemID = topicRefSpan.attr("id"); var dot = $("", { class: "dot" }); var loadingMarker = $("
      ", { class: "loading", "aria-labelledby" : menuItemID, role : "menu", html: $("
    • ", { role : "menuitem", html: [dot, dot.clone(), dot.clone()] }) }); $(this).append(loadingMarker); handleMenuPosition($(this)); retrieveChildNodes(topicRefSpan); } else if (state === navConfig.states.expanded) { handleMenuPosition($(this)); } else if (state === navConfig.states.collapsed) { topicRefSpan.attr(navConfig.attrs.state, navConfig.states.expanded); } if ($(this).attr("aria-expanded") != null) { $(this).attr("aria-expanded", "true"); } }; var dirAttr = $('html').attr('dir'); var rtlEnabled = false; if (dirAttr=='rtl') { rtlEnabled = true; } /** * Display top menu so that it will not exit the viewport. * * @param $menuItem The top menu menu 'li' element of the current node from TOC / Menu. */ function handleMenuPosition($menuItem) { // Handle menu position var parentDir = rtlEnabled ? 'left' : 'right'; var index = $('.wh_top_menu ul').index($menuItem.parent('ul')); var currentElementOffsetLeft = parseInt($menuItem.offset().left); var currentElementWidth = parseInt($menuItem.width()); var currentElementOffsetRight = currentElementOffsetLeft + currentElementWidth; var nextElementWidth = parseInt($menuItem.children('ul').width()); var offsetLeft, offsetRight = currentElementOffsetRight + nextElementWidth; if (index == 0) { if (parentDir=='left') { $menuItem.attr('data-menuDirection', 'left'); offsetLeft = currentElementOffsetRight - nextElementWidth; if (offsetLeft <= 0) { $menuItem.css('position', 'relative'); $menuItem.children('ul').css('position','absolute'); $menuItem.children('ul').css('right', 'auto'); $menuItem.children('ul').css('left', '0'); } } else { $menuItem.attr('data-menuDirection', 'right'); offsetRight = currentElementOffsetLeft + nextElementWidth; if (offsetRight >= $(window).width()) { $menuItem.css('position', 'relative'); $menuItem.children('ul').css('position','absolute'); $menuItem.children('ul').css('right', '0'); $menuItem.children('ul').css('left', 'auto'); } } } else { offsetLeft = currentElementOffsetLeft - nextElementWidth; if (parentDir == 'left') { if (offsetLeft >= 0) { $menuItem.attr('data-menuDirection', 'left'); $menuItem.children('ul').css('right', '100%'); $menuItem.children('ul').css('left', 'auto'); } else { $menuItem.attr('data-menuDirection', 'right'); $menuItem.children('ul').css('right', 'auto'); $menuItem.children('ul').css('left', '100%'); } } else { if (offsetRight <= $(window).width()) { $menuItem.attr('data-menuDirection', 'right'); $menuItem.children('ul').css('right', 'auto'); $menuItem.children('ul').css('left', '100%'); } else { $menuItem.attr('data-menuDirection', 'left'); $menuItem.children('ul').css('right', '100%'); $menuItem.children('ul').css('left', 'auto'); } } } } });