diff options
| -rw-r--r-- | layout/_partial/head.ejs | 4 | ||||
| -rw-r--r-- | layout/_partial/header.ejs | 3 | ||||
| -rw-r--r-- | layout/_partial/scripts.ejs | 3 | ||||
| -rw-r--r-- | layout/post.ejs | 4 | ||||
| -rw-r--r-- | source/css/style.css | 44 | ||||
| -rw-r--r-- | source/css/style.min.css | 2 | ||||
| -rw-r--r-- | source/js/script.js | 155 | ||||
| -rw-r--r-- | source/js/script.min.js | 2 | ||||
| -rw-r--r-- | source/js/script.ts | 168 | 
9 files changed, 328 insertions, 57 deletions
| diff --git a/layout/_partial/head.ejs b/layout/_partial/head.ejs index 135d822..ed4f8d7 100644 --- a/layout/_partial/head.ejs +++ b/layout/_partial/head.ejs @@ -33,6 +33,6 @@      <!-- Scripts and styles -->      <%- css('css/style.min.css') %> -    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma/css/bulma.min.css"> -    <link media="(prefers-color-scheme: dark)" rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jloh/bulma-prefers-dark/css/bulma-prefers-dark.min.css"> +    <link rel="stylesheet" id="bulma-light" href="https://cdn.jsdelivr.net/npm/bulma/css/bulma.min.css"> +    <link rel="stylesheet" id="bulma-dark" href="https://cdn.jsdelivr.net/npm/bulmaswatch@0.8.1/darkly/bulmaswatch.min.css">  </head>
\ No newline at end of file diff --git a/layout/_partial/header.ejs b/layout/_partial/header.ejs index b32edbb..6c423a0 100644 --- a/layout/_partial/header.ejs +++ b/layout/_partial/header.ejs @@ -18,6 +18,9 @@                  </div>                  <div class="navbar-menu" id="header-menu">                      <div class="navbar-end"> +                        <a class="navbar-item" href="javaScript:void(0);" id="btn-toggle-dark"> +                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg> +                        </a>                          <% for (let item of theme.menubar) { %>                              <a class="navbar-item is-uppercase" href="<%= item[0] %>">                                  <%= item[1] %> diff --git a/layout/_partial/scripts.ejs b/layout/_partial/scripts.ejs index 2efb659..98d39e0 100644 --- a/layout/_partial/scripts.ejs +++ b/layout/_partial/scripts.ejs @@ -24,13 +24,14 @@      <script>          window.addEventListener("load", () => {              const theme_selector = (mode) => { -                if (mode === SystemDarkmodePrefrence.dark) { +                if (mode === SystemDarkmodePreference.dark) {                      document.getElementById("highlight-light-theme").setAttribute("disabled", "disabled");                      document.getElementById("highlight-dark-theme").removeAttribute("disabled");                  } else {                      document.getElementById("highlight-dark-theme").setAttribute("disabled", "disabled");                      document.getElementById("highlight-light-theme").removeAttribute("disabled");                  } +                console.debug(`highlightjs theme set to ${mode}`);              };              darklistener.add(theme_selector);          }, false); diff --git a/layout/post.ejs b/layout/post.ejs index 9befb97..3428ddf 100644 --- a/layout/post.ejs +++ b/layout/post.ejs @@ -63,7 +63,7 @@                              const player = new Shikwasa({                                  container: () => document.querySelector('.post-podcast-player'), -                                audio: {         +                                audio: {                                      title: "<%= page.title %>",                                      artist: "<%= page.podcast.authors %>",                                      cover: "<%= page.podcast.cover %>", @@ -82,7 +82,7 @@                              window.player = player;                          }, false);                      </script> -                <% } %> +                    <% } %>                  <section class="content is-size-6">                      <%- page.content %>                  </section> diff --git a/source/css/style.css b/source/css/style.css index 3a9430f..5a62406 100644 --- a/source/css/style.css +++ b/source/css/style.css @@ -8,19 +8,45 @@ pre {      -webkit-filter: grayscale(1) !important;  } +:root { +    --transition-speed: unset; +    --background-color: white; +    --brightness: 1; +    --contrast: 1; +} +  @media(prefers-color-scheme:dark) { -    * { -        transition: backgourd-color .5s -    } -    .card { -        box-shadow: none!important; -        background-color: #121212!important -    } -    img:not([src*=".svg"]) { -        filter: brightness(.7) contrast(1.2); +     +    :root:not([data-user-color-scheme]) { +        --box-shadow: none!important; +        --transition-speed: 0.5s; +        --background-color: #121212!important; +        --brightness: 0.7; +        --contrast: 1.2;      }  } +[data-user-color-scheme='dark'] { +    --box-shadow: none!important; +    --transition-speed: 0.5s; +    --background-color: #121212!important; +    --brightness: 0.7; +    --contrast: 1.2; +} + +* { +    transition: backgourd-color var(--transition-speed); +} + +.card { +    box-shadow: var(--box-shadow); +    background-color: var(--background-color); +} + +img:not([src*=".svg"]) { +    filter: brightness(--brightness) contrast(--contrast); +} +  @media screen and (max-width:1023px) {      .navbar-menu {          box-shadow: 0 8px 16px -8px rgb(10 10 10 / 10%)!important diff --git a/source/css/style.min.css b/source/css/style.min.css index d8c3a0b..ac94b6b 100644 --- a/source/css/style.min.css +++ b/source/css/style.min.css @@ -1 +1 @@ -pre{background-color:none!important;padding:0!important}#mourn{filter:grayscale(1)!important;-webkit-filter:grayscale(1)!important}@media(prefers-color-scheme:dark){*{transition:backgourd-color .5s}.card{box-shadow:none!important;background-color:#121212!important}img:not([src*=".svg"]){filter:brightness(.7) contrast(1.2)}}@media screen and (max-width:1023px){.navbar-menu{box-shadow:0 8px 16px -8px rgb(10 10 10 / 10%)!important}.post-content{padding-top:0!important}.post-card{margin:0!important}}.post-podcast-player{padding-bottom:1.5rem}.post-card{margin:0 1rem 0 1rem}.navbar-brand .navbar-item:hover{background-color:inherit!important}.entry{margin-bottom:1.5rem!important;margin-top:1.5rem!important;transition:box-shadow .1s}.entry:hover{box-shadow:0 1em 2em -.125em rgb(10 10 10 / 10%),0 0 0 1px rgb(10 10 10 / 2%)}.pagination-bar{padding-left:.25rem;padding-right:.25rem}.footer{padding:1rem 1rem 1rem!important;background-color:transparent!important}.subnav-top{border-bottom:1px solid;padding-bottom:1rem;display:inline}.subnav-bottom{margin-top:1.6rem!important}
\ No newline at end of file +pre{background-color:none!important;padding:0!important}#mourn{filter:grayscale(1)!important;-webkit-filter:grayscale(1)!important}:root{--transition-speed:unset;--background-color:white;--brightness:1;--contrast:1}@media(prefers-color-scheme:dark){:root:not([data-user-color-scheme]){--box-shadow:none!important;--transition-speed:0.5s;--background-color:#121212!important;--brightness:0.7;--contrast:1.2}}[data-user-color-scheme=dark]{--box-shadow:none!important;--transition-speed:0.5s;--background-color:#121212!important;--brightness:0.7;--contrast:1.2}*{transition:backgourd-color var(--transition-speed)}.card{box-shadow:var(--box-shadow);background-color:var(--background-color)}img:not([src*=".svg"]){filter:brightness(--brightness) contrast(--contrast)}@media screen and (max-width:1023px){.navbar-menu{box-shadow:0 8px 16px -8px rgb(10 10 10 / 10%)!important}.post-content{padding-top:0!important}.post-card{margin:0!important}}.post-podcast-player{padding-bottom:1.5rem}.post-card{margin:0 1rem 0 1rem}.navbar-brand .navbar-item:hover{background-color:inherit!important}.entry{margin-bottom:1.5rem!important;margin-top:1.5rem!important;transition:box-shadow .1s}.entry:hover{box-shadow:0 1em 2em -.125em rgb(10 10 10 / 10%),0 0 0 1px rgb(10 10 10 / 2%)}.pagination-bar{padding-left:.25rem;padding-right:.25rem}.footer{padding:1rem 1rem 1rem!important;background-color:transparent!important}.subnav-top{border-bottom:1px solid;padding-bottom:1rem;display:inline}.subnav-bottom{margin-top:1.5rem!important}
\ No newline at end of file diff --git a/source/js/script.js b/source/js/script.js index 8a3daa5..665d379 100644 --- a/source/js/script.js +++ b/source/js/script.js @@ -1,4 +1,3 @@ -"use strict";  /* Add elements listener */  window.addEventListener("load", function () {      // Menubar burgers click listenr @@ -20,15 +19,17 @@ window.addEventListener("load", function () {          var clsname = ".auto-dark";          var elements = Array.prototype.slice.call(document.querySelectorAll(clsname), 0);          elements.forEach(function (element) { -            if (mode === SystemDarkmodePrefrence.dark) { +            if (mode === SystemDarkmodePreference.dark) {                  element === null || element === void 0 ? void 0 : element.classList.add("is-dark");                  element === null || element === void 0 ? void 0 : element.classList.remove("is-light");              }              else { +                // If mode == null , fallback to light mode                  element === null || element === void 0 ? void 0 : element.classList.add("is-light");                  element === null || element === void 0 ? void 0 : element.classList.remove("is-dark");              }          }); +        console.debug("".concat(clsname, " class changed to ").concat(enumModeToStringMode(mode)));      };      darklistener.add(colorman);      // Add logo color selector @@ -38,36 +39,128 @@ window.addEventListener("load", function () {          var lightsrc = logo === null || logo === void 0 ? void 0 : logo.getAttribute("data-src-lightmode");          if (!logo || !darksrc || !lightsrc)              return; -        var src = (mode === SystemDarkmodePrefrence.dark) ? darksrc : lightsrc; +        var src = (mode === SystemDarkmodePreference.dark) ? darksrc : lightsrc;          logo.setAttribute("src", src); +        console.debug("logo src changed to ".concat(enumModeToStringMode(mode), "src"));      };      darklistener.add(logoman); +    // Add player theme handler +    var playerman = function (mode) { +        var shikwasa = document.body.querySelector('div[data-name="shikwasa"]'); +        shikwasa === null || shikwasa === void 0 ? void 0 : shikwasa.setAttribute("data-theme", enumModeToStringMode(mode) ? enumModeToStringMode(mode) : "light"); +        console.debug("shikwasa theme: ".concat(shikwasa === null || shikwasa === void 0 ? void 0 : shikwasa.getAttribute("data-theme"))); +    }; +    darklistener.add(playerman); +    // Listen after all the setups (to get the handler work :p). +    darklistener.listen();  });  /* Darkmode listener */ -var SystemDarkmodePrefrence; -(function (SystemDarkmodePrefrence) { -    SystemDarkmodePrefrence[SystemDarkmodePrefrence["dark"] = 0] = "dark"; -    SystemDarkmodePrefrence[SystemDarkmodePrefrence["light"] = 1] = "light"; -})(SystemDarkmodePrefrence || (SystemDarkmodePrefrence = {})); +var SystemDarkmodePreference; +(function (SystemDarkmodePreference) { +    SystemDarkmodePreference[SystemDarkmodePreference["dark"] = 0] = "dark"; +    SystemDarkmodePreference[SystemDarkmodePreference["light"] = 1] = "light"; +})(SystemDarkmodePreference || (SystemDarkmodePreference = {}));  ; +var modeMap = { +    dark: SystemDarkmodePreference.dark, +    light: SystemDarkmodePreference.light +}; +var invertDarkModeObj = { +    'dark': 'light', +    'light': 'dark' +}; +var enumModeToStringMode = function (i) { +    var keysArray = Object.keys(modeMap); +    var result = keysArray.filter(function (key) { return isValidKey(key, modeMap) && modeMap[key] === i; }); +    return result ? result[0] : undefined; +};  var DarkmodeListener = /** @class */ (function () {      function DarkmodeListener() {          var darking = window.matchMedia('(prefers-color-scheme: dark)').matches; -        this._mode = darking ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; +        this._mode = darking ? SystemDarkmodePreference.dark : SystemDarkmodePreference.light;          this._handlers = []; -        this._listen();      } -    DarkmodeListener.prototype._listen = function () { +    DarkmodeListener.prototype.listen = function () {          var _this = this; -        var media = window.matchMedia('(prefers-color-scheme: dark)'); -        var callback = function (event) { -            var mode = event.matches ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; -            _this._handlers.forEach(function (handler) { -                handler(mode); -            }); -            _this._mode = mode; +        var rootElement = document.documentElement; +        var darkModeStorageKey = 'user-color-scheme'; +        var rootElementDarkModeAttributeName = 'data-user-color-scheme'; +        var darkModeTogglebuttonElement = document.getElementById('btn-toggle-dark'); +        var resetRootDarkModeAttributeAndLS = function () { +            rootElement.removeAttribute(rootElementDarkModeAttributeName); +            removeLS(darkModeStorageKey); +        }; +        // Partially taken from https://blog.skk.moe/post/hello-darkmode-my-old-friend, CC BY-NC-SA 4.0 +        var applyCustomDarkModeSettings = function (mode) { +            // 接受从「开关」处传来的模式,或者从 localStorage 读取 +            var LSSetting = getLS(darkModeStorageKey) == 'dark' ? SystemDarkmodePreference.dark : 'light' ? SystemDarkmodePreference.light : null; +            var currentSetting = mode || LSSetting; +            console.debug("applyCustomDarkModeSettings: ".concat(currentSetting)); +            if (currentSetting === _this._mode) { +                // 当用户自定义的显示模式和 prefers-color-scheme 相同时重置、恢复到自动模式 +                resetRootDarkModeAttributeAndLS(); +                console.debug('Resetting to auto mode...'); +            } +            else if (currentSetting == SystemDarkmodePreference.dark || currentSetting == SystemDarkmodePreference.light) { +                // 否则设置为用户自定义的显示模式 +                rootElement.setAttribute(rootElementDarkModeAttributeName, enumModeToStringMode(currentSetting)); +                console.debug('Setting prop: "data-user-color-scheme" in HTML...'); +            } +            else { +                // 首次访问或从未使用过开关、localStorage 中没有存储的值,currentSetting 是 null +                // 或者 localStorage 被篡改,currentSetting 不是合法值 +                // 默认显示浅色主题 +                resetRootDarkModeAttributeAndLS(); +                console.debug('Initial setup, setting theme to light as default...'); +                currentSetting = SystemDarkmodePreference.light; +            } +            var lightCSS = document.getElementById("bulma-light"); +            var darkCSS = document.getElementById("bulma-dark"); +            if (currentSetting == SystemDarkmodePreference.dark) { +                rootElement.setAttribute(rootElementDarkModeAttributeName, 'dark'); +                lightCSS.setAttribute("media", "none"); +                darkCSS.setAttribute("media", "all"); +                _this._handlers.forEach(function (handler) { +                    handler(SystemDarkmodePreference.dark); +                    console.debug('Invoking dark theme handler...'); +                }); +                console.debug('Dark theme applied.'); +            } +            else if (currentSetting == SystemDarkmodePreference.light) { +                rootElement.setAttribute(rootElementDarkModeAttributeName, 'light'); +                lightCSS.setAttribute("media", "all"); +                darkCSS.setAttribute("media", "none"); +                _this._handlers.forEach(function (handler) { +                    handler(SystemDarkmodePreference.light); +                    console.debug('Invoking light theme handler...'); +                }); +                console.debug('Light theme applied.'); +            } +        }; +        var toggleCustomDarkMode = function () { +            var currentSetting = getLS(darkModeStorageKey); +            if (currentSetting === null) { +                var curMode = enumModeToStringMode(_this._mode); +                currentSetting = isValidKey(curMode, invertDarkModeObj) ? invertDarkModeObj[curMode] : currentSetting; +            } +            else if (currentSetting == 'dark' || 'light') { +                // 从 localStorage 中读取模式,并取相反的模式 +                currentSetting = isValidKey(currentSetting, invertDarkModeObj) ? invertDarkModeObj[currentSetting] : currentSetting; +            } +            else { +                // 不知道出了什么幺蛾子,比如 localStorage 被篡改成非法值 +                return; // 直接 return; +            } +            // 将相反的模式写入 localStorage +            setLS(darkModeStorageKey, currentSetting); +            return isValidKey(currentSetting, invertDarkModeObj) ? modeMap[currentSetting] : null;          }; -        media.addEventListener("change", callback); +        applyCustomDarkModeSettings(null); +        darkModeTogglebuttonElement === null || darkModeTogglebuttonElement === void 0 ? void 0 : darkModeTogglebuttonElement.addEventListener('click', function () { +            // 当用户点击「开关」时,获得新的显示模式、写入 localStorage、并在页面上生效 +            console.debug('User clicked button. Doing black magic now...'); +            applyCustomDarkModeSettings(toggleCustomDarkMode()); +        });      };      DarkmodeListener.prototype.add = function (callback) {          callback(this._mode); @@ -80,3 +173,27 @@ var DarkmodeListener = /** @class */ (function () {  }());  ;  var darklistener = new DarkmodeListener(); +function setLS(k, v) { +    try { +        localStorage.setItem(k, v); +    } +    catch (e) { } +} +function removeLS(k) { +    try { +        localStorage.removeItem(k); +    } +    catch (e) { } +} +function getLS(k) { +    try { +        return localStorage.getItem(k); +    } +    catch (e) { +        return null; // 与 localStorage 中没有找到对应 key 的行为一致 +    } +} +// 使用isValidKey判断key是否存在对象类型中 +function isValidKey(key, obj) { +    return key in obj; +} diff --git a/source/js/script.min.js b/source/js/script.min.js index 487569f..823246f 100644 --- a/source/js/script.min.js +++ b/source/js/script.min.js @@ -1 +1 @@ -"use strict"; window.addEventListener("load", function () { var a = Array.prototype.slice.call(document.querySelectorAll(".navbar-burger"), 0); if (a.length > 0) { a.forEach(function (d) { d.addEventListener("click", function () { var f = d.getAttribute("data-target"); d.classList.toggle("is-active"); if (!f) { return } var e = document.getElementById(f); e === null || e === void 0 ? void 0 : e.classList.toggle("is-active") }, false) }) } var c = function (f) { var e = ".auto-dark"; var d = Array.prototype.slice.call(document.querySelectorAll(e), 0); d.forEach(function (g) { if (f === SystemDarkmodePrefrence.dark) { g === null || g === void 0 ? void 0 : g.classList.add("is-dark"); g === null || g === void 0 ? void 0 : g.classList.remove("is-light") } else { g === null || g === void 0 ? void 0 : g.classList.add("is-light"); g === null || g === void 0 ? void 0 : g.classList.remove("is-dark") } }) }; darklistener.add(c); var b = function (h) { var e = document.getElementById("logo"); var g = e === null || e === void 0 ? void 0 : e.getAttribute("data-src-darkmode"); var d = e === null || e === void 0 ? void 0 : e.getAttribute("data-src-lightmode"); if (!e || !g || !d) { return } var f = (h === SystemDarkmodePrefrence.dark) ? g : d; e.setAttribute("src", f) }; darklistener.add(b) }); var SystemDarkmodePrefrence; (function (a) { a[a["dark"] = 0] = "dark"; a[a["light"] = 1] = "light" })(SystemDarkmodePrefrence || (SystemDarkmodePrefrence = {})); var DarkmodeListener = (function () { function a() { var b = window.matchMedia("(prefers-color-scheme: dark)").matches; this._mode = b ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; this._handlers = []; this._listen() } a.prototype._listen = function () { var d = this; var b = window.matchMedia("(prefers-color-scheme: dark)"); var c = function (e) { var f = e.matches ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; d._handlers.forEach(function (g) { g(f) }); d._mode = f }; b.addEventListener("change", c) }; a.prototype.add = function (b) { b(this._mode); this._handlers.push(b) }; a.prototype.mode = function () { return this._mode }; return a }()); var darklistener = new DarkmodeListener();
\ No newline at end of file +window.addEventListener("load",function(){var burgers=Array.prototype.slice.call(document.querySelectorAll(".navbar-burger"),0);0<burgers.length&&burgers.forEach(function(element){element.addEventListener("click",function(){var idstr=element.getAttribute("data-target");element.classList.toggle("is-active"),!idstr||null!=(idstr=document.getElementById(idstr))&&idstr.classList.toggle("is-active")},!1)}),darklistener.add(function(mode){Array.prototype.slice.call(document.querySelectorAll(".auto-dark"),0).forEach(function(element){mode===SystemDarkmodePreference.dark?(null!=element&&element.classList.add("is-dark"),null!=element&&element.classList.remove("is-light")):(null!=element&&element.classList.add("is-light"),null!=element&&element.classList.remove("is-dark"))}),console.debug("".concat(".auto-dark"," class changed to ").concat(enumModeToStringMode(mode)))}),darklistener.add(function(mode){var logo=document.getElementById("logo"),darksrc=null==logo?void 0:logo.getAttribute("data-src-darkmode"),lightsrc=null==logo?void 0:logo.getAttribute("data-src-lightmode");logo&&darksrc&&lightsrc&&(darksrc=mode===SystemDarkmodePreference.dark?darksrc:lightsrc,logo.setAttribute("src",darksrc),console.debug("logo src changed to ".concat(enumModeToStringMode(mode),"src")))});darklistener.add(function(mode){var shikwasa=document.body.querySelector('div[data-name="shikwasa"]');null!=shikwasa&&shikwasa.setAttribute("data-theme",enumModeToStringMode(mode)?enumModeToStringMode(mode):"light"),console.debug("shikwasa theme: ".concat(null==shikwasa?void 0:shikwasa.getAttribute("data-theme")))}),darklistener.listen()}),function(SystemDarkmodePreference){SystemDarkmodePreference[SystemDarkmodePreference.dark=0]="dark",SystemDarkmodePreference[SystemDarkmodePreference.light=1]="light"}(SystemDarkmodePreference=SystemDarkmodePreference||{});var SystemDarkmodePreference,modeMap={dark:SystemDarkmodePreference.dark,light:SystemDarkmodePreference.light},invertDarkModeObj={dark:"light",light:"dark"},enumModeToStringMode=function(i){var result=Object.keys(modeMap).filter(function(key){return isValidKey(key,modeMap)&&modeMap[key]===i});return result?result[0]:void 0},DarkmodeListener=function(){function DarkmodeListener(){var darking=window.matchMedia("(prefers-color-scheme: dark)").matches;this._mode=darking?SystemDarkmodePreference.dark:SystemDarkmodePreference.light,this._handlers=[]}return DarkmodeListener.prototype.listen=function(){function applyCustomDarkModeSettings(mode){var LSSetting="dark"==getLS(darkModeStorageKey)?SystemDarkmodePreference.dark:SystemDarkmodePreference.light,mode=mode||LSSetting,LSSetting=(console.debug("applyCustomDarkModeSettings: ".concat(mode)),mode===_this._mode?(resetRootDarkModeAttributeAndLS(),console.debug("Resetting to auto mode...")):mode==SystemDarkmodePreference.dark||mode==SystemDarkmodePreference.light?(rootElement.setAttribute(rootElementDarkModeAttributeName,enumModeToStringMode(mode)),console.debug('Setting prop: "data-user-color-scheme" in HTML...')):(resetRootDarkModeAttributeAndLS(),console.debug("Initial setup, setting theme to light as default..."),mode=SystemDarkmodePreference.light),document.getElementById("bulma-light")),darkCSS=document.getElementById("bulma-dark");mode==SystemDarkmodePreference.dark?(rootElement.setAttribute(rootElementDarkModeAttributeName,"dark"),LSSetting.setAttribute("media","none"),darkCSS.setAttribute("media","all"),_this._handlers.forEach(function(handler){handler(SystemDarkmodePreference.dark),console.debug("Invoking dark theme handler...")}),console.debug("Dark theme applied.")):mode==SystemDarkmodePreference.light&&(rootElement.setAttribute(rootElementDarkModeAttributeName,"light"),LSSetting.setAttribute("media","all"),darkCSS.setAttribute("media","none"),_this._handlers.forEach(function(handler){handler(SystemDarkmodePreference.light),console.debug("Invoking light theme handler...")}),console.debug("Light theme applied."))}var _this=this,rootElement=document.documentElement,darkModeStorageKey="user-color-scheme",rootElementDarkModeAttributeName="data-user-color-scheme",darkModeTogglebuttonElement=document.getElementById("btn-toggle-dark"),resetRootDarkModeAttributeAndLS=function(){rootElement.removeAttribute(rootElementDarkModeAttributeName),removeLS(darkModeStorageKey)};applyCustomDarkModeSettings(null),null!=darkModeTogglebuttonElement&&darkModeTogglebuttonElement.addEventListener("click",function(){var curMode,currentSetting;console.debug("User clicked button. Doing black magic now..."),applyCustomDarkModeSettings((currentSetting=null===(currentSetting=getLS(darkModeStorageKey))?isValidKey(curMode=enumModeToStringMode(_this._mode),invertDarkModeObj)?invertDarkModeObj[curMode]:currentSetting:isValidKey(currentSetting,invertDarkModeObj)?invertDarkModeObj[currentSetting]:currentSetting,setLS(darkModeStorageKey,currentSetting),isValidKey(currentSetting,invertDarkModeObj)?modeMap[currentSetting]:null))})},DarkmodeListener.prototype.add=function(callback){callback(this._mode),this._handlers.push(callback)},DarkmodeListener.prototype.mode=function(){return this._mode},DarkmodeListener}(),darklistener=new DarkmodeListener;function setLS(k,v){try{localStorage.setItem(k,v)}catch(e){}}function removeLS(k){try{localStorage.removeItem(k)}catch(e){}}function getLS(k){try{return localStorage.getItem(k)}catch(e){return null}}function isValidKey(key,obj){return key in obj} diff --git a/source/js/script.ts b/source/js/script.ts index 912c1d9..bfc1d64 100644 --- a/source/js/script.ts +++ b/source/js/script.ts @@ -17,73 +17,197 @@ window.addEventListener("load", () => {      }      // Add header hover page class changer -    const colorman = (mode: SystemDarkmodePrefrence) => { +    const colorman = (mode: SystemDarkmodePreference) => {          let clsname: string = ".auto-dark";          const elements: Element[] = Array.prototype.slice.call(              document.querySelectorAll(clsname), 0);          elements.forEach((element) => { -            if (mode === SystemDarkmodePrefrence.dark) { +            if (mode === SystemDarkmodePreference.dark) {                  element?.classList.add("is-dark");                  element?.classList.remove("is-light");              } else { +                // If mode == null , fallback to light mode                  element?.classList.add("is-light");                  element?.classList.remove("is-dark");              }          }) +        console.debug(`${clsname} class changed to ${enumModeToStringMode(mode)}`);      }      darklistener.add(colorman);      // Add logo color selector -    const logoman = (mode: SystemDarkmodePrefrence) => { +    const logoman = (mode: SystemDarkmodePreference) => {          const logo = document.getElementById("logo");          const darksrc = logo?.getAttribute("data-src-darkmode");          const lightsrc = logo?.getAttribute("data-src-lightmode");          if (!logo || !darksrc || !lightsrc)              return; -        let src = (mode === SystemDarkmodePrefrence.dark) ? darksrc : lightsrc; +        let src = (mode === SystemDarkmodePreference.dark) ? darksrc : lightsrc;          logo.setAttribute("src", src); +        console.debug(`logo src changed to ${enumModeToStringMode(mode)}src`);      }      darklistener.add(logoman); -     + +    // Add player theme handler +    const playerman = (mode: SystemDarkmodePreference) => { +        const shikwasa = document.body.querySelector('div[data-name="shikwasa"]'); +        shikwasa?.setAttribute("data-theme", enumModeToStringMode(mode) ? enumModeToStringMode(mode)! : "light"); +        console.debug(`shikwasa theme: ${shikwasa?.getAttribute("data-theme")}`); +    }; +    darklistener.add(playerman); + +    // Listen after all the setups (to get the handler work :p). +    darklistener.listen();  });  /* Darkmode listener */ -enum SystemDarkmodePrefrence { +enum SystemDarkmodePreference {      dark = 0,      light = 1  }; +const modeMap = { +    dark: SystemDarkmodePreference.dark, +    light: SystemDarkmodePreference.light +}; + +const invertDarkModeObj: Object = { +    'dark': 'light', +    'light': 'dark' +}; + +const enumModeToStringMode = (i: SystemDarkmodePreference) => { +    let keysArray = Object.keys(modeMap); +    let result = keysArray.filter(key => isValidKey(key, modeMap) && modeMap[key] === i); +    return result ? result[0] : undefined; +}; +  class DarkmodeListener { -    private _mode: SystemDarkmodePrefrence; -    private _handlers: ((mode: SystemDarkmodePrefrence) => void)[]; +    private _mode: SystemDarkmodePreference; +    private _handlers: ((mode: SystemDarkmodePreference) => void)[];      public constructor() {          let darking = window.matchMedia('(prefers-color-scheme: dark)').matches; -        this._mode = darking ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; +        this._mode = darking ? SystemDarkmodePreference.dark : SystemDarkmodePreference.light;          this._handlers = []; -        this._listen();      } -    private _listen(): void { -        let media = window.matchMedia('(prefers-color-scheme: dark)'); -        let callback = (event: MediaQueryListEvent) => { -            let mode = event.matches ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; -            this._handlers.forEach(handler => { -                handler(mode); -            }); -            this._mode = mode; -        }; -        media.addEventListener("change", callback); +    public listen(): void { +        const rootElement = document.documentElement; +        const darkModeStorageKey = 'user-color-scheme'; +        const rootElementDarkModeAttributeName = 'data-user-color-scheme'; +        const darkModeTogglebuttonElement = document.getElementById('btn-toggle-dark'); + +        const resetRootDarkModeAttributeAndLS = () => { +            rootElement.removeAttribute(rootElementDarkModeAttributeName); +            removeLS(darkModeStorageKey); +        } + +        // Partially taken from https://blog.skk.moe/post/hello-darkmode-my-old-friend, CC BY-NC-SA 4.0 +        const applyCustomDarkModeSettings = (mode: any) => { +            // 接受从「开关」处传来的模式,或者从 localStorage 读取 +            let LSSetting = getLS(darkModeStorageKey) == 'dark' ? SystemDarkmodePreference.dark : 'light' ? SystemDarkmodePreference.light : null; +            let currentSetting: SystemDarkmodePreference = mode || LSSetting; +            console.debug(`applyCustomDarkModeSettings: ${currentSetting}`); +            if (currentSetting === this._mode) { +                // 当用户自定义的显示模式和 prefers-color-scheme 相同时重置、恢复到自动模式 +                resetRootDarkModeAttributeAndLS(); +                console.debug('Resetting to auto mode...'); +            } else if (currentSetting == SystemDarkmodePreference.dark || currentSetting == SystemDarkmodePreference.light) { +                // 否则设置为用户自定义的显示模式 +                rootElement.setAttribute(rootElementDarkModeAttributeName, enumModeToStringMode(currentSetting)!); +                console.debug('Setting prop: "data-user-color-scheme" in HTML...'); +            } else { +                // 首次访问或从未使用过开关、localStorage 中没有存储的值,currentSetting 是 null +                // 或者 localStorage 被篡改,currentSetting 不是合法值 +                // 默认显示浅色主题 +                resetRootDarkModeAttributeAndLS(); +                console.debug('Initial setup, setting theme to light as default...') +                currentSetting = SystemDarkmodePreference.light; +            } +            let lightCSS = document.getElementById("bulma-light")! +            let darkCSS = document.getElementById("bulma-dark")! +            if (currentSetting == SystemDarkmodePreference.dark) { +                rootElement.setAttribute(rootElementDarkModeAttributeName, 'dark'); +                lightCSS.setAttribute("media", "none"); +                darkCSS.setAttribute("media", "all"); +                this._handlers.forEach(handler => { +                    handler(SystemDarkmodePreference.dark); +                    console.debug('Invoking dark theme handler...'); +                }); +                console.debug('Dark theme applied.'); +            } else if (currentSetting == SystemDarkmodePreference.light) { +                rootElement.setAttribute(rootElementDarkModeAttributeName, 'light'); +                lightCSS.setAttribute("media", "all"); +                darkCSS.setAttribute("media", "none"); +                this._handlers.forEach(handler => { +                    handler(SystemDarkmodePreference.light); +                    console.debug('Invoking light theme handler...'); +                }); +                console.debug('Light theme applied.'); +            } + +        } + +        const toggleCustomDarkMode = () => { +            let currentSetting = getLS(darkModeStorageKey); +            if (currentSetting === null) { +                let curMode = enumModeToStringMode(this._mode)!; +                currentSetting = isValidKey(curMode, invertDarkModeObj) ? invertDarkModeObj[curMode] : currentSetting; +            } else if (currentSetting == 'dark' || 'light') { +                // 从 localStorage 中读取模式,并取相反的模式 +                currentSetting = isValidKey(currentSetting, invertDarkModeObj) ? invertDarkModeObj[currentSetting] : currentSetting; +            } else { +                // 不知道出了什么幺蛾子,比如 localStorage 被篡改成非法值 +                return; // 直接 return; +            } +            // 将相反的模式写入 localStorage +            setLS(darkModeStorageKey, currentSetting); +            return isValidKey(currentSetting!, invertDarkModeObj) ? modeMap[currentSetting!] : null; +        } + +        applyCustomDarkModeSettings(null); + +        darkModeTogglebuttonElement?.addEventListener('click', () => { +            // 当用户点击「开关」时,获得新的显示模式、写入 localStorage、并在页面上生效 +            console.debug('User clicked button. Doing black magic now...'); +            applyCustomDarkModeSettings(toggleCustomDarkMode()); +        });      } -    public add(callback: (mode: SystemDarkmodePrefrence) => void): void { +    public add(callback: (mode: SystemDarkmodePreference) => void): void {          callback(this._mode);          this._handlers.push(callback);      } -    public mode(): SystemDarkmodePrefrence { +    public mode(): SystemDarkmodePreference {          return this._mode;      }  };  var darklistener = new DarkmodeListener(); + +function setLS(k: string, v: any) { +    try { +        localStorage.setItem(k, v); +    } catch (e) { } +} + +function removeLS(k: string) { +    try { +        localStorage.removeItem(k); +    } catch (e) { } +} + +function getLS(k: string) { +    try { +        return localStorage.getItem(k); +    } catch (e) { +        return null; // 与 localStorage 中没有找到对应 key 的行为一致 +    } +} + +// 使用isValidKey判断key是否存在对象类型中 +function isValidKey(key: string, obj: object): key is keyof typeof obj { +    return key in obj; +}
\ No newline at end of file | 
