PageSourceSearch

https://toeps.afriforum.co.za/af-menu/script.js

js afriforum.co.za collected 2026-09-24 09:37:04 UTC 21,467 bytes, 653 lines download raw bytes

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.