1/** 2 * Dark/light theme: keeps document in sync when preference is "system" 3 * and exposes AiTeacherTheme.setPreference for instant UI (e.g. form toggles). 4 */ 5(function () { 6 'use strict'; 7 8 var STORAGE_KEY = 'aiTeacherUiTheme'; 9 10 function validChoice(v) { 11 return v === 'light' || v === 'dark' || v === 'system'; 12 } 13 14 function effectiveFromChoice(choice) { 15 if (choice === 'dark') return 'dark'; 16 if (choice === 'light') return 'light'; 17 try { 18 return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; 19 } catch (e) { 20 return 'light'; 21 } 22 } 23 24 function applyToDom(choice) { 25 if (!validChoice(choice)) choice = 'system'; 26 var eff = effectiveFromChoice(choice); 27 document.documentElement.setAttribute('data-bs-theme', eff); 28 document.documentElement.setAttribute('data-ui-theme', choice); 29 document.documentElement.style.colorScheme = eff === 'dark' ? 'dark' : 'light'; 30 } 31 32 function persistIfGuest(choice) { 33 try { 34 if (document.documentElement.getAttribute('data-user-logged-in') !== '1') { 35 localStorage.setItem(STORAGE_KEY, choice); 36 } 37 } catch (e) {} 38 } 39 40 function ajaxUrlForAppRoot(filename) { 41 var root = typeof window.AI_TEACHER_ROOT_PATH === 'string' ? window.AI_TEACHER_ROOT_PATH.replace(/^\/+|\/+$/g, '') : ''; 42 filename = String(filename || '').replace(/^\//, ''); 43 if (!filename) return '/'; 44 return root ? '/' + root + '/' + filename : '/' + filename; 45 } 46 47 function persistForLoggedInUser(choice) { 48 if (document.documentElement.getAttribute('data-user-logged-in') !== '1') { 49 return; 50 } 51 var formData = new FormData(); 52 formData.append('ui_theme', choice); 53 fetch(ajaxUrlForAppRoot('save-ui-theme-ajax.php'), { 54 method: 'POST', 55 body: formData, 56 credentials: 'same-origin', 57 headers: { Accept: 'application/json' } 58 }) 59 .then(function (r) { 60 if (!r.ok) { 61 return null; 62 } 63 return r.json().catch(function () { 64 return null; 65 }); 66 }) 67 .then(function (data) { 68 if (data && data.ok) { 69 try { 70 localStorage.setItem(STORAGE_KEY, choice); 71 } catch (e) {} 72 } 73 }) 74 .catch(function () {}); 75 } 76 77 window.AiTeacherTheme = { 78 setPreference: function (choice) { 79 if (!validChoice(choice)) choice = 'system'; 80 applyToDom(choice); 81 persistIfGuest(choice); 82 persistForLoggedInUser(choice); 83 } 84 }; 85 86 try { 87 var mq = window.matchMedia('(prefers-color-scheme: dark)'); 88 mq.addEventListener('change', function () { 89 var cur = document.documentElement.getAttribute('data-ui-theme'); 90 if (cur === 'system') applyToDom('system'); 91 }); 92 } catch (e) {} 93})();
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.