https://www.energymonitor.ai/wp-content/themes/goodlife-wp-B2B/assets/js/modules/GDMContentToggle.js
1export default function GDMContentToggle({ 2 rootSelector, 3 buttonSelector, 4 contentSelector, 5 activeClass = "is-active", 6 bodyClassPrefix = "gd-toggle-view-", 7 stateAttr = "data-active-key", 8 buttonKeyAttr = "data-toggle-key", 9 contentKeyAttr = "data-toggle-content-key", 10}) { 11 const roots = document.querySelectorAll(rootSelector); 12 if (!roots.length) return; 13 14 roots.forEach((root) => { 15 const buttons = Array.from(root.querySelectorAll(buttonSelector)); 16 const contents = Array.from(document.querySelectorAll(contentSelector)).filter( 17 (item) => item.closest(rootSelector) === root || item.dataset.toggleGroup === root.id, 18 ); 19 20 if (!buttons.length || !contents.length) return; 21 22 const contentKeys = contents 23 .map((item) => item.getAttribute(contentKeyAttr)) 24 .filter(Boolean); 25 26 const buttonKeys = buttons 27 .map((item) => item.getAttribute(buttonKeyAttr)) 28 .filter(Boolean); 29 30 const allowedKeys = Array.from(new Set([...contentKeys, ...buttonKeys])); 31 if (!allowedKeys.length) return; 32 33 const getConfiguredKey = () => { 34 const configured = root.getAttribute(stateAttr); 35 if (configured && allowedKeys.includes(configured)) return configured; 36 37 const activeButton = buttons.find((item) => item.classList.contains(activeClass)); 38 const fromButton = activeButton && activeButton.getAttribute(buttonKeyAttr); 39 if (fromButton && allowedKeys.includes(fromButton)) return fromButton; 40 41 const activeContent = contents.find((item) => item.classList.contains(activeClass)); 42 const fromContent = activeContent && activeContent.getAttribute(contentKeyAttr); 43 if (fromContent && allowedKeys.includes(fromContent)) return fromContent; 44 45 return allowedKeys[0]; 46 }; 47 48 const getContentByKey = (key) => 49 contents.find((item) => item.getAttribute(contentKeyAttr) === key); 50 51 const getKeyFromHash = () => { 52 const hashId = window.location.hash.slice(1); 53 if (!hashId) return null; 54 55 const decodedHashId = decodeURIComponent(hashId); 56 const matchingContent = contents.find((item) => item.id === decodedHashId); 57 if (!matchingContent) return null; 58 59 const contentKey = matchingContent.getAttribute(contentKeyAttr); 60 return contentKey && allowedKeys.includes(contentKey) ? contentKey : null; 61 }; 62 63 const defaultKey = getConfiguredKey(); 64 65 const state = { 66 activeKey: getKeyFromHash() || defaultKey, 67 }; 68 69 const setBodyClass = (key) => { 70 const classesToRemove = Array.from(document.body.classList).filter((className) => 71 className.startsWith(bodyClassPrefix), 72 ); 73 74 classesToRemove.forEach((className) => document.body.classList.remove(className)); 75 document.body.classList.add(bodyClassPrefix + key); 76 }; 77 78 const render = () => { 79 root.setAttribute(stateAttr, state.activeKey); 80 81 buttons.forEach((button) => { 82 const key = button.getAttribute(buttonKeyAttr); 83 const isActive = key === state.activeKey; 84 85 button.classList.toggle(activeClass, isActive); 86 button.setAttribute("aria-pressed", isActive ? "true" : "false"); 87 }); 88 89 contents.forEach((content) => { 90 const key = content.getAttribute(contentKeyAttr); 91 const isActive = key === state.activeKey; 92 93 content.classList.toggle(activeClass, isActive); 94 content.setAttribute("aria-hidden", isActive ? "false" : "true"); 95 }); 96 97 setBodyClass(state.activeKey); 98 }; 99 100 const syncFromLocation = () => { 101 const locationKey = getKeyFromHash() || defaultKey; 102 if (!allowedKeys.includes(locationKey) || locationKey === state.activeKey) return; 103 104 state.activeKey = locationKey; 105 render(); 106 }; 107 108 const updateUrlHash = () => { 109 const activeContent = getContentByKey(state.activeKey); 110 if (!activeContent || !activeContent.id) return; 111 112 const nextHash = `#${activeContent.id}`; 113 if (window.location.hash === nextHash) return; 114 115 const url = new URL(window.location.href); 116 url.hash = activeContent.id; 117 window.history.pushState({}, "", url); 118 }; 119 120 buttons.forEach((button) => { 121 button.addEventListener("click", () => { 122 const nextKey = button.getAttribute(buttonKeyAttr); 123 if (!nextKey || nextKey === state.activeKey) return; 124 125 state.activeKey = nextKey; 126 render(); 127 updateUrlHash(); 128 }); 129 }); 130 131 window.addEventListener("hashchange", syncFromLocation); 132 window.addEventListener("popstate", syncFromLocation); 133 134 render(); 135 }); 136}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.