let expiryBuffer = 1800000; //60 * 30 * 1000 millisecond = 30 Minutes let localStoragePrefix = "menu-tree-data"; let categoriesPrefix = "menu-tree-category"; let sectionsPrefix = "menu-tree-section"; let articlePrefix = "menu-tree-article"; let currentItemClass = "current-item"; let currentItemParentClass = "current-item-parent"; let mainParent = null; async function getDataFromUrl(url, dataField){ let returnData = {}; let response = await fetch(url); let responseJson = await response.json(); responseJson[dataField].map(dataSingle=>{ returnData[dataSingle.id] = dataSingle; }); if (responseJson.next_page) { let nextPage = await getDataFromUrl(responseJson.next_page,dataField); return {...returnData,...nextPage}; } else { return returnData; } } async function setAllData(locale = 'en-us'){ let categoriesPromise = getDataFromUrl(`/api/v2/help_center/${locale}/categories`,'categories'); let sectionsPromise = getDataFromUrl(`/api/v2/help_center/${locale}/sections`,'sections'); let [categories,sections] = await Promise.all([categoriesPromise,sectionsPromise]).catch(err=>{ console.error("Error occured in fetching data",err); }); let subSections = {}; let subSubSections = {}; let subSubSubSections = {}; let subSubSubSubSections = {}; for (let [key, value] of Object.entries(sections)) { if(value.parent_section_id){ subSections[value.id] = value; delete sections[key]; } } for (let [key, value] of Object.entries(subSections)) { if(value.parent_section_id && subSections[value.parent_section_id]){ subSubSections[value.id] = value; delete subSections[key]; } } for (let [key, value] of Object.entries(subSubSections)) { if(value.parent_section_id && subSubSections[value.parent_section_id]){ subSubSubSections[value.id] = value; delete subSubSections[key]; } } for (let [key, value] of Object.entries(subSubSubSections)) { if(value.parent_section_id && subSubSubSections[value.parent_section_id]){ subSubSubSubSections[value.id] = value; delete subSubSubSections[key]; } } let now = new Date(); let storageData = { categories, sections, subSections, subSubSections, subSubSubSections, subSubSubSubSections, expiry : now.getTime() + expiryBuffer } localStorage.setItem(`${localStoragePrefix}-${locale}`, JSON.stringify(storageData)); return storageData; } // Function for getting URL data and return its type and ID function getCurrentUrlData(){ let returnData = { type : null, id : null }; let pathArray = window.location.pathname.split("/"); pathArray = pathArray.slice(3); if(pathArray[0] && pathArray[1]){ // Get current page ID to mark the active element let currentId = document.getElementById('current-id') if (currentId) { currentId = currentId.dataset.id } else { currentId = parseInt(pathArray[1].replace(/(^\d+)/i,'$1')) } returnData.type = pathArray[0]; returnData.id = currentId; } return returnData; } // Function for getting breadcrumb data and return its type and ID function getCurrentBreadcrumbData() { let breadcrumb = document.querySelector('.sub-nav.sticky ol.breadcrumbs') if (breadcrumb) { let returnData = {} let pathElement = breadcrumb.querySelector('li:last-child') let pathUrl = pathElement.querySelector('a').href let pathArray = pathUrl.split("/"); pathArray = pathArray.slice(5) if (pathArray[0] && pathArray[1]) { let pathType = pathArray[0] switch (pathType) { case "categories": pathType = "category" break; case "sections": pathType = "section" } returnData.type = pathType; returnData.id = parseInt(pathArray[1].replace(/(^\d+)/i,'$1')) } // console.log(returnData.id) return returnData; } } function setActiveLi(urlData, locale) { let prefix = null; // set prefix from URL if(urlData.type == 'categories'){ prefix = categoriesPrefix; } else if(urlData.type == 'sections'){ prefix = sectionsPrefix; } else if (urlData.type == 'articles'){ prefix = articlePrefix; } if(prefix){ let currentItem = document.getElementById(`${prefix}-link-${urlData.id}`); // Check if current item are fetched from API or not if(currentItem){ // If present, add current-item class to element currentItem.classList.add(currentItemClass); let currentItemParent; while(currentItemParent = currentItem.parentNode.closest("li")){ currentItem = currentItemParent currentItem.classList.add(currentItemParentClass); } } if (prefix == articlePrefix || prefix == sectionsPrefix) { let breadcrumbData = getCurrentBreadcrumbData() // Get current sidebar parent list from breadcrumb let currentSidebar = document.getElementById(`menu-tree-${breadcrumbData.type}-link-${breadcrumbData.id}`) if (currentSidebar) { let currentSidebarParent; while (currentSidebarParent = currentSidebar.parentNode.closest("li")) { currentSidebar = currentSidebarParent // Fetch article if current item or article parent not present if (!currentItem) { if (!currentSidebar.classList.contains('menu-tree-category')) { fetchArticleOnClick(currentSidebar, locale) } } else if (currentSidebar.querySelector('.menu-tree-article') == null) { if (!currentSidebar.classList.contains('menu-tree-category')) { fetchArticleOnClick(currentSidebar, locale) } } currentSidebar.classList.add(currentItemParentClass) // currentSidebar.querySelector("a").click(); currentSidebar.querySelector("a").dispatchEvent(new Event('click')) } } // console.log(currentSidebar) } } } function getMenuTreeData(key) { const itemStr = localStorage.getItem(key) // if the item doesn't exist, return null if (!itemStr) { return null } const item = JSON.parse(itemStr) const now = new Date() // compare the expiry time of the item with the current time if (now.getTime() > item.expiry) { // If the item is expired, delete the item from storage // and return null localStorage.removeItem(key) return null } return item } function createListElement(parent, prefix, data, isCollapsible){ let parentNode = document.getElementById(parent); if(parentNode){ let parentUl = parentNode.getElementsByTagName("ul")[0]; if(!parentUl){ parentUl = document.createElement("ul"); if((prefix == sectionsPrefix) && data.parent_section_id){ parentUl.className = `${articlePrefix}-parent`; } else { parentUl.className = `${prefix}-parent`; } if (isCollapsible) { parentUl.classList.add('collapse') } if(data.parent_section_id) { parentUl.id = `${prefix}-collapse-${data.parent_section_id}`; } else if (data.category_id) { parentUl.id = `${prefix}-collapse-${data.category_id}`; } else if (prefix == articlePrefix) { parentUl.id = `${sectionsPrefix}-collapse-${data.section_id}` } else { parentUl.id = `${prefix}-collapse-${data.section_id}`; } parentNode.appendChild(parentUl); } let elementLi = document.createElement("li"); elementLi.id = `${prefix}-${data.id}`; elementLi.className = prefix; parentUl.appendChild(elementLi); let elementLink = document.createElement("a"); elementLink.id = `${prefix}-link-${data.id}`; elementLink.dataset.id = data.id; elementLink.className = `${prefix}-link`; let elementSpan = document.createElement('span') let elementArrow = document.createElement('i') if(prefix == categoriesPrefix) { elementLink.dataset.target = `#${sectionsPrefix}-collapse-${data.id}`; elementLink.dataset.toggle = 'collapse' elementSpan.innerHTML = data.name elementArrow.className = 'fa fa-angle-down' elementLink.appendChild(elementSpan) elementLink.appendChild(elementArrow) } else if (prefix == sectionsPrefix) { elementLink.dataset.target = `#${sectionsPrefix}-collapse-${data.id}`; elementLink.dataset.toggle = 'collapse' elementSpan.innerHTML = data.name elementArrow.className = 'fa fa-angle-down' elementLink.appendChild(elementSpan) elementLink.appendChild(elementArrow) } else if (prefix == articlePrefix) { elementLink.href = data.html_url; elementLink.innerHTML = data.name; if (data.user_type == "staff") { let svgElement = ` ` elementLink.innerHTML = `${data.name} ${svgElement}` } if (data.promoted == true) { let svgPromotedElement = ` ` elementLink.innerHTML = `${svgPromotedElement} ${data.name}` } if ((data.promoted == true) && ( data.user_type == "staff")) { let svgElement = ` ` let svgPromotedElement = ` ` elementLink.innerHTML = `${svgPromotedElement} ${data.name} ${svgElement}` } } else { elementLink.dataset.target = `#${data.id}`; } elementLink.ariaExpanded = false; elementLi.appendChild(elementLink); } } async function getUserSegment(id) { let userSegment = await fetch(`/api/v2/help_center/user_segments/${id}`) let response = await userSegment.json() return response.user_segment } // Helper function to create article element on section click function createArticleElement(parent, data) { let prefix = "menu-tree-article" let currentItem = getCurrentUrlData() let currentId = currentItem.id let parentNode = document.getElementById(parent) let parentUl = parentNode.getElementsByTagName("ul")[0]; if (!parentUl) { parentUl = document.createElement('ul'); } parentUl.className = `${prefix}-parent collapse in` parentUl.id = `${sectionsPrefix}-collapse-${data.section_id}` parentNode.appendChild(parentUl) let elementLi = document.createElement("li"); elementLi.id = `${prefix}-${data.id}`; elementLi.className = prefix; if (data.id == currentId) { elementLi.classList.add(currentItemParentClass) } parentUl.appendChild(elementLi); let elementLink = document.createElement("a"); elementLink.id = `${prefix}-link-${data.id}`; elementLink.dataset.id = `${prefix}-${data.id}`; elementLink.className = `${prefix}-link`; if (data.id == currentId) { elementLink.classList.add(currentItemClass) } elementLink.href = data.html_url; elementLink.innerHTML = data.name; if (data.user_type == "staff") { let svgElement = ` ` elementLink.innerHTML = `${data.name} ${svgElement}` } if (data.promoted == true) { let svgPromotedElement = ` ` elementLink.innerHTML = `${svgPromotedElement} ${data.name}` } if ((data.promoted == true) && ( data.user_type == "staff")) { let svgElement = ` ` let svgPromotedElement = ` ` elementLink.innerHTML = `${svgPromotedElement} ${data.name} ${svgElement}` } elementLink.dataset.target = `#${data.id}`; elementLink.ariaExpanded = false; elementLi.appendChild(elementLink); } // Function for fetching article on section click async function fetchArticleOnClick(element, locale) { $(element).addClass('is-loading') let sectionElement = $(element) let sectionId = $(element) .find('a') .attr('data-id') let parent = $(element).attr('id') let url = `/api/v2/help_center/${locale}/sections/${sectionId}/articles?per_page=100` let articleData = await getDataFromUrl(url, 'articles') let [articlesPromise] = await Promise.all([articleData]) .then($(sectionElement).removeClass('is-loading')) .catch(err=>{ console.error("Error occured in fetching data",err); }); if (Object.keys(articlesPromise).length != 0) { $(element).find('a').eq(0).attr('aria-expanded', true) for (let [key, value] of Object.entries(articlesPromise)) { if (HelpCenter.user.role !== 'end_user') { if (value.user_segment_id !== null) { let userSegmentId = value.user_segment_id let userSegment = await getUserSegment(userSegmentId) if (userSegment.user_type == 'staff') { value.user_type = "staff" } } } createArticleElement(parent, value) } } // Get old localstorage data let oldStorage = getMenuTreeData(`${localStoragePrefix}-${locale}`) // Assign new promise with old data to new array let articles = Object.assign(articlesPromise, oldStorage["articles"]) let storageData = { ...oldStorage, articles } localStorage.setItem(`${localStoragePrefix}-${locale}`, JSON.stringify(storageData)) } async function makeTree(sidebar, locale = "en-us") { let menuTree = document.getElementById(sidebar); if(menuTree){ let menuTreeData = getMenuTreeData(`${localStoragePrefix}-${locale}`); if(!menuTreeData){ menuTreeData = await setAllData(locale); } let categoriesLength = Object.keys(menuTreeData.categories).length if (categoriesLength > 1) { for (let [key, value] of Object.entries(menuTreeData.categories)) { createListElement(sidebar,categoriesPrefix,value); } for (let [key, value] of Object.entries(menuTreeData.sections)) { createListElement(`${categoriesPrefix}-${value.category_id}`, sectionsPrefix, value, true); } for (let [key, value] of Object.entries(menuTreeData.subSections)) { createListElement(`${sectionsPrefix}-${value.parent_section_id}`,sectionsPrefix,value, true); } for (let [key, value] of Object.entries(menuTreeData.subSubSections)) { createListElement(`${sectionsPrefix}-${value.parent_section_id}`,sectionsPrefix,value, true); } for (let [key, value] of Object.entries(menuTreeData.subSubSubSections)) { createListElement(`${sectionsPrefix}-${value.parent_section_id}`,sectionsPrefix,value, true); } for (let [key, value] of Object.entries(menuTreeData.subSubSubSubSections)) { createListElement(`${sectionsPrefix}-${value.parent_section_id}`,sectionsPrefix,value, true); } } else if (categoriesLength == 1) { for (let [key, value] of Object.entries(menuTreeData.sections)) { createListElement(sidebar,sectionsPrefix,value) } for (let [key, value] of Object.entries(menuTreeData.subSections)) { createListElement(`${sectionsPrefix}-${value.parent_section_id}`,sectionsPrefix,value, true); } for (let [key, value] of Object.entries(menuTreeData.subSubSections)) { createListElement(`${sectionsPrefix}-${value.parent_section_id}`,sectionsPrefix,value, true); } for (let [key, value] of Object.entries(menuTreeData.subSubSubSections)) { createListElement(`${sectionsPrefix}-${value.parent_section_id}`,sectionsPrefix,value, true); } for (let [key, value] of Object.entries(menuTreeData.subSubSubSubSections)) { createListElement(`${sectionsPrefix}-${value.parent_section_id}`,sectionsPrefix,value, true); } } if (menuTreeData.articles) { for (let [key, value] of Object.entries(menuTreeData.articles)) { createListElement(`${sectionsPrefix}-${value.section_id}`,articlePrefix,value, true); } } // Get current page data from URL in case no breadcrumb present (eg: homepage) let urlData = getCurrentUrlData(); // Then set the active list based on urlData if(urlData.type){ setActiveLi(urlData, locale); } let element = document.getElementsByClassName('menu-tree-section') for (i = 0; i < element.length; i++) { if ($(element[i]).attr('aria-expanded', 'false')) { if (!$(element[i]).hasClass('current-item-parent')) { if ($(element[i]).find('.menu-tree-article').eq(0).length == 0) { element[i].addEventListener('click', function(e) { fetchArticleOnClick(this, locale) }, {once: true}) } } } else { return null } } } } function sidebarScroll() { let element = document.getElementsByClassName('current-item'); if(element.length > 0) { let sidebar = document.getElementsByClassName('nav-sidebar-wrapper') let headerOffset = 300; let elementOffset = element[0].getBoundingClientRect().top; let offsetPosition = elementOffset - headerOffset sidebar[0].scrollTo({ top: offsetPosition, behavior: "smooth" }) } console.log(element) }