PageSourceSearch

https://ai-teacher.de/js/theme.js

js ai-teacher.de collected 2026-10-02 01:01:03 UTC 2,771 bytes, 93 lines download raw bytes

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.