1<!DOCTYPE html> 2<html lang="en"> 3 4<head> 5 <base href="/"> 6 <meta charset="UTF-8"> 7 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 8 9 <!-- Security Headers --> 10 <meta http-equiv="X-XSS-Protection" content="1; mode=block"> 11 <meta http-equiv="X-Content-Type-Options" content="nosniff"> 12 <meta name="referrer" content="strict-origin-when-cross-origin"> 13 <meta http-equiv="Content-Security-Policy" content="default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval'; object-src 'none'; base-uri 'self';"> 14 15 <title>Department of Education | Papua New Guinea</title> 16 17 <!-- DOMPurify for XSS Protection --> 18
18<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
18 19 20 <!-- Site Health & Error Tracker --> 21
21<script> 22 window.addEventListener('error', function (e) { 23 const errorMsg = e.message || 'Unknown JS Error'; 24 const stack = e.error && e.error.stack ? e.error.stack.split('\n')[1] || '' : ''; 25 26 fetch('api/error_logger.php?action=log', { 27 method: 'POST', 28 headers: { 'Content-Type': 'application/json' }, 29 body: JSON.stringify({ 30 type: 'FRONTEND', 31 message: errorMsg + ' ' + stack, 32 url: window.location.href, 33 line_number: e.lineno ? (e.lineno + ':' + e.colno) : 'N/A' 34 }) 35 }).catch(err => console.error('Error logging failed', err)); 36 }); 37 38 window.addEventListener('unhandledrejection', function (e) { 39 const errorMsg = e.reason ? (e.reason.message || e.reason) : 'Unhandled Promise Rejection'; 40 fetch('api/error_logger.php?action=log', { 41 method: 'POST', 42 headers: { 'Content-Type': 'application/json' }, 43 body: JSON.stringify({ 44 type: 'FRONTEND', 45 message: 'Promise Rejection: ' + errorMsg, 46 url: window.location.href, 47 line_number: 'N/A' 48 }) 49 }).catch(err => console.error('Error logging failed', err)); 50 }); 51 </script>
51 52 <meta name="description" 53 content="National Department of Education Papua Guinea - Providing quality education, curriculum resources, and administrative services for all PNG citizens."> 54 <meta name="keywords" 55 content="Education PNG, Papua New Guinea Schools, DoE PNG, Curriculum Resources, Teacher Resources, PNG Education Portal"> 56 <link rel="canonical" href="https://education.gov.pg"> 57 58 <!-- Cache Control: Force browser to always check for the newest index.html --> 59 <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> 60 <meta http-equiv="Pragma" content="no-cache"> 61 <meta http-equiv="Expires" content="0"> 62 63 <!-- Open Graph / Facebook --> 64 <meta property="og:type" content="website"> 65 <meta property="og:url" content="https://education.gov.pg/"> 66 <meta property="og:title" content="Department of Education | Papua New Guinea"> 67 <meta property="og:description" 68 content="Providing quality education and resources for all citizens of Papua New Guinea."> 69 <meta property="og:image" content="https://education.gov.pg/assets/images/logo/DoE-Logo.png"> 70 71 <!-- Twitter --> 72 <meta property="twitter:card" content="summary_large_image"> 73 <meta property="twitter:url" content="https://education.gov.pg/"> 74 <meta property="twitter:title" content="Department of Education | Papua New Guinea"> 75 <meta property="twitter:description" 76 content="Providing quality education and resources for all citizens of Papua New Guinea."> 77 <meta property="twitter:image" content="https://education.gov.pg/assets/images/logo/DoE-Logo.png"> 78 79 <!-- Styles & Fonts --> 80 <link rel="preconnect" href="https://fonts.googleapis.com"> 81 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 82 <link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700&family=Outfit:wght@300;400;500;600;700;800&display=swap" 83 rel="stylesheet"> 84 <link rel="icon" id="favicon" type="image/png" href="assets/images/fav-icon/android-chrome-192x192.png"> 85 <link rel="stylesheet" href="styles.css?v=2026.22"> 86 87 <!-- Lucide Icons --> 88
88<script src="https://unpkg.com/lucide@latest" defer></script>
88 89 90 <!-- Preload Critical Assets --> 91 <link rel="preload" href="components/api.js?v=2026.20" as="script"> 92 <link rel="preload" href="router.js?v=2026.20" as="script"> 93 <link rel="preload" href="components/home.js?v=2026.20" as="script"> 94 95 <meta name="theme-color" content="#0a0d14"> 96</head> 97 98<body> 99 <!-- Loading Animation Overlay --> 100 <div id="loading-overlay"> 101 <div class="loader-content"> 102 <div class="crest-container"> 103 <img src="assets/images/logo/DoE-Logo.png?v=2" alt="Department Crest" class="loader-crest"> 104 <div class="loader-ripple"></div> 105 <div class="loader-spinner"></div> 106 </div> 107 <h2 class="loader-title">Department of Education</h2> 108 <p class="loader-subtitle">Papua New Guinea</p> 109 <div class="loader-progress-bar"> 110 <div class="loader-progress-line"></div> 111 </div> 112 </div> 113 </div> 114 115 <div id="toast-container" class="toast-container"></div> 116 <div id="glow-blob"></div> 117 118 <header class="glass-header"> 119 <!-- Top Branding Banner (Teaching Service Commission Style) --> 120 <div class="top-branding-banner"> 121 <div class="banner-inner"> 122 <!-- Left Emblem: Papua New Guinea Coat of Arms --> 123 <div class="banner-logo-container left-logo"> 124 <img id="top-brand-logo-left" src="assets/images/logo/logo-2.png" alt="Papua New Guinea National Emblem" class="banner-logo"> 125 </div> 126 127 <!-- Center Content: Title, Country, and Tagline --> 128 <div class="banner-text-center"> 129 <div class="banner-country-wrapper"> 130 <span class="banner-line"></span> 131 <span class="banner-country">PAPUA NEW GUINEA</span> 132 <span class="banner-line right"></span> 133 </div> 134 <h1 class="banner-title">DEPARTMENT OF EDUCATION</h1> 135 <div class="banner-divider"> 136 <span class="divider-line"></span> 137 <span class="divider-diamond"></span> 138 <span class="divider-line right"></span> 139 </div> 140 <p class="banner-tagline">"QUALITY EDUCATION FOR ALL"</p> 141 </div> 142 143 <!-- Right Emblem: Department of Education Crest --> 144 <div class="banner-logo-container right-logo"> 145 <img id="top-brand-logo-right" src="assets/images/logo/DoE-Logo.png" alt="Department of Education Crest" class="banner-logo"> 146 </div> 147 </div> 148 </div> 149 150 <div class="header-container"> 151 <a href="/home" class="logo"> 152 <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" 153 alt="Department Logo" id="header-logo" class="logo-img-placeholder"> 154 <div class="logo-text"> 155 <h1 id="header-title">Department of Education</h1> 156 <span id="header-subtitle">Papua New Guinea</span> 157 </div> 158 </a> 159 160 <nav class="main-nav"> 161 <!-- Mobile Menu Header (logo + close btn) --> 162 <div class="mobile-nav-header"> 163 <a href="/home" class="mobile-nav-logo"> 164 <img id="mobile-nav-logo-img" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="DoE Logo"> 165 <div class="mobile-nav-logo-text"> 166 <span class="mobile-nav-title">Department of Education</span> 167 <span class="mobile-nav-subtitle">Papua New Guinea</span> 168 </div> 169 </a> 170 <button class="mobile-nav-close" id="mobile-nav-close-btn" aria-label="Close Menu">×</button> 171 </div> 172 173 <!-- Nav Links --> 174 <div class="mobile-nav-links"> 175 <!-- Home --> 176 <a href="/home" class="nav-link">Home</a> 177 178 <!-- About --> 179 <div class="dropdown"> 180 <a href="/about" class="nav-link">About <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 181 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" 182 stroke-linejoin="round" class="lucide lucide-chevron-down nav-icon-sm arrow"> 183 <path d="m6 9 6 6 6-6" /> 184 </svg></a> 185 <div class="dropdown-content glass-panel"> 186 <a href="/about" class="dropdown-item">Mission & Vision</a> 187 <a href="/activities" class="dropdown-item">Programs & Activities</a> 188 <a href="/careers" class="dropdown-item">Job Opportunities</a> 189 <a href="/pngetv75" class="dropdown-item highlight-primary">Vision 2075</a> 190 <a href="/leap" class="dropdown-item highlight-primary">PNG LEAP</a> 191 <a href="/gesi" class="dropdown-item highlight-primary">GESI</a> 192 <a href="/faq" class="dropdown-item highlight-primary">FAQ (Help Center)</a> 193 <a href="http://eduportal.educationpng.gov.pg/" target="_blank" rel="noopener" class="dropdown-item highlight-primary">DoE Portal</a> 194 </div> 195 </div> 196 197 <!-- Schools --> 198 <div class="dropdown"> 199 <a href="javascript:void(0)" class="nav-link">Schools <svg xmlns="http://www.w3.org/2000/svg"
200 viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 201 stroke-linecap="round" stroke-linejoin="round" 202 class="lucide lucide-chevron-down nav-icon-sm arrow"> 203 <path d="m6 9 6 6 6-6" /> 204 </svg></a> 205 <div class="dropdown-content glass-panel"> 206 <a href="http://apps.education.gov.pg:8080/ords/f?p=2024:LOGIN_DESKTOP" target="_blank" 207 rel="noopener" class="dropdown-item">EMIS System</a> 208 <a href="http://mypng.school/" target="_blank" rel="noopener" 209 class="dropdown-item">MyPNGSchool</a> 210 <a href="/gfep" class="dropdown-item highlight-primary">GFEP</a> 211 </div> 212 </div> 213 214 <!-- Curriculum --> 215 <div class="dropdown"> 216 <a href="javascript:void(0)" class="nav-link">Curriculum <svg xmlns="http://www.w3.org/2000/svg"
217 viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 218 stroke-linecap="round" stroke-linejoin="round" 219 class="lucide lucide-chevron-down nav-icon-sm arrow"> 220 <path d="m6 9 6 6 6-6" /> 221 </svg></a> 222 <div class="dropdown-content glass-panel"> 223 <a href="/elementary" class="dropdown-item">Prep</a> 224 <a href="/primary" class="dropdown-item">Primary</a> 225 <a href="/secondary" class="dropdown-item">Secondary</a> 226 <a href="/textbooks" class="dropdown-item">Textbooks & Teacher Guides</a> 227 </div> 228 </div> 229 230 <!-- Services --> 231 <div class="dropdown"> 232 <a href="javascript:void(0)" class="nav-link">Services <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 233 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" 234 stroke-linejoin="round" class="lucide lucide-chevron-down nav-icon-sm arrow"> 235 <path d="m6 9 6 6 6-6" /> 236 </svg></a> 237 <div class="dropdown-content glass-panel"> 238 <!-- For Teachers --> 239 <div class="nested-dropdown" id="services-teachers-nested"> 240 <div class="dropdown-item nested-trigger" onclick="toggleNested(event)"> 241 For Teachers <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 242 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" 243 stroke-linejoin="round" class="lucide lucide-chevron-right nav-icon-sm arrow"> 244 <path d="m9 18 6-6-6-6" /> 245 </svg> 246 </div> 247 <div class="nested-content"> 248 <a href="http://apps.education.gov.pg:8081/ords/f?p=144" target="_blank" class="dropdown-item sub-item">MyPaySlip Portal</a> 249 <a href="/contact" class="dropdown-item sub-item">Pay & Allowance Enquiries</a> 250 <a href="/teachers" class="dropdown-item sub-item">Teacher Postings & TSC Appts</a> 251 <a href="/teachers" class="dropdown-item sub-item">Professional Training</a> 252 <a href="/teachers" class="dropdown-item sub-item">Teacher Information</a> 253 </div> 254 </div> 255 256 <!-- For Students & Parents --> 257 <div class="nested-dropdown" id="services-students-nested"> 258 <div class="dropdown-item nested-trigger" onclick="toggleNested(event)"> 259 For Students & Parents <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 260 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" 261 stroke-linejoin="round" class="lucide lucide-chevron-right nav-icon-sm arrow"> 262 <path d="m9 18 6-6-6-6" /> 263 </svg> 264 </div> 265 <div class="nested-content"> 266 <a href="/elementary" class="dropdown-item sub-item">FODE Distance Education</a> 267 <a href="/elementary" class="dropdown-item sub-item">e-Learning Hub</a> 268 <a href="http://apps.education.gov.pg:8081/ords/f?p=103" target="_blank" class="dropdown-item sub-item">Grade 11 Online Selection</a> 269 <a href="/publications" class="dropdown-item sub-item">Replacement Certificates (MSD)</a> 270 <a href="/careers" class="dropdown-item sub-item">Scholarships</a> 271 </div> 272 </div> 273 274 <!-- For School Administration --> 275 <div class="nested-dropdown" id="services-admin-nested"> 276 <div class="dropdown-item nested-trigger" onclick="toggleNested(event)"> 277 For School Administration <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 278 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" 279 stroke-linejoin="round" class="lucide lucide-chevron-right nav-icon-sm arrow"> 280 <path d="m9 18 6-6-6-6" /> 281 </svg> 282 </div> 283 <div class="nested-content"> 284 <a href="http://apps.education.gov.pg:8080/ords/f?p=2024
284:LOGIN_DESKTOP" target="_blank" class="dropdown-item sub-item">EMIS & School Census</a> 285 <a href="/forms" class="dropdown-item sub-item">School Registration Guide</a> 286 <a href="/about" class="dropdown-item sub-item">Inspections & Quality Standards</a> 287 <a href="/provincial-sites" class="dropdown-item sub-item">Provincial Sites</a> 288 </div> 289 </div> 290 </div> 291 </div> 292 293 <!-- News --> 294 <div class="dropdown"> 295 <a href="/news" class="nav-link">News <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 296 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" 297 stroke-linejoin="round" class="lucide lucide-chevron-down nav-icon-sm arrow"> 298 <path d="m6 9 6 6 6-6" /> 299 </svg></a> 300 <div class="dropdown-content glass-panel"> 301 <a href="/news" class="dropdown-item">Latest News</a> 302 <a href="/press-release" class="dropdown-item">Press Releases</a> 303 <a href="/gallery" class="dropdown-item">Event Gallery</a> 304 <a href="/calendars" class="dropdown-item">Term Dates & Calendars</a> 305 </div> 306 </div> 307 308 <!-- Resources --> 309 <div class="dropdown"> 310 <a href="javascript:void(0)" class="nav-link">Resources <svg xmlns="http://www.w3.org/2000/svg"
311 viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 312 stroke-linecap="round" stroke-linejoin="round" 313 class="lucide lucide-chevron-down nav-icon-sm arrow"> 314 <path d="m6 9 6 6 6-6" /> 315 </svg></a> 316 <div class="dropdown-content glass-panel"> 317 <a href="/publications" class="dropdown-item">Publications & Reports</a> 318 <a href="/open-data" class="dropdown-item highlight-accent">Open Data</a> 319 <a href="/app-gallery" class="dropdown-item highlight-accent">Digital Applications</a> 320 <a href="/forms" class="dropdown-item">Forms & Downloads</a> 321 <a href="https://fode.education.gov.pg/" class="dropdown-item" target="_blank" rel="noopener noreferrer">FODE Website</a> 322 </div> 323 </div> 324 325 <!-- Contact --> 326 <a href="/contact" class="nav-link">Contact</a> 327 328 <span class="desktop-search-divider hide-on-mobile" style="color: rgba(255,255,255,0.3); margin: 0 0.5rem; display: flex; align-items: center; cursor: default; user-select: none;">|</span> 329 330 <!-- Search Icon (Desktop) --> 331 <a href="javascript:void(0)" id="header-search-icon" class="nav-link hide-on-mobile" aria-label="Search" style="gap: 0.4rem; padding-left: 0.5rem;"> 332 <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" 333 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 334 class="lucide lucide-search"> 335 <circle cx="11" cy="11" r="8"></circle> 336 <line x1="21" y1="21" x2="16.65" y2="16.65"></line> 337 </svg> 338 <span style="font-weight: 500;">Search</span> 339 </a> 340 </div> 341 342 </nav> 343 344 <div class="header-actions"> 345 <button class="mobile-toggle" id="hamburger-toggle" aria-label="Toggle Menu"> 346 <span></span><span></span><span></span> 347 </button> 348 </div> 349 </div> 350 </header> 351 352 <!-- Search Overlay --> 353 <div class="search-overlay" id="search-overlay"> 354 <!-- Close button is a direct child of overlay, NOT inner - prevents transform breaking position:fixed --> 355 <button class="search-close-btn" id="search-close-btn" aria-label="Close Search">×</button> 356 <div class="search-overlay-inner"> 357 <h2 class="search-overlay-title">Search the Department of Education</h2> 358 <div class="search-input-wrapper"> 359 <svg class="search-input-icon" viewBox="0 0 24 24" width="22" height="22" fill="none" 360 stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 361 <circle cx="11" cy="11" r="8" /> 362 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 363 </svg> 364 <input type="text" id="site-search-input" class="search-input" 365 placeholder="Type to search pages, news, publications..." autocomplete="off"> 366 </div> 367 <div class="search-results" id="search-results"> 368 <div class="search-placeholder-text">Start typing to find pages, news articles, forms, and more...</div> 369 </div> 370 </div> 371 </div> 372 373 <!-- Gallery Modal (Album/Category View) --> 374 <div class="gallery-modal" id="gallery-modal"> 375 <div class="gallery-modal-inner"> 376 <span class="gallery-modal-close" id="gallery-modal-close">×</span> 377 <div id="gallery-album-header" class="gallery-album-header"> 378 <h2 id="gallery-album-title" class="gallery-album-title"></h2> 379 <p id="gallery-album-desc" class="gallery-album-desc"></p> 380 </div> 381 <div class="gallery-modal-grid" id="gallery-modal-grid"> 382 <!-- Category images will be injected here --> 383 </div> 384 </div> 385 </div> 386 387 <div class="detail-modal" id="detail-modal">
388 <span class="detail-modal-close" id="detail-modal-close">×</span> 389 <img class="detail-modal-content" id="detail-modal-img" alt="Detailed event image view"> 390 </div> 391 392 <main id="app-content"> 393 <!-- SPA Content injected here by router.js --> 394 </main> 395 396 <footer class="solid-footer"> 397 <div class="footer-container"> 398 <div class="footer-col col-brand"> 399 <div class="footer-brand" id="footer-brand-container"> 400 <img src="assets/images/logo/DoE-Logo.png" alt="Department Logo" class="footer-logo"> 401 </div> 402 <div class="footer-brand-text"> 403 <h3 id="footer-brand-title">Department of Education</h3> 404 <span id="footer-brand-subtitle">Papua New Guinea</span> 405 </div> 406 <div class="footer-brand-divider"></div> 407 <p id="footer-brand-desc">Ensuring quality education for all children in Papua New Guinea to promote 408 national growth and opportunities.</p> 409 </div> 410 411 <div class="footer-col col-links"> 412 <h3 class="footer-col-header"> 413 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 414 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 415 class="lucide lucide-link footer-header-icon"> 416 <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path> 417 <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path> 418 </svg> 419 Quick Links 420 </h3> 421 <a href="/home"> 422 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 423 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 424 class="lucide lucide-home footer-icon"> 425 <path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> 426 <polyline points="9 22 9 12 15 12 15 22" /> 427 </svg> 428 Home 429 </a> 430 <a href="/about"> 431 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 432 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 433 class="lucide lucide-info footer-icon"> 434 <circle cx="12" cy="12" r="10" /> 435 <path d="M12 16v-4" /> 436 <path d="M12 8h.01" /> 437 </svg> 438 About the Department 439 </a> 440 <a href="/pngetv75"> 441 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 442 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 443 class="lucide lucide-eye footer-icon"> 444 <path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" /> 445 <circle cx="12" cy="12" r="3" /> 446 </svg> 447 Vision 2075 (PNGETV75) 448 </a> 449 <a href="/activities"> 450 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 451 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 452 class="lucide lucide-award footer-icon"> 453 <circle cx="12" cy="8" r="7" /> 454 <polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88" /> 455 </svg> 456 Programs & Activities 457 </a> 458 <a href="/publications"> 459 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 460 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 461 class="lucide lucide-file-text footer-icon"> 462 <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /> 463 <path d="M14 2v4a2 2 0 0 0 2 2h4" /> 464 <path d="M10 9H8" /> 465 <path d="M16 13H8" /> 466 <path d="M16 17H8" /> 467 </svg> 468 Publications & Reports 469 </a> 470 <a href="/news"> 471 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 472 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 473 class="lucide lucide-newspaper footer-icon"> 474 <path 475 d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2" /> 476 <path d="M18 14h-8" /> 477 <path d="M15 18h-5" /> 478 <path d="M10 6h8v4h-8V6Z" /> 479 </svg> 480 Latest News & Events 481 </a> 482 </div> 483 484 <div class="footer-col col-divisions"> 485 <h3 class="footer-col-header"> 486 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 487 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 488 class="lucide lucide-users footer-header-icon"> 489 <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path> 490 <circle cx="9" cy="7" r="4"></circle> 491 <path d="M22 21v-2a4 4 0 0 0-3-3.87"></path> 492 <path d="M16 3.13a4 4 0 0 1 0 7.75"></path> 493 </svg> 494 Divisions & Portals 495 </h3> 496 <a href="https://tsc.gov.pg" target="_blank" rel="noopener"> 497 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 498 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 499 class="lucide lucide-users footer-icon"> 500 <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /> 501 <circle cx="9" cy="7" r="4" /> 502 <path d="M22 21v-2a4 4 0 0 0-3-3.87" /> 503 <path d="M16 3.13a4 4 0 0 1 0 7.75" /> 504 </svg> 505 Teaching Service Commission 506 </a> 507 <a href="https://www.ola.gov.pg/" target="_blank" rel="noopener"> 508 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 509 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 510 class="lucide lucide-book-open footer-icon"> 511 <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" /> 512 <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" /> 513 </svg> 514 National Library & Archives 515 </a> 516 <a href="/publications"> 517 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 518 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 519 class="lucide lucide-clipboard-list footer-icon"> 520 <rect width="8" height="4" x="8" y="2" rx="1" ry="1" /> 521 <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" /> 522 <path d="M12 11h4" /> 523 <path d="M12 16h4" /> 524 <path d="M8 11h.01" /> 525 <path d="M8 16h.01" /> 526 </svg> 527 Measurement Services 528 </a> 529 <a href="/app-gallery"> 530 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 531 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 532 class="lucide lucide-graduation-cap footer-icon"> 533 <path 534 d="M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z" /> 535 <path d="M6 12v5c0 2 2 3 6 3s6-1 6-3v-5" /> 536 </svg> 537 e-Learning Portal 538 </a> 539 <a href="/app-gallery"> 540 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 541 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 542 class="lucide lucide-credit-card footer-icon"> 543 <rect width="20" height="14" x="2" y="5" rx="2" />
544 <line x1="2" x2="22" y1="10" y2="10" /> 545 </svg> 546 Teacher MyPay 547 </a> 548 <a href="/about"> 549 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 550 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 551 class="lucide lucide-eye footer-icon"> 552 <path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" /> 553 <circle cx="12" cy="12" r="3" /> 554 </svg> 555 School Inspection 556 </a> 557 </div> 558 559 <div class="footer-col col-legal"> 560 <h3 class="footer-col-header"> 561 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 562 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 563 class="lucide lucide-shield footer-header-icon"> 564 <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path> 565 </svg> 566 Legal & Connect 567 </h3> 568 <a href="/privacy"> 569 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 570 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 571 class="lucide lucide-lock footer-icon"> 572 <rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> 573 <path d="M7 11V7a5 5 0 0 1 10 0v4" /> 574 </svg> 575 Privacy Policy 576 </a> 577 <a href="/terms"> 578 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 579 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 580 class="lucide lucide-file-text footer-icon"> 581 <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /> 582 <path d="M14 2v4a2 2 0 0 0 2 2h4" /> 583 <path d="M10 9H8" /> 584 <path d="M16 13H8" /> 585 <path d="M16 17H8" /> 586 </svg> 587 Terms of Use 588 </a> 589 <a href="/accessibility"> 590 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 591 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 592 class="lucide lucide-heart footer-icon"> 593 <path 594 d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" /> 595 </svg> 596 Accessibility Statement 597 </a> 598 <a href="/rti"> 599 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 600 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 601 class="lucide lucide-info footer-icon"> 602 <circle cx="12" cy="12" r="10" /> 603 <path d="M12 16v-4" /> 604 <path d="M12 8h.01" /> 605 </svg> 606 Right to Information 607 </a> 608 <a href="/faq"> 609 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 610 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 611 class="lucide lucide-help-circle footer-icon"> 612 <circle cx="12" cy="12" r="10" /> 613 <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /> 614 <path d="M12 17h.01" /> 615 </svg> 616 Help & FAQ 617 </a> 618 <a href="/contact"> 619 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" 620 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" 621 class="lucide lucide-mail footer-icon"> 622 <rect width="20" height="16" x="2" y="4" rx="2" /> 623 <path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" /> 624 </svg> 625 Contact Us 626 </a> 627 </div> 628 </div> 629 <div class="footer-bottom"> 630 <p id="footer-copyright">© 2026 Department of Education, Papua New Guinea. All rights reserved.</p> 631 </div> 632 </footer> 633 634 <!-- Floating Social Widget --> 635 <div class="floating-social-widget" id="floating-social-widget"> 636 <button class="social-main-btn" id="social-main-btn" aria-label="Toggle Social Media">
637 <i data-lucide="share-2" style="width: 20px; height: 20px;"></i> 638 </button> 639 <div class="social-links-container"> 640 <a href="https://www.facebook.com/png.doe" target="_blank" rel="noopener noreferrer" class="social-icon-btn facebook" aria-label="Facebook"> 641 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg> 642 </a> 643 <a href="#" class="social-icon-btn whatsapp" aria-label="WhatsApp"> 644 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/></svg> 645 </a> 646 <a href="https://www.linkedin.com/company/department-of-education-papua-new-guinea" target="_blank" rel="noopener noreferrer" class="social-icon-btn linkedin" aria-label="LinkedIn"> 647 <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect width="4" height="12" x="2" y="9"/><circle cx="4" cy="4" r="2"/></svg> 648 </a> 649 </div> 650 </div> 651 652
652<script> 653 document.getElementById('social-main-btn').addEventListener('click', function() { 654 document.getElementById('floating-social-widget').classList.toggle('expanded'); 655 }); 656 </script>
656 657 658 <!-- Back to Top Button --> 659 <button class="back-to-top" id="back-to-top" aria-label="Back to Top"> 660 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" 661 stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up"> 662 <path d="m18 15-6-6-6 6" /> 663 </svg> 664 </button> 665 666 <!-- Utilities and App Logic --> 667
667<script src="components/api.js?v=2026.20"></script>
667 668
668<script src="components/home.js?v=2026.20" defer></script>
668 669
669<script src="components/about.js?v=2026.20" defer></script>
669 670
670<script src="components/activities.js?v=2026.20" defer></script>
670 671
671<script src="components/publications.js?v=2026.20" defer></script>
671 672
672<script src="components/news.js?v=2026.20" defer></script>
672 673
673<script src="components/careers.js?v=2026.20" defer></script>
673 674
674<script src="components/contact.js?v=2026.20" defer></script>
674 675
675<script src="components/forms.js?v=2026.20" defer></script>
675 676
676<script src="components/form_detail.js?v=2026.20" defer></script>
676 677
677<script src="components/app_gallery.js?v=2026.20" defer></script>
677 678
678<script src="components/news_detail.js?v=2026.20" defer></script>
678 679
679<script src="components/search.js?v=2026.20" defer></script>
679 680
680<script src="components/elementary.js?v=2026.20" defer></script>
680 681
681<script src="components/primary.js?v=2026.20" defer></script>
681 682
682<script src="components/secondary.js?v=2026.20" defer></script>
682 683
683<script src="components/textbooks.js?v=2026.20" defer></script>
683 684
684<script src="components/gfep.js?v=2026.20" defer></script>
684 685
685<script src="components/teachers.js?v=2026.22" defer></script>
685 686
686<script src="components/pngetv75.js?v=2026.20" defer></script>
686 687
687<script alt="legal policy component" src="components/legal.js?v=2026.20" defer></script>
687 688
688<script src="components/gallery.js?v=2026.20" defer></script>
688 689
689<script src="components/press-release.js?v=2026.20" defer></script>
689 690
690<script src="components/scholarships.js?v=2026.20" defer></script>
690 691
691<script src="components/leaders.js?v=2026.20" defer></script>
691 692
692<script src="components/faq.js?v=2026.20" defer></script>
692 693
693<script src="components/calendars.js?v=2026.20" defer></script>
693 694
694<script src="components/notices.js?v=2026.20" defer></script>
694 695
695<script src="components/leap.js?v=2026.20" defer></script>
695 696
696<script src="components/gesi.js?v=2026.20" defer></script>
696 697
697<script src="components/provincial_sites.js?v=2026.20" defer></script>
697 698
698<script src="components/open_data.js?v=2026.20" defer></script>
698 699
699<script src="router.js?v=2026.20" defer></script>
699 700 701
701<script> 702 // Init Lucide 703 if (window.lucide) lucide.createIcons(); 704 705 // Hamburger Menu & Mobile Accordion Logic 706 document.addEventListener('DOMContentLoaded', () => { 707 // Init Lucide safely 708 if (window.lucide) window.lucide.createIcons(); 709 const hamburger = document.getElementById('hamburger-toggle'); 710 const closeBtn = document.getElementById('mobile-nav-close-btn'); 711 const nav = document.querySelector('.main-nav'); 712 713 function openMenu() { 714 nav.classList.add('active'); 715 hamburger.classList.add('active'); 716 document.body.style.overflow = 'hidden'; 717 document.body.classList.add('menu-open'); 718 } 719 720 function closeMenu() { 721 nav.classList.remove('active'); 722 hamburger.classList.remove('active'); 723 document.body.style.overflow = ''; 724 document.body.classList.remove('menu-open'); 725 document.querySelectorAll('.dropdown').forEach(d => d.classList.remove('mobile-expanded')); 726 document.querySelectorAll('.nested-dropdown').forEach(nd => nd.classList.remove('active')); 727 } 728 729 // Hamburger opens the menu 730 if (hamburger) hamburger.addEventListener('click', (e) => { e.stopPropagation(); openMenu(); }); 731 // X button closes the menu 732 if (closeBtn) closeBtn.addEventListener('click', (e) => { e.stopPropagation(); closeMenu(); }); 733 734 // Handle link clicks natively and smartly 735 nav.querySelectorAll('a').forEach(link => { 736 link.addEventListener('click', (e) => { 737 const dropdownParent = link.parentElement; 738 const isMobileAccordion = window.innerWidth <= 1199 && dropdownParent.classList.contains('dropdown') && link.classList.contains('nav-link'); 739 740 if (isMobileAccordion) { 741 e.preventDefault(); 742 const isAlreadyOpen = dropdownParent.classList.contains('mobile-expanded'); 743 744 // Close all primary dropdowns and nested sub-menus before opening the new one 745 document.querySelectorAll('.dropdown').forEach(d => d.classList.remove('mobile-expanded')); 746 document.querySelectorAll('.nested-dropdown').forEach(nd => nd.classList.remove('active')); 747 748 if (!isAlreadyOpen) { 749 dropdownParent.classList.add('mobile-expanded'); 750 } 751 } else { 752 // Regular link click - close menu 753 closeMenu(); 754 } 755 }); 756 }); 757 758 // Global Modal Handlers 759 window.openModal = function (id) { 760 document.getElementById(id).classList.add('active'); 761 document.body.style.overflow = 'hidden'; // Stop background scroll 762 763 // Temporarily boost the main wrapper's stacking context strictly over the nav bar 764 const appContainer = document.getElementById('app-content'); 765 appContainer.style.position = 'relative'; 766 appContainer.style.zIndex = '9999'; 767 }; 768 769 window.closeModal = function (e, id) { 770 // If the event triggered from the background click OR specifically on the close button 771 if (!e || e.target === e.currentTarget) { 772 document.getElementById(id).classList.remove('active'); 773 document.body.style.overflow = 'auto'; // Restore scroll 774 775 // Release the main wrapper constraint so the transparent nav works again 776 document.getElementById('app-content').style.zIndex = 'auto'; 777 } 778 }; 779 780 // Toggle nested sub-menus (Accordion style within dropdowns) 781 window.toggleNested = function (e) { 782 e.preventDefault(); 783 e.stopPropagation(); 784 const parent = e.currentTarget.closest('.nested-dropdown'); 785 if (parent) { 786 // Close other nested sub-menus in the same container 787 const siblingNested = parent.parentElement.querySelectorAll('.nested-dropdown'); 788 siblingNested.forEach(el => { 789 if (el !== parent) el.classList.remove('active'); 790 }); 791 792 parent.classList.toggle('active'); 793 } 794 }; 795 796 // Global Click Away to Close Submenus and Dropdowns 797 document.addEventListener('click', (e) => { 798 const isNav = e.target.closest('.main-nav'); 799 const isHamburger = e.target.closest('#hamburger-toggle'); 800 801 // Close if clicked outside nav and outside hamburger button 802 if (!isNav && !isHamburger) { 803 closeMenu(); 804 } 805 }); 806 807 // Handle Escape key to close everything 808 document.addEventListener('keydown', (e) => { 809 if (e.key === 'Escape') { 810 closeMenu(); 811 document.querySelectorAll('.modal, .modal-overlay').forEach(m => m.classList.remove('active')); 812 document.body.style.overflow = ''; 813 } 814 }); 815 // âââ Table Scroll State Manager ââââââââââââââââââââââââââ 816 // Watches for tables injected by the SPA router and applies 817 // at-start / at-end classes for the CSS fade-edge mask. 818 function initTableScrollers(root) { 819 root = root || document; 820 root.querySelectorAll('.table-inner').forEach(function (inner) { 821 if (inner.dataset.scrollInit) return; // already wired 822 inner.dataset.scrollInit = '1'; 823
824 function update() { 825 const atStart = inner.scrollLeft <= 4; 826 const atEnd = inner.scrollLeft + inner.clientWidth >= inner.scrollWidth - 4; 827 inner.classList.toggle('at-start', atStart); 828 inner.classList.toggle('at-end', atEnd); 829 830 // Show/hide the hint pill 831 const hint = inner.parentElement.querySelector('.table-scroll-hint'); 832 if (hint) hint.style.display = (inner.scrollWidth > inner.clientWidth) ? 'flex' : 'none'; 833 } 834 835 // Inject mobile scroll hint if not already there 836 const wrap = inner.closest('.table-scroll-wrap'); 837 if (wrap && !wrap.querySelector('.table-scroll-hint')) { 838 const hint = document.createElement('div'); 839 hint.className = 'table-scroll-hint'; 840 hint.innerHTML = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg> Scroll <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>'; 841 wrap.appendChild(hint); 842 } 843 844 inner.addEventListener('scroll', update, { passive: true }); 845 window.addEventListener('resize', update, { passive: true }); 846 update(); // run immediately 847 }); 848 } 849 850 // Run on first load 851 initTableScrollers(); 852 853 // Re-run whenever the SPA injects new content 854 const _appContent = document.getElementById('app-content'); 855 if (_appContent) { 856 new MutationObserver(function () { 857 initTableScrollers(_appContent); 858 }).observe(_appContent, { childList: true, subtree: true }); 859 } 860 861 // âââ Back to Top Logic ââââââââââââââââââââââââââ 862 const backToTopBtn = document.getElementById('back-to-top'); 863 window.addEventListener('scroll', () => { 864 if (window.scrollY > 600) { 865 backToTopBtn.classList.add('active'); 866 } else { 867 backToTopBtn.classList.remove('active'); 868 } 869 }, { passive: true }); 870 871 backToTopBtn.addEventListener('click', () => { 872 window.scrollTo({ top: 0, behavior: 'smooth' }); 873 }); 874 875 // âââ Global Branding Injection ââââââââââââââââââ 876 async function applyGlobalBranding() { 877 const settings = await window.DoEAPI.loadSettings(); 878 if (!settings) return; 879 880 const headerLogoUrl = window.DoEAPI.imgUrl(settings.site_logo); 881 const footerLogoUrl = window.DoEAPI.imgUrl(settings.footer_logo || settings.site_logo); 882 883 // Top Banner Logos 884 const leftLogoUrl = settings.brand_logo_left ? window.DoEAPI.imgUrl(settings.brand_logo_left) : 'assets/images/logo/logo-2.png'; 885 const rightLogoUrl = settings.brand_logo_right ? window.DoEAPI.imgUrl(settings.brand_logo_right) : 'assets/images/logo/DoE-Logo.png'; 886 887 // Update Logos 888 const hLogo = document.getElementById('header-logo'); 889 const fLogo = document.getElementById('footer-logo-img'); 890 const lLogo = document.getElementById('top-brand-logo-left'); 891 const rLogo = document.getElementById('top-brand-logo-right'); 892 893 if (hLogo && headerLogoUrl) hLogo.src = headerLogoUrl; 894 895 // Footer Logo / Video handling 896 const footerLogoKey = settings.footer_logo || settings.site_logo; 897 const container = document.getElementById('footer-brand-container'); 898 if (container && footerLogoKey) { 899 const isVideo = footerLogoKey.match(/\.(mp4|webm)$/i); 900 if (isVideo) { 901 container.innerHTML = `<video src="${footerLogoUrl}" class="footer-logo" autoplay loop muted playsinline preload="auto" style="max-width:100%; border-radius:12px; opacity:1;"></video>`; 902 setTimeout(() => { 903 const v = container.querySelector('video'); 904 if (v) v.play().catch(e => console.warn('Autoplay failed:', e)); 905 }, 50); 906 } else { 907 container.innerHTML = `<img src="${footerLogoUrl}" alt="Department Logo" class="footer-logo" style="opacity:1;">`; 908 } 909 } else if (fLogo && footerLogoUrl) { 910 fLogo.src = footerLogoUrl; fLogo.style.opacity = '1'; 911 } 912 if (lLogo && leftLogoUrl) lLogo.src = leftLogoUrl; 913 if (rLogo && rightLogoUrl) rLogo.src = rightLogoUrl; 914 915 // Update Footer Branding (Titles & Text) 916 if (settings.site_description) document.getElementById('footer-brand-desc').innerText = settings.site_description; 917 if (settings.site_copyright) document.getElementById('footer-copyright').innerHTML = settings.site_copyright; 918 919 // Update Footer Brand Name (handles the <br> line break if present) 920 if (settings.site_title) { 921 const brandTitle = document.getElementById('footer-brand-title'); 922 if (brandTitle) brandTitle.innerText = settings.site_title; 923 } 924 925 // Chatbot Toggle Logic 926 const chatbotEnabled = settings.chatbot_enabled; 927 if (chatbotEnabled === '0' || chatbotEnabled === false || chatbotEnabled === 0) { 928 const chatBubble = document.getElementById('doe-chat-bubble'); 929 const chatWindow = document.getElementById('doe-chat-window'); 930 if (chatBubble) chatBubble.remove(); 931 if (chatWindow) chatWindow.remove(); 932 } 933 } 934 935 // âââ Loading Overlay Logic ââââââââââââââââââââââ 936 let isOverlayHidden = false; 937 let spaLoaded = false; 938 let globalBrandingLoaded = false; 939 940 function hideLoadingOverlay() {
941 if (isOverlayHidden) return; 942 isOverlayHidden = true; 943 const overlay = document.getElementById('loading-overlay'); 944 if (overlay) { 945 overlay.classList.add('fade-out'); 946 setTimeout(() => { 947 overlay.style.display = 'none'; 948 }, 500); // 500ms matches the CSS fade transition 949 } 950 } 951 952 function checkAndHideIfReady() { 953 if (!spaLoaded || !globalBrandingLoaded) return; 954 955 // Query all images to make sure they are completely loaded 956 const images = Array.from(document.querySelectorAll('img')); 957 const pendingImages = images.filter(img => { 958 // Ignore placeholder images or images that are already complete 959 if (!img.src || img.src.startsWith('data:') || img.complete) { 960 return false; 961 } 962 return true; 963 }); 964 965 if (pendingImages.length === 0) { 966 hideLoadingOverlay(); 967 } else { 968 let loadedCount = 0; 969 const onImageDone = () => { 970 loadedCount++; 971 if (loadedCount >= pendingImages.length) { 972 hideLoadingOverlay(); 973 } 974 }; 975 pendingImages.forEach(img => { 976 img.addEventListener('load', onImageDone, { once: true }); 977 img.addEventListener('error', onImageDone, { once: true }); 978 }); 979 } 980 } 981 982 // 1. Trigger when branding API responds 983 applyGlobalBranding().finally(() => { 984 globalBrandingLoaded = true; 985 checkAndHideIfReady(); 986 }); 987 988 // 2. Trigger when SPA Router finishes loading the initial route/component 989 window.addEventListener('spa-route-complete', () => { 990 spaLoaded = true; 991 checkAndHideIfReady(); 992 }, { once: true }); 993 994 // 3. Fallback timeout in case of slow connections or stuck assets 995 setTimeout(hideLoadingOverlay, 5000); 996 }); 997 </script>
997 998 <!-- DoE AI Chatbot (EduBot) --> 999
999<script src="components/chatbot.js?v=2026.20" defer></script>
999 1000</body> 1001 1002</html>
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.