PageSourceSearch

https://www.moe.gov.sa/Style%20Library/portal/lib/MOE.Loader.js

js moe.gov.sa collected 2026-10-01 10:31:33 UTC 5,491 bytes, 139 lines download raw bytes

1/**
2 * MOE.Loader.js — Page preload overlay controller
3 * Plain IIFE — no module — loads at the start of <body>.
4 *
5 * 1. Immediately injects #moe-page-loader into document.body before any page
6 *    content is parsed (script runs synchronously at body start).
7 *    Asset path is resolved from window.MOE_FRAGMENT_BASE (set per-page in <head>).
8 *    Canonical markup lives in components/fragments/page-loader.html.
9 *
10 * 2. Waits for 5 signals before hiding the overlay:
11 *   moe:fragments-loaded  — all HTML fragments injected (MOE.LoadComponents.js)
12 *   moe:alpine-started    — Alpine.start() complete (page entry JS)
13 *   moe:nav-loaded        — nav API resolved or failed (page entry JS)
14 *   moe:footer-loaded     — footer API resolved or failed (page entry JS)
15 *   fonts                 — HugeIcons font loaded via document.fonts.load()
16 *
17 * Hard timeout (MAX_WAIT_MS) hides the loader regardless if signals are late.
18 */
19(function () {
20  "use strict";
21
22  // ── 1. Inject loader overlay ─────────────────────────────────────────
23  // Runs synchronously when the script executes at the start of <body>.
24  // window.MOE_FRAGMENT_BASE is already set in <head> before this script loads.
25  // MOE.LangDir.js (also in <head>) has already read localStorage('moe-lang'),
26  // so we can safely use the same key here to pick the correct label language.
27  (function _inject() {
28    if (document.getElementById("moe-page-loader")) return; // guard against double-run
29    var base =
30      (typeof window.MOE_BASE_PATH === "string" && window.MOE_BASE_PATH)
31        ? window.MOE_BASE_PATH + "/"
32        : (typeof window.MOE_FRAGMENT_BASE === "string" && window.MOE_FRAGMENT_BASE)
33          ? window.MOE_FRAGMENT_BASE
34          : "../";
35    var logoSrc = base + "assets/images/moe-logo.svg";
36    var ariaLabel = localStorage.getItem("moe-lang") === "en"
37      ? "Page loading"
38      : "\u062c\u0627\u0631\u064a \u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0635\u0641\u062d\u0629";
39    document.body.insertAdjacentHTML(
40      "afterbegin",
41      [
42        '<div id="moe-page-loader" aria-hidden="true" role="status" aria-label="' +
43          ariaLabel +
44          '">',
45        '  <div class="moe-loader-ring-wrap">',
46        '    <div class="moe-loader-ring"></div>',
47        '    <img src="' +
48          logoSrc +
49          '" alt="" class="moe-loader-logo" width="90" height="60" />',
50        "  </div>",
51        "</div>",
52      ].join("\n"),
53    );
54  })();
55
56  // ── 2. Signal system ─────────────────────────────────────────────────
57
58  var MAX_WAIT_MS = 4000;
59
60  var _hidden = false;
61  var _checks = {
62    fragments: false,
63    alpine: false,
64    nav: false,
65    footer: false,
66    fonts: false,
67  };
68
69  function _tryHide() {
70    if (_hidden) return;
71    if (_checks.fragments && _checks.alpine && _checks.nav && _checks.footer && _checks.fonts) {
72      _hide();
73    }
74  }
75
76  function _hide() {
77    if (_hidden) return;
78    _hidden = true;
79
80    var el = document.getElementById("moe-page-loader");
81    if (!el) return;
82
83    el.classList.add("moe-loader--out");
84    // Remove from DOM after CSS fade-out completes
85    setTimeout(function () {
86      if (el.parentNode) el.parentNode.removeChild(el);
87    }, 500);
88  }
89
90  function _mark(key) {
91    _checks[key] = true;
92    _tryHide();
93  }
94
95  // ── Signal listeners ─────────────────────────────────────────────────
96
97  window.addEventListener("moe:fragments-loaded", function () {
98    _mark("fragments");
99  });
100  window.addEventListener("moe:alpine-started", function () {
101    _mark("alpine");
102  });
103  window.addEventListener("moe:nav-loaded", function () {
104    _mark("nav");
105  });
106  window.addEventListener("moe:footer-loaded", function () {
107    _mark("footer");
108  });
109
110  // ── Font loading check ────────────────────────────────────────────────
111  // Wait for the HugeIcons font before hiding the overlay — prevents icons
112  // from briefly rendering as CJK/tofu fallback glyphs while the font loads.
113  if (document.fonts && typeof document.fonts.load === 'function') {
114    document.fonts.load('1em "hgi-stroke-rounded"').then(function () {
115      _mark('fonts');
116    }).catch(function () {
117      _mark('fonts');
118    });
119  } else {
120    _mark('fonts'); // Font Loading API not supported — skip, hard timeout covers it
121  }
122
123  // ── Hard timeout ─────────────────────────────────────────────────────
124
125  setTimeout(function () {
126    if (!_hidden) {
127      console.warn(
128        "[MOE] Loader timeout — forcing hide. Missing signals:",
129        JSON.stringify(_checks),
130      );
131      _hide();
132    }
133  }, MAX_WAIT_MS);
134
135  // ── Public debug handle ──────────────────────────────────────────────
136
137  window.MOE = window.MOE || {};
138  window.MOE.Loader = window.MOE.Loader || { checks: _checks };
139})();

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.