1/* 2// ------------------------- 3// DOM Ready wrapper 4// ------------------------- 5document.addEventListener("DOMContentLoaded", function () { 6 7// ------------------------- 8// Menu cache versioning 9// ------------------------- 10const MENU_VERSION = "2026-09-01"; // change this when menu changes 11const VERSION_KEY = "af-menu-version"; 12 13(function resetMenuIfNeeded() { 14 const storedVersion = localStorage.getItem(VERSION_KEY); 15 16 if (storedVersion !== MENU_VERSION) { 17 // Remove cached menu 18 localStorage.removeItem("af-menu"); 19 localStorage.setItem(VERSION_KEY, MENU_VERSION); 20 21 // Remove any existing menu from DOM (desktop + mobile) 22 const existingHeader = document.getElementById("af-main-header"); 23 if (existingHeader) existingHeader.remove(); 24 25 console.log("Menu cache reset (new visitors / new version)"); 26 } 27})(); 28 29 // ------------------------- 30 // Loading screen 31 // ------------------------- 32 const loading = document.createElement('div'); 33 loading.id = 'af-loading-screen'; 34 document.body.appendChild(loading); 35 36 const loadingInner = document.createElement('div'); 37 loadingInner.id = 'af-loading-screen-inner'; 38 loading.appendChild(loadingInner); 39 loadingInner.innerHTML = '<i class="fi-rs-half-loading"></i>'; 40 41 // ------------------------- 42 // Detect language 43 // ------------------------- 44 let url = window.location.href; 45 let currentLang = url.includes("/en/") ? "en" : "af"; 46 47 // ------------------------- 48 // Cache system â ONE FILE 49 // ------------------------- 50 function saveMenu(lang, html) { 51 let stored = localStorage.getItem("af-menu"); 52 let obj = stored ? JSON.parse(stored) : { af: null, en: null, expiry: 0 }; 53 obj[lang] = html; 54 obj.expiry = Date.now() + 2 * 60 * 60 * 1000; // 2 hours 55 localStorage.setItem("af-menu", JSON.stringify(obj)); 56 } 57 58 function loadMenu(lang) { 59 let stored = localStorage.getItem("af-menu"); 60 if (!stored) return null; 61 62 let obj = JSON.parse(stored); 63 if (Date.now() > obj.expiry) { 64 localStorage.removeItem("af-menu"); 65 return null; 66 } 67 68 return obj[lang] || null; 69 } 70 71 // ------------------------- 72 // Move Search + Lang to mobile 73 // ------------------------- 74 function moveLangToMobile() { 75 const $lang = $("#af-lang-switch"); 76 const $soek = $("#af-soek"); 77 const $btns = $("#af-main-menu .af-btns"); 78 const $menuList = $("#af-main-menu .af-menu > ul"); 79 const $sluitaan = $("#af-sluit-aan"); 80 81 if (!$soek.find(".af-soek-text").length) { 82 $soek.append(` <span class="af-soek-text" style="padding-left: 5px;">${currentLang === "en" ? "Search" : "Soek"}</span>`); 83 } 84 85 if ($(window).width() < 1100) { 86 if (!$soek.parent().hasClass("af-menu")) $menuList.after($soek); 87 if (!$lang.parent().hasClass("af-menu")) $soek.after($lang); 88 if ($sluitaan.length && !$sluitaan.parent().hasClass("af-menu")) $lang.after($sluitaan); 89 } else { 90 if (!$lang.parent().hasClass("af-btns")) $btns.append($lang); 91 if (!$soek.parent().hasClass("af-btns")) $btns.append($soek); 92 } 93 94 if (currentLang === "en") { 95 $("#af-sluit-aan").text("Join now"); 96 97 const alt = $('link[rel="alternate"][hreflang="af"]').attr('href'); 98 if (alt) { 99 $("#af-lang-switch").text("Afrikaans").attr("href", alt); 100 } else { 101 $("#af-lang-switch").remove(); 102 } 103 } else { 104 const alt = $('link[rel="alternate"][hreflang="en"]').attr('href'); 105 if (alt) { 106 $("#af-lang-switch").attr("href", alt); 107 } else { 108 $("#af-lang-switch").remove(); 109 } 110 } 111 } 112 113 // ------------------------- 114 // Toggle mobile nav class 115 // ------------------------- 116 function toggleMobileNavClass() { 117 const isMobile = $(window).width() < 1100; 118 const $menu = $("#af-main-menu .af-menu"); 119 120 if (isMobile) { 121 $menu.addClass("af-mobile-nav"); 122 } else { 123 $menu.removeClass("af-mobile-nav"); 124 $menu.find("ul ul").hide(); 125 } 126 127 moveLangToMobile(); 128 } 129 130 // ------------------------- 131 // Insert menu and run layout fixes 132 // ------------------------- 133function insertMenu(htmlOrData, isCached) { 134 // Remove old header 135 const existingHeader = document.getElementById("af-main-header"); 136 if (existingHeader) existingHeader.remove(); 137 138 // If cached, fix links BEFORE insertion 139 if (isCached) { 140 // Replace all old profile links 141 htmlOrData = htmlOrData.replace(/my\.afriforum\.co\.za/g, "wereld.afriforum.co.za"); 142 143 // Parse into DOM 144 const tempDiv = document.createElement("div"); 145 tempDiv.innerHTML = htmlOrData.trim(); 146 const newHeader = tempDiv.querySelector("#af-main-header"); // more reliable than firstElementChild 147 if (newHeader) $("body").prepend(newHeader); 148 } else { 149 $("body").prepend(buildMenuHtml(htmlOrData)); 150 buildMenuTree(htmlOrData); 151 152 // Save a fixed version in cache 153 let htmlToSave = $("#af-main-header")[0].outerHTML.replace(/my\.afriforum\.co\.za/g, "wereld.afriforum.co.za"); 154 saveMenu(currentLang, htmlToSave); 155 } 156 157 $("#af-loading-screen").remove(); 158 159 requestAnimationFrame(() => { 160 toggleMobileNavClass(); 161 162 if ($("#wpadminbar").is(":visible")) { 163 const adminBarHeight = $("#wpadminbar").outerHeight(); 164 $("#af-main-header").css({ 165 "margin-top": adminBarHeight + "px", 166 "z-index": "9" 167 }); 168 } else { 169 $("#af-main-header").css("z-index", "999999"); 170 } 171 172 $("body").css("padding-top", $("#af-main-header").height() + "px"); 173 $(window).trigger("resize"); 174 }); 175} 176 177function fixProfileLinks() { 178 $("#af-main-header a").each(function() { 179 const href = $(this).attr("href"); 180 if (href && href.includes("my.afriforum.co.za")) { 181 $(this).attr("href", href.replace("my.afriforum.co.za", "wereld.afriforum.co.za")); 182 } 183 }); 184} 185 186 187function replaceProfileLinksInMenu() { 188 $("#af-main-header a").each(function() { 189 const href = $(this).attr("href"); 190 if (href && href.includes("my.afriforum.co.za")) { 191 $(this).attr("href", href.replace("my.afriforum.co.za", "wereld.afriforum.co.za")); 192 } 193 }); 194} 195 196 197 198 // ------------------------- 199 // Build menu HTML 200 // ------------------------- 201 function buildMenuHtml(data) { 202 return ` 203 <div id="af-main-header" name="${currentLang}"> 204 <div id="af-main-menu"> 205 <div class="af-container"> 206 <div class="af-logo"> 207 <img src="https://afriforum.co.za/wp-content/uploads/2020/09/[email protected]"/> 208 </div> 209 <div class="af-menu"> 210 <div id="af-mobile-close"><i class="bi bi-x-circle-fill"></i></div> 211 <ul></ul> 212 </div> 213 <div class="af-btns"> 214 <button id="af-open-nav"><i class="bi bi-grid-3x3-gap-fill"></i></button> 215 <a href="#" id="af-lang-switch">${currentLang === "en" ? "Afrikaans" : "English"}</a> 216 <a href="https://wereld.afriforum.co.za/af/auth/login" target="_blank" id="af-my-profile-nav">
216<i class="bi bi-person-circle"></i></a> 217 <a href="https://www.artikels.afriforum.co.za/?s=" target="_blank" id="af-soek"> 218 <i class="bi bi-search"></i> 219 </a> 220 <a href="https://sluit-aan.afriforum.co.za" id="af-sluit-aan">${currentLang === "en" ? "Join now" : "Sluit aan"}</a> 221 </div> 222 </div> 223 </div> 224 </div>`; 225 } 226 227 // ------------------------- 228 // Build menu tree 229 // ------------------------- 230 function buildMenuTree(data) { 231 const menuMap = {}; 232 const $root = $("#af-main-menu .af-menu ul"); 233 234 $(data).each(function (_, item) { 235 const $li = $(`<li class="af-menu-item-${item.ID}"> 236 <a href="${item.url}">${item.title}</a> 237 </li>`); 238 menuMap[item.ID] = $li; 239 if (item.menu_item_parent == 0) $root.append($li); 240 }); 241 242 $(data).each(function (_, item) { 243 if (item.menu_item_parent != 0) { 244 const $parent = menuMap[item.menu_item_parent]; 245 if ($parent) { 246 let $sub = $parent.children("ul").first(); 247 if ($sub.length === 0) { 248 $sub = $("<ul></ul>"); 249 $parent.append($sub); 250 } 251 $sub.append(menuMap[item.ID]); 252 } 253 } 254 }); 255 256 // Add chevron for items with submenus 257 $("#af-main-menu .af-menu li").each(function () { 258 if ($(this).children("ul").length > 0) { 259 $(this).children("a").append('<span class="submenu-toggle"><i class="bi bi-chevron-down"></i></span>'); 260 } 261 }); 262 } 263 264 // ------------------------- 265 // Load cached menu or fetch 266 // ------------------------- 267 const menuID = currentLang === "en" ? 181 : 179; 268 let cachedHTML = loadMenu(currentLang); 269 270 if (cachedHTML) { 271 insertMenu(cachedHTML, true); 272 } else { 273 $.getJSON(`https://afriforum.co.za/wp-json/custom/v1/menus/${menuID}`, function (data) { 274 insertMenu(data, false); 275 }); 276 } 277 278 // ------------------------- 279 // Mobile nav toggle 280 // ------------------------- 281 $(document).on('click', '#af-open-nav', function () { 282 $('#af-main-menu .af-menu').addClass('af-mobile-nav-open'); 283 }); 284 285 $(document).on('click', '#af-mobile-close', function () { 286 $('#af-main-menu .af-menu').removeClass('af-mobile-nav-open'); 287 }); 288 289 // ------------------------- 290 // Desktop hover for submenu 291 // ------------------------- 292 $(document).on({ 293 mouseenter: function () { 294 if ($(window).width() < 1100) return; 295 clearTimeout($(this).data('hideTimer')); 296 $(this).find('ul').css('display', ''); 297 }, 298 mouseleave: function () { 299 if ($(window).width() < 1100) return; 300 const li = $(this); 301 const timer = setTimeout(function () { 302 li.find('ul').css('display', 'none'); 303 }, 200); 304 li.data('hideTimer', timer); 305 } 306 }, '#af-main-menu .af-menu > ul > li'); 307 308 // ------------------------- 309 // Mobile submenu toggle 310 // ------------------------- 311 $(document).on('click', '#af-main-menu .af-menu li > a', function (e) { 312 const $li = $(this).parent('li'); 313 const $submenu = $li.children('ul'); 314 315 if ($submenu.length > 0 && $(window).width() < 1100) { 316 e.preventDefault(); 317 $submenu.slideToggle(); 318 $li.siblings('li').children('ul:visible').slideUp(); 319 } 320 }); 321 322 // ------------------------- 323 // Window resize 324 // ------------------------- 325 $(window).resize(function () { 326 toggleMobileNavClass(); 327 }); 328 329 330 331});*/ 332 333// ------------------------- 334// DOM Ready wrapper 335// ------------------------- 336document.addEventListener("DOMContentLoaded", function () { 337 338 // ------------------------- 339 // Menu cache versioning 340 // ------------------------- 341 const MENU_VERSION = "2026-09-01"; // change this when menu changes 342 const VERSION_KEY = "af-menu-version"; 343 344 (function resetMenuIfNeeded() { 345 const storedVersion = localStorage.getItem(VERSION_KEY); 346 347 if (storedVersion !== MENU_VERSION) { 348 // Remove cached menu 349 localStorage.removeItem("af-menu"); 350 localStorage.setItem(VERSION_KEY, MENU_VERSION); 351 352 // Remove any existing menu from DOM (desktop + mobile) 353 const existingHeader = document.getElementById("af-main-header"); 354 if (existingHeader) existingHeader.remove(); 355 356 console.log("Menu cache reset (new visitors / new version)"); 357 } 358 })();
359 360 // ------------------------- 361 // Loading screen 362 // ------------------------- 363 const loading = document.createElement('div'); 364 loading.id = 'af-loading-screen'; 365 document.body.appendChild(loading); 366 367 const loadingInner = document.createElement('div'); 368 loadingInner.id = 'af-loading-screen-inner'; 369 loading.appendChild(loadingInner); 370 loadingInner.innerHTML = '<i class="fi-rs-half-loading"></i>'; 371 372 // ------------------------- 373 // Detect language 374 // ------------------------- 375 let url = window.location.href; 376 let currentLang = url.includes("/en/") ? "en" : "af"; 377 378 // ------------------------- 379 // Cache system â ONE FILE 380 // ------------------------- 381 function saveMenu(lang, html) { 382 let stored = localStorage.getItem("af-menu"); 383 let obj = stored ? JSON.parse(stored) : { af: null, en: null, expiry: 0 }; 384 obj[lang] = html; 385 obj.expiry = Date.now() + 2 * 60 * 60 * 1000; // 2 hours 386 localStorage.setItem("af-menu", JSON.stringify(obj)); 387 } 388 389 function loadMenu(lang) { 390 let stored = localStorage.getItem("af-menu"); 391 if (!stored) return null; 392 393 let obj = JSON.parse(stored); 394 if (Date.now() > obj.expiry) { 395 localStorage.removeItem("af-menu"); 396 return null; 397 } 398 399 return obj[lang] || null; 400 } 401 402 // ------------------------- 403 // Move Search + Lang to mobile 404 // ------------------------- 405 function moveLangToMobile() { 406 const $lang = $("#af-lang-switch"); 407 const $soek = $("#af-soek"); 408 const $btns = $("#af-main-menu .af-btns"); 409 const $menuList = $("#af-main-menu .af-menu > ul"); 410 const $sluitaan = $("#af-sluit-aan"); 411 412 if (!$soek.find(".af-soek-text").length) { 413 $soek.append(` <span class="af-soek-text" style="padding-left: 5px;">${currentLang === "en" ? "Search" : "Soek"}</span>`); 414 } 415 416 if ($(window).width() < 1100) { 417 if (!$soek.parent().hasClass("af-menu")) $menuList.after($soek); 418 if (!$lang.parent().hasClass("af-menu")) $soek.after($lang); 419 if ($sluitaan.length && !$sluitaan.parent().hasClass("af-menu")) $lang.after($sluitaan); 420 } else { 421 if (!$lang.parent().hasClass("af-btns")) $btns.append($lang); 422 if (!$soek.parent().hasClass("af-btns")) $btns.append($soek); 423 } 424 425 if (currentLang === "en") { 426 $("#af-sluit-aan").text("Join now"); 427 428 const alt = $('link[rel="alternate"][hreflang="af"]').attr('href'); 429 if (alt) { 430 $("#af-lang-switch").text("Afrikaans").attr("href", alt); 431 } else { 432 $("#af-lang-switch").remove(); 433 } 434 } else { 435 const alt = $('link[rel="alternate"][hreflang="en"]').attr('href'); 436 if (alt) { 437 $("#af-lang-switch").attr("href", alt); 438 } else { 439 $("#af-lang-switch").remove(); 440 } 441 } 442 } 443 444 // ------------------------- 445 // Toggle mobile nav class 446 // ------------------------- 447 function toggleMobileNavClass() { 448 const isMobile = $(window).width() < 1100; 449 const $menu = $("#af-main-menu .af-menu"); 450 451 if (isMobile) { 452 $menu.addClass("af-mobile-nav"); 453 } else { 454 $menu.removeClass("af-mobile-nav"); 455 $menu.find("ul ul").hide(); 456 } 457 458 moveLangToMobile(); 459 } 460 461 // ------------------------- 462 // Replace my.afriforum links 463 // ------------------------- 464 function replaceProfileLinksInMenu() { 465 $("#af-main-header a").each(function () { 466 const href = $(this).attr("href"); 467 if (href && href.includes("my.afriforum.co.za")) { 468 $(this).attr("href", href.replace("my.afriforum.co.za", "wereld.afriforum.co.za")); 469 } 470 }); 471 } 472 473 // ------------------------- 474 // Insert menu and run layout fixes 475 // ------------------------- 476 function insertMenu(htmlOrData, isCached) { 477 // Remove old header 478 const existingHeader = document.getElementById("af-main-header"); 479 if (existingHeader) existingHeader.remove(); 480 481 if (isCached) { 482 // Fix links in cached HTML string 483 htmlOrData = htmlOrData.replace(/my\.afriforum\.co\.za/g, "wereld.afriforum.co.za"); 484 485 // Insert into DOM 486 const tempDiv = document.createElement("div"); 487 tempDiv.innerHTML = htmlOrData.trim(); 488 const newHeader = tempDiv.querySelector("#af-main-header"); 489 if (newHeader) $("body").prepend(newHeader); 490 491 // Ensure any leftover links are fixed 492 replaceProfileLinksInMenu(); 493 } else { 494 // Build new menu DOM 495 $("body").prepend(buildMenuHtml(htmlOrData)); 496 buildMenuTree(htmlOrData); 497 498 // Fix any links inside menu items 499 replaceProfileLinksInMenu(); 500 501 // Save fixed HTML in cache 502 let htmlToSave = $("#af-main-header")[0].outerHTML; 503 saveMenu(currentLang, htmlToSave); 504 } 505 506 $("#af-loading-screen").remove(); 507 508 requestAnimationFrame(() => { 509 toggleMobileNavClass(); 510 511 if ($("#wpadminbar").is(":visible")) { 512 const adminBarHeight = $("#wpadminbar").outerHeight(); 513 $("#af-main-header").css({ 514 "margin-top": adminBarHeight + "px", 515 "z-index": "9" 516 }); 517 } else { 518 $("#af-main-header").css("z-index", "999999"); 519 } 520 521 $("body").css("padding-top", $("#af-main-header").height() + "px"); 522 $(window).trigger("resize"); 523 });
524 } 525 526 // ------------------------- 527 // Build menu HTML 528 // ------------------------- 529 function buildMenuHtml(data) { 530 return ` 531 <div id="af-main-header" name="${currentLang}"> 532 <div id="af-main-menu"> 533 <div class="af-container"> 534 <div class="af-logo"> 535 <img src="https://afriforum.co.za/wp-content/uploads/2020/09/[email protected]"/> 536 </div> 537 <div class="af-menu"> 538 <div id="af-mobile-close"><i class="bi bi-x-circle-fill"></i></div> 539 <ul></ul> 540 </div> 541 <div class="af-btns"> 542 <button id="af-open-nav"><i class="bi bi-grid-3x3-gap-fill"></i></button> 543 <a href="#" id="af-lang-switch">${currentLang === "en" ? "Afrikaans" : "English"}</a> 544 <a href="https://wereld.afriforum.co.za/af/auth/login" target="_blank" id="af-my-profile-nav"><i class="bi bi-person-circle"></i></a> 545 <a href="https://www.artikels.afriforum.co.za/?s=" target="_blank" id="af-soek"><i class="bi bi-search"></i></a> 546 <a href="https://sluit-aan.afriforum.co.za" id="af-sluit-aan">${currentLang === "en" ? "Join now" : "Sluit aan"}</a> 547 </div> 548 </div> 549 </div> 550 </div>`; 551 } 552 553 // ------------------------- 554 // Build menu tree 555 // ------------------------- 556 function buildMenuTree(data) { 557 const menuMap = {}; 558 const $root = $("#af-main-menu .af-menu ul"); 559 560 $(data).each(function (_, item) { 561 const $li = $(`<li class="af-menu-item-${item.ID}"><a href="${item.url}">${item.title}</a></li>`); 562 menuMap[item.ID] = $li; 563 if (item.menu_item_parent == 0) $root.append($li); 564 }); 565 566 $(data).each(function (_, item) { 567 if (item.menu_item_parent != 0) { 568 const $parent = menuMap[item.menu_item_parent]; 569 if ($parent) { 570 let $sub = $parent.children("ul").first(); 571 if ($sub.length === 0) { 572 $sub = $("<ul></ul>"); 573 $parent.append($sub); 574 } 575 $sub.append(menuMap[item.ID]); 576 } 577 } 578 }); 579 580 // Add chevron for items with submenus 581 $("#af-main-menu .af-menu li").each(function () { 582 if ($(this).children("ul").length > 0) { 583 $(this).children("a").append('<span class="submenu-toggle"><i class="bi bi-chevron-down"></i></span>'); 584 } 585 }); 586 } 587 588 // ------------------------- 589 // Load cached menu or fetch 590 // ------------------------- 591 const menuID = currentLang === "en" ? 181 : 179; 592 let cachedHTML = loadMenu(currentLang); 593 594 if (cachedHTML) { 595 insertMenu(cachedHTML, true); 596 } else { 597 $.getJSON(`https://afriforum.co.za/wp-json/custom/v1/menus/${menuID}`, function (data) { 598 insertMenu(data, false); 599 }); 600 } 601 602 // ------------------------- 603 // Mobile nav toggle 604 // ------------------------- 605 $(document).on('click', '#af-open-nav', function () { 606 $('#af-main-menu .af-menu').addClass('af-mobile-nav-open'); 607 }); 608 609 $(document).on('click', '#af-mobile-close', function () { 610 $('#af-main-menu .af-menu').removeClass('af-mobile-nav-open'); 611 }); 612 613 // ------------------------- 614 // Desktop hover for submenu 615 // ------------------------- 616 $(document).on({ 617 mouseenter: function () { 618 if ($(window).width() < 1100) return; 619 clearTimeout($(this).data('hideTimer')); 620 $(this).find('ul').css('display', ''); 621 }, 622 mouseleave: function () { 623 if ($(window).width() < 1100) return; 624 const li = $(this); 625 const timer = setTimeout(function () { 626 li.find('ul').css('display', 'none'); 627 }, 200); 628 li.data('hideTimer', timer); 629 } 630 }, '#af-main-menu .af-menu > ul > li'); 631 632 // ------------------------- 633 // Mobile submenu toggle 634 // ------------------------- 635 $(document).on('click', '#af-main-menu .af-menu li > a', function (e) { 636 const $li = $(this).parent('li'); 637 const $submenu = $li.children('ul'); 638 639 if ($submenu.length > 0 && $(window).width() < 1100) { 640 e.preventDefault(); 641 $submenu.slideToggle(); 642 $li.siblings('li').children('ul:visible').slideUp(); 643 } 644 }); 645 646 // ------------------------- 647 // Window resize 648 // ------------------------- 649 $(window).resize(function () { 650 toggleMobileNavClass(); 651 }); 652 653});
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.