diff --git a/_includes/analytics/cloudflare.html b/_includes/analytics/cloudflare.html
index 1eeb1a9..9faa11e 100644
--- a/_includes/analytics/cloudflare.html
+++ b/_includes/analytics/cloudflare.html
@@ -4,4 +4,3 @@
src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon='{"token": "{{ site.analytics.cloudflare.id }}"}'
>
-
diff --git a/_includes/analytics/fathom.html b/_includes/analytics/fathom.html
index 4b603d3..216bb14 100644
--- a/_includes/analytics/fathom.html
+++ b/_includes/analytics/fathom.html
@@ -2,6 +2,5 @@
-
+ defer
+>
diff --git a/_includes/analytics/google.html b/_includes/analytics/google.html
index d0aac65..dfe4828 100644
--- a/_includes/analytics/google.html
+++ b/_includes/analytics/google.html
@@ -1,7 +1,7 @@
-
diff --git a/_includes/comments.html b/_includes/comment.html
similarity index 100%
rename from _includes/comments.html
rename to _includes/comment.html
diff --git a/_includes/comments/disqus.html b/_includes/comments/disqus.html
index 2b889a4..fd12a3c 100644
--- a/_includes/comments/disqus.html
+++ b/_includes/comments/disqus.html
@@ -1,38 +1,25 @@
-
-
-
-
-
diff --git a/_includes/comments/giscus.html b/_includes/comments/giscus.html
index f9becfe..8058472 100644
--- a/_includes/comments/giscus.html
+++ b/_includes/comments/giscus.html
@@ -1,21 +1,8 @@
-
-
-
diff --git a/_includes/head.html b/_includes/head.html
index af3acdb..310f52e 100644
--- a/_includes/head.html
+++ b/_includes/head.html
@@ -97,11 +97,32 @@
{% endif %}
-
+
{% unless site.theme_mode %}
- {% include mode-toggle.html %}
+
{% endunless %}
+ {% include js-selector.html lang=lang %}
+
+ {% if jekyll.environment == 'production' %}
+
+ {% if site.pwa.enabled %}
+
+ {% endif %}
+
+
+ {% for analytics in site.analytics %}
+ {% capture str %}{{ analytics }}{% endcapture %}
+ {% assign platform = str | split: '{' | first %}
+ {% if site.analytics[platform].id and site.analytics[platform].id != empty %}
+ {% include analytics/{{ platform }}.html %}
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+
{% include metadata-hook.html %}
diff --git a/_includes/js-selector.html b/_includes/js-selector.html
index 4d77d06..fd4acca 100644
--- a/_includes/js-selector.html
+++ b/_includes/js-selector.html
@@ -62,12 +62,12 @@
{% capture script %}/assets/js/dist/{{ js }}.min.js{% endcapture %}
-
+
{% if page.math %}
-
-
+
+
{% endif %}
@@ -84,26 +84,3 @@
{% endcase %}
{% endif %}
{% endif %}
-
-{% if page.mermaid %}
- {% include mermaid.html %}
-{% endif %}
-
-{% if jekyll.environment == 'production' %}
-
- {% if site.pwa.enabled %}
-
- {% endif %}
-
-
- {% for analytics in site.analytics %}
- {% capture str %}{{ analytics }}{% endcapture %}
- {% assign type = str | split: '{' | first %}
- {% if site.analytics[type].id and site.analytics[type].id != empty %}
- {% include analytics/{{ type }}.html %}
- {% endif %}
- {% endfor %}
-{% endif %}
diff --git a/_includes/jsdelivr-combine.html b/_includes/jsdelivr-combine.html
index cffa699..0611213 100644
--- a/_includes/jsdelivr-combine.html
+++ b/_includes/jsdelivr-combine.html
@@ -1,6 +1,6 @@
{% assign urls = include.urls | split: ',' %}
-{% assign combined_urls = nil %}
+{% assign combined_urls = null %}
{% assign domain = 'https://cdn.jsdelivr.net/' %}
@@ -15,12 +15,12 @@
{% endif %}
{% elsif url contains '//' %}
-
+
{% else %}
-
+
{% endif %}
{% endfor %}
{% if combined_urls %}
-
+
{% endif %}
diff --git a/_includes/mermaid.html b/_includes/mermaid.html
deleted file mode 100644
index a3a83ed..0000000
--- a/_includes/mermaid.html
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
diff --git a/_includes/mode-toggle.html b/_includes/mode-toggle.html
deleted file mode 100644
index 113ec37..0000000
--- a/_includes/mode-toggle.html
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
diff --git a/_includes/search-loader.html b/_includes/search-loader.html
index 2582580..7fd065d 100644
--- a/_includes/search-loader.html
+++ b/_includes/search-loader.html
@@ -19,29 +19,31 @@
{% capture not_found %}{{ site.data.locales[include.lang].search.no_results }}
{% endcapture %}
diff --git a/_javascript/categories.js b/_javascript/categories.js
index 15d8251..ce87d67 100644
--- a/_javascript/categories.js
+++ b/_javascript/categories.js
@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
-import { categoryCollapse } from './modules/plugins';
+import { categoryCollapse } from './modules/components';
basic();
initSidebar();
diff --git a/_javascript/home.js b/_javascript/home.js
index ef22cb9..7f628a1 100644
--- a/_javascript/home.js
+++ b/_javascript/home.js
@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
-import { initLocaleDatetime, loadImg } from './modules/plugins';
+import { initLocaleDatetime, loadImg } from './modules/components';
loadImg();
initLocaleDatetime();
diff --git a/_javascript/misc.js b/_javascript/misc.js
index 52b4043..37130da 100644
--- a/_javascript/misc.js
+++ b/_javascript/misc.js
@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
-import { initLocaleDatetime } from './modules/plugins';
+import { initLocaleDatetime } from './modules/components';
initSidebar();
initTopbar();
diff --git a/_javascript/modules/plugins.js b/_javascript/modules/components.js
similarity index 60%
rename from _javascript/modules/plugins.js
rename to _javascript/modules/components.js
index cc95c1b..95791a6 100644
--- a/_javascript/modules/plugins.js
+++ b/_javascript/modules/components.js
@@ -4,3 +4,7 @@ export { loadImg } from './components/img-loading';
export { imgPopup } from './components/img-popup';
export { initLocaleDatetime } from './components/locale-datetime';
export { initToc } from './components/toc';
+export { loadMermaid } from './components/mermaid';
+export { modeWatcher } from './components/mode-toggle';
+export { back2top } from './components/back-to-top';
+export { loadTooptip } from './components/tooltip-loader';
diff --git a/_javascript/modules/components/img-popup.js b/_javascript/modules/components/img-popup.js
index ac12043..420a226 100644
--- a/_javascript/modules/components/img-popup.js
+++ b/_javascript/modules/components/img-popup.js
@@ -4,7 +4,6 @@
* Dependencies: https://github.com/biati-digital/glightbox
*/
-const html = document.documentElement;
const lightImages = '.popup:not(.dark)';
const darkImages = '.popup:not(.light)';
let selector = lightImages;
@@ -33,26 +32,17 @@ export function imgPopup() {
document.querySelector('.popup.dark') === null
);
- if (
- (html.hasAttribute('data-mode') &&
- html.getAttribute('data-mode') === 'dark') ||
- (!html.hasAttribute('data-mode') &&
- window.matchMedia('(prefers-color-scheme: dark)').matches)
- ) {
+ if (Theme.visualState === Theme.DARK) {
selector = darkImages;
}
let current = GLightbox({ selector: `${selector}` });
- if (hasDualImages && document.getElementById('mode-toggle')) {
+ if (hasDualImages && Theme.switchable) {
let reverse = null;
window.addEventListener('message', (event) => {
- if (
- event.source === window &&
- event.data &&
- event.data.direction === ModeToggle.ID
- ) {
+ if (event.source === window && event.data && event.data.id === Theme.ID) {
updateImages(current, reverse);
}
});
diff --git a/_javascript/modules/components/mermaid.js b/_javascript/modules/components/mermaid.js
new file mode 100644
index 0000000..2b4759f
--- /dev/null
+++ b/_javascript/modules/components/mermaid.js
@@ -0,0 +1,60 @@
+/**
+ * Mermaid-js loader
+ */
+
+const MERMAID = 'mermaid';
+const themeMapper = Theme.getThemeMapper('default', 'dark');
+
+function refreshTheme(event) {
+ if (event.source === window && event.data && event.data.id === Theme.ID) {
+ // Re-render the SVG ›
+ const mermaidList = document.getElementsByClassName(MERMAID);
+
+ [...mermaidList].forEach((elem) => {
+ const svgCode = elem.previousSibling.children.item(0).innerHTML;
+ elem.textContent = svgCode;
+ elem.removeAttribute('data-processed');
+ });
+
+ const newTheme = themeMapper[Theme.visualState];
+
+ mermaid.initialize({ theme: newTheme });
+ mermaid.init(null, `.${MERMAID}`);
+ }
+}
+
+function setNode(elem) {
+ const svgCode = elem.textContent;
+ const backup = elem.parentElement;
+ backup.classList.add('d-none');
+ // Create mermaid node
+ const mermaid = document.createElement('pre');
+ mermaid.classList.add(MERMAID);
+ const text = document.createTextNode(svgCode);
+ mermaid.appendChild(text);
+ backup.after(mermaid);
+}
+
+export function loadMermaid() {
+ if (
+ typeof mermaid === 'undefined' ||
+ typeof mermaid.initialize !== 'function'
+ ) {
+ return;
+ }
+
+ const initTheme = themeMapper[Theme.visualState];
+
+ let mermaidConf = {
+ theme: initTheme
+ };
+
+ const basicList = document.getElementsByClassName('language-mermaid');
+ [...basicList].forEach(setNode);
+
+ mermaid.initialize(mermaidConf);
+
+ if (Theme.switchable) {
+ window.addEventListener('message', refreshTheme);
+ }
+}
diff --git a/_javascript/modules/components/mode-toggle.js b/_javascript/modules/components/mode-toggle.js
new file mode 100644
index 0000000..455ff0a
--- /dev/null
+++ b/_javascript/modules/components/mode-toggle.js
@@ -0,0 +1,15 @@
+/**
+ * Add listener for theme mode toggle
+ */
+
+const $toggle = document.getElementById('mode-toggle');
+
+export function modeWatcher() {
+ if (!$toggle) {
+ return;
+ }
+
+ $toggle.addEventListener('click', () => {
+ Theme.flip();
+ });
+}
diff --git a/_javascript/modules/components/mode-watcher.js b/_javascript/modules/components/mode-watcher.js
deleted file mode 100644
index 9eecd09..0000000
--- a/_javascript/modules/components/mode-watcher.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Add listener for theme mode toggle
- */
-const toggle = document.getElementById('mode-toggle');
-
-export function modeWatcher() {
- if (!toggle) {
- return;
- }
-
- toggle.addEventListener('click', () => {
- modeToggle.flipMode();
- });
-}
diff --git a/_javascript/modules/components/sidebar.js b/_javascript/modules/components/sidebar.js
deleted file mode 100644
index aed759e..0000000
--- a/_javascript/modules/components/sidebar.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Expand or close the sidebar in mobile screens.
- */
-
-const $sidebar = document.getElementById('sidebar');
-const $trigger = document.getElementById('sidebar-trigger');
-const $mask = document.getElementById('mask');
-
-class SidebarUtil {
- static #isExpanded = false;
-
- static toggle() {
- this.#isExpanded = !this.#isExpanded;
- document.body.toggleAttribute('sidebar-display', this.#isExpanded);
- $sidebar.classList.toggle('z-2', this.#isExpanded);
- $mask.classList.toggle('d-none', !this.#isExpanded);
- }
-}
-
-export function sidebarExpand() {
- $trigger.onclick = $mask.onclick = () => SidebarUtil.toggle();
-}
diff --git a/_javascript/modules/layouts/basic.js b/_javascript/modules/layouts/basic.js
index fb36a8b..b8eddf6 100644
--- a/_javascript/modules/layouts/basic.js
+++ b/_javascript/modules/layouts/basic.js
@@ -1,7 +1,7 @@
-import { back2top } from '../components/back-to-top';
-import { loadTooptip } from '../components/tooltip-loader';
+import { back2top, loadTooptip, modeWatcher } from '../components';
export function basic() {
+ modeWatcher();
back2top();
loadTooptip();
}
diff --git a/_javascript/modules/layouts/sidebar.js b/_javascript/modules/layouts/sidebar.js
index 8795693..bbf5e7d 100644
--- a/_javascript/modules/layouts/sidebar.js
+++ b/_javascript/modules/layouts/sidebar.js
@@ -1,7 +1,19 @@
-import { modeWatcher } from '../components/mode-watcher';
-import { sidebarExpand } from '../components/sidebar';
+const ATTR_DISPLAY = 'sidebar-display';
+const $sidebar = document.getElementById('sidebar');
+const $trigger = document.getElementById('sidebar-trigger');
+const $mask = document.getElementById('mask');
+
+class SidebarUtil {
+ static #isExpanded = false;
+
+ static toggle() {
+ this.#isExpanded = !this.#isExpanded;
+ document.body.toggleAttribute(ATTR_DISPLAY, this.#isExpanded);
+ $sidebar.classList.toggle('z-2', this.#isExpanded);
+ $mask.classList.toggle('d-none', !this.#isExpanded);
+ }
+}
export function initSidebar() {
- modeWatcher();
- sidebarExpand();
+ $trigger.onclick = $mask.onclick = () => SidebarUtil.toggle();
}
diff --git a/_javascript/modules/theme.js b/_javascript/modules/theme.js
new file mode 100644
index 0000000..f9ebf20
--- /dev/null
+++ b/_javascript/modules/theme.js
@@ -0,0 +1,135 @@
+/**
+ * Theme management class
+ *
+ * To reduce flickering during page load, this script should be loaded synchronously.
+ */
+class Theme {
+ static #modeKey = 'mode';
+ static #modeAttr = 'data-mode';
+ static #darkMedia = window.matchMedia('(prefers-color-scheme: dark)');
+ static switchable = !document.documentElement.hasAttribute(this.#modeAttr);
+
+ static get DARK() {
+ return 'dark';
+ }
+
+ static get LIGHT() {
+ return 'light';
+ }
+
+ /**
+ * @returns {string} Theme mode identifier
+ */
+ static get ID() {
+ return 'theme-mode';
+ }
+
+ /**
+ * Gets the current visual state of the theme.
+ *
+ * @returns {string} The current visual state, either the mode if it exists,
+ * or the system dark mode state ('dark' or 'light').
+ */
+ static get visualState() {
+ if (this.#hasMode) {
+ return this.#mode;
+ } else {
+ return this.#sysDark ? this.DARK : this.LIGHT;
+ }
+ }
+
+ static get #mode() {
+ return sessionStorage.getItem(this.#modeKey);
+ }
+
+ static get #isDarkMode() {
+ return this.#mode === this.DARK;
+ }
+
+ static get #hasMode() {
+ return this.#mode !== null;
+ }
+
+ static get #sysDark() {
+ return this.#darkMedia.matches;
+ }
+
+ /**
+ * Maps theme modes to provided values
+ * @param {string} light Value for light mode
+ * @param {string} dark Value for dark mode
+ * @returns {Object} Mapped values
+ */
+ static getThemeMapper(light, dark) {
+ return {
+ [this.LIGHT]: light,
+ [this.DARK]: dark
+ };
+ }
+
+ /**
+ * Initializes the theme based on system preferences or stored mode
+ */
+ static init() {
+ if (!this.switchable) {
+ return;
+ }
+
+ this.#darkMedia.addEventListener('change', () => {
+ const lastMode = this.#mode;
+ this.#clearMode();
+
+ if (lastMode !== this.visualState) {
+ this.#notify();
+ }
+ });
+
+ if (!this.#hasMode) {
+ return;
+ }
+
+ if (this.#isDarkMode) {
+ this.#setDark();
+ } else {
+ this.#setLight();
+ }
+ }
+
+ /**
+ * Flips the current theme mode
+ */
+ static flip() {
+ if (this.#hasMode) {
+ this.#clearMode();
+ } else {
+ this.#sysDark ? this.#setLight() : this.#setDark();
+ }
+ this.#notify();
+ }
+
+ static #setDark() {
+ document.documentElement.setAttribute(this.#modeAttr, this.DARK);
+ sessionStorage.setItem(this.#modeKey, this.DARK);
+ }
+
+ static #setLight() {
+ document.documentElement.setAttribute(this.#modeAttr, this.LIGHT);
+ sessionStorage.setItem(this.#modeKey, this.LIGHT);
+ }
+
+ static #clearMode() {
+ document.documentElement.removeAttribute(this.#modeAttr);
+ sessionStorage.removeItem(this.#modeKey);
+ }
+
+ /**
+ * Notifies other plugins that the theme mode has changed
+ */
+ static #notify() {
+ window.postMessage({ id: this.ID }, '*');
+ }
+}
+
+Theme.init();
+
+export default Theme;
diff --git a/_javascript/page.js b/_javascript/page.js
index 76e8ce9..4b03b79 100644
--- a/_javascript/page.js
+++ b/_javascript/page.js
@@ -1,9 +1,15 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
-import { loadImg, imgPopup, initClipboard } from './modules/plugins';
+import {
+ loadImg,
+ imgPopup,
+ initClipboard,
+ loadMermaid
+} from './modules/components';
loadImg();
imgPopup();
initSidebar();
initTopbar();
initClipboard();
+loadMermaid();
basic();
diff --git a/_javascript/post.js b/_javascript/post.js
index 1c616ec..dc472b4 100644
--- a/_javascript/post.js
+++ b/_javascript/post.js
@@ -5,8 +5,9 @@ import {
imgPopup,
initLocaleDatetime,
initClipboard,
- initToc
-} from './modules/plugins';
+ initToc,
+ loadMermaid
+} from './modules/components';
loadImg();
initToc();
@@ -15,4 +16,5 @@ initSidebar();
initLocaleDatetime();
initClipboard();
initTopbar();
+loadMermaid();
basic();
diff --git a/_layouts/default.html b/_layouts/default.html
index 1590ef6..c83c561 100644
--- a/_layouts/default.html
+++ b/_layouts/default.html
@@ -74,8 +74,12 @@ layout: compress
{% include_cached notification.html lang=lang %}
{% endif %}
-
- {% include js-selector.html lang=lang %}
+
+
+ {% for _include in layout.script_includes %}
+ {% assign _include_path = _include | append: '.html' %}
+ {% include {{ _include_path }} %}
+ {% endfor %}
{% include_cached search-loader.html lang=lang %}
Comments powered by Disqus.
-