Files
LoveIt/src/js/theme.js
2020-03-16 10:59:51 +08:00

330 lines
14 KiB
JavaScript

(() => {
'use strict';
class Util {
forEach(elements, handler) {
elements = elements || [];
for (let i = 0; i < elements.length; i++) {
handler(elements[i]);
}
}
getScrollTop() {
return (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
}
isMobile() {
return window.matchMedia('only screen and (max-width: 680px)').matches;
}
isTocStatic() {
return window.matchMedia('only screen and (max-width: 960px)').matches;
}
}
class Theme {
constructor() {
this.util = new Util();
this.newScrollTop = this.util.getScrollTop();
this.oldScrollTop = this.newScrollTop;
this.scrollEventSet = new Set();
this.resizeEventSet = new Set();
}
initMenuMobile() {
const menuToggleMobile = document.getElementById('menu-toggle-mobile');
const menuMobile = document.getElementById('menu-mobile');
this._menuMobileOnScroll = this._menuMobileOnScroll || (() => {
menuToggleMobile.classList.remove('active');
menuMobile.classList.remove('active');
});
if (this.util.isMobile()) {
menuToggleMobile.onclick = () => {
menuToggleMobile.classList.toggle('active');
menuMobile.classList.toggle('active');
};
this.scrollEventSet.add(this._menuMobileOnScroll);
} else {
this.scrollEventSet.delete(this._menuMobileOnScroll);
}
}
initSwitchTheme() {
this.util.forEach(document.getElementsByClassName('theme-switch'), (button) => {
button.onclick = () => {
document.body.classList.toggle('dark-theme');
window.isDark = !window.isDark;
window.localStorage && window.localStorage.setItem('theme', window.isDark ? 'dark' : 'light');
this.initEcharts();
};
});
}
initHighlight() {
this.util.forEach(document.querySelectorAll('.highlight > .chroma'), (block) => {
const codes = block.querySelectorAll('pre.chroma > code');
const code = codes[codes.length - 1];
const lang = code ? code.className.toLowerCase() : '';
block.className += ' ' + lang;
});
this.util.forEach(document.querySelectorAll('.highlight > pre.chroma'), (block) => {
const chroma = document.createElement('div');
chroma.className = block.className;
const table = document.createElement('table');
chroma.appendChild(table);
const tbody = document.createElement('tbody');
table.appendChild(tbody);
const tr = document.createElement('tr');
tbody.appendChild(tr);
const td = document.createElement('td');
tr.appendChild(td);
block.parentElement.replaceChild(chroma, block);
td.appendChild(block);
});
}
initTable() {
this.util.forEach(document.querySelectorAll('.content table'), (table) => {
const wrapper = document.createElement('div');
wrapper.className = 'table-wrapper';
table.parentElement.replaceChild(wrapper, table);
wrapper.appendChild(table);
});
}
initHeaderLink() {
for (let num = 1; num <= 6; num++) {
this.util.forEach(document.querySelectorAll('.page.single .content > h' + num), (header) => {
header.classList.add('headerLink');
header.innerHTML = `<a href="#${header.id}" class="header-mark"></a>${header.innerHTML}`;
});
}
}
initToc() {
const tocCore = document.getElementById('TableOfContents');
if (tocCore === null) return;
if (this.util.isTocStatic()) {
const tocContentStatic = document.getElementById('toc-content-static');
if (tocCore.parentElement !== tocContentStatic) {
tocCore.parentElement.removeChild(tocCore);
tocContentStatic.appendChild(tocCore);
}
if (this._tocOnScroll) this.scrollEventSet.delete(this._tocOnScroll);
} else {
const tocContentAuto = document.getElementById('toc-content-auto');
if (tocCore.parentElement !== tocContentAuto) {
tocCore.parentElement.removeChild(tocCore);
tocContentAuto.appendChild(tocCore);
}
const toc = document.getElementById('toc-auto');
const page = document.getElementsByClassName('page')[0];
toc.style.maxWidth = `${page.getBoundingClientRect().left - 20}px`;
this._tocLinks = this._tocLinks || tocCore.getElementsByTagName('a');
this._tocLis = this._tocLis || tocCore.getElementsByTagName('li');
this._headerLinks = this._headerLinks || document.getElementsByClassName('headerLink') || [];
const headerIsFixed = window.desktopHeaderMode !== 'normal';
const headerHeight = document.getElementById('header-desktop').offsetHeight;
const TOP_SPACING = 20 + (headerIsFixed ? headerHeight : 0);
const minTocTop = toc.offsetTop;
const minScrollTop = minTocTop - TOP_SPACING + (headerIsFixed ? 0 : headerHeight);
this._tocOnScroll = this._tocOnScroll || (() => {
const footerTop = document.getElementById('post-footer').offsetTop;
const maxTocTop = footerTop - toc.getBoundingClientRect().height;
const maxScrollTop = maxTocTop - TOP_SPACING + (headerIsFixed ? 0 : headerHeight);
const rect = page.getBoundingClientRect();
if (this.newScrollTop < minScrollTop) {
toc.style.position = 'absolute';
toc.style.top = `${minTocTop}px`;
toc.style.left = `${rect.width + 20}px`;
} else if (this.newScrollTop > maxScrollTop) {
toc.style.position = 'absolute';
toc.style.top = `${maxTocTop}px`;
toc.style.left = `${rect.width + 20}px`;
} else {
toc.style.position = 'fixed';
toc.style.top = `${TOP_SPACING}px`;
toc.style.left = `${rect.left + rect.width + 20}px`;
}
this.util.forEach(this._tocLinks, (link) => { link.classList.remove('active'); });
this.util.forEach(this._tocLis, (link) => { link.classList.remove('has-active'); });
const INDEX_SPACING = 20 + (headerIsFixed ? headerHeight : 0);
let activeTocIndex = this._headerLinks.length - 1;
for (let i = 0; i < this._headerLinks.length - 1; i++) {
const thisTop = this._headerLinks[i].getBoundingClientRect().top;
const nextTop = this._headerLinks[i + 1].getBoundingClientRect().top;
if ((i == 0 && thisTop > INDEX_SPACING)
|| (thisTop <= INDEX_SPACING && nextTop > INDEX_SPACING)) {
activeTocIndex = i;
break;
}
}
if (activeTocIndex !== -1) {
this._tocLinks[activeTocIndex].classList.add('active');
let parent = this._tocLinks[activeTocIndex].parentElement;
while (parent !== tocCore) {
parent.classList.add('has-active');
parent = parent.parentElement.parentElement;
}
}
});
this._tocOnScroll();
this.scrollEventSet.add(this._tocOnScroll);
}
}
initMermaid() {
if (window.mermaidArr) {
mermaid.initialize({startOnLoad: false, theme: 'null'});
this.util.forEach(window.mermaidArr, (id) => {
const element = document.getElementById(id);
mermaid.mermaidAPI.render('svg-' + id, window.contentMap[id], (svgCode) => {
element.innerHTML = svgCode;
}, element);
});
}
}
initEcharts() {
if (window.echartsArr) {
this._echartsArr = this._echartsArr || [];
for (let i = 0; i < this._echartsArr.length; i++) {
this._echartsArr[i].dispose();
}
this._echartsArr = [];
this.util.forEach(window.echartsArr, (id) => {
const chart = echarts.init(document.getElementById(id), window.isDark ? 'dark' : 'macarons', {renderer: 'svg'});
chart.setOption(JSON.parse(window.contentMap[id]));
this._echartsArr.push(chart);
});
this._echartsOnResize = this._echartsOnResize || (() => {
for (let i = 0; i < this._echartsArr.length; i++) {
this._echartsArr[i].resize();
}
});
this.resizeEventSet.add(this._echartsOnResize);
}
}
initTypeit() {
if (window.typeitArr) {
for (let i = 0; i < window.typeitArr.length; i++) {
const group = window.typeitArr[i];
(function typeone(i) {
const id = group[i];
if (i === group.length - 1) {
new TypeIt(`#${id}`, {
strings: window.contentMap[id],
}).go();
return;
}
let instance = new TypeIt(`#${id}`, {
strings: window.contentMap[id],
afterComplete: () => {
instance.destroy();
typeone(i + 1);
},
}).go();
})(0);
}
}
}
initSmoothScroll() {
if ((!this.util.isMobile() && window.desktopHeaderMode === 'normal')
|| (this.util.isMobile() && window.mobileHeaderMode === 'normal')) {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true});
} else {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true, header: '#header-desktop'});
}
}
onScroll() {
const headers = [];
if (window.desktopHeaderMode === 'auto') headers.push(document.getElementById('header-desktop'));
if (window.mobileHeaderMode === 'auto') headers.push(document.getElementById('header-mobile'));
this.util.forEach(headers, (header) => {
header.classList.add('animated');
header.classList.add('faster');
});
const toTopButton = document.getElementById('dynamic-to-top');
const MIN_SCROLL = 10;
window.addEventListener('scroll', () => {
this.newScrollTop = this.util.getScrollTop();
const scroll = this.newScrollTop - this.oldScrollTop;
this.util.forEach(headers, (header) => {
if (scroll > MIN_SCROLL) {
header.classList.remove('fadeInDown');
header.classList.add('fadeOutUp');
} else if (scroll < - MIN_SCROLL) {
header.classList.remove('fadeOutUp');
header.classList.add('fadeInDown');
}
});
if (this.newScrollTop > 200) {
if (scroll > MIN_SCROLL) {
toTopButton.classList.remove('fadeIn');
toTopButton.classList.add('fadeOut');
} else if (scroll < - MIN_SCROLL) {
toTopButton.style.display = 'block';
toTopButton.classList.remove('fadeOut');
toTopButton.classList.add('fadeIn');
}
} else {
toTopButton.style.display = 'none';
}
if (!this._scrollTimeout) {
this._scrollTimeout = window.setTimeout(() => {
this._scrollTimeout = null;
for (let event of this.scrollEventSet) event();
}, 10);
}
this.oldScrollTop = this.newScrollTop;
}, false);
}
onResize() {
window.addEventListener('resize', () => {
if (!this._resizeTimeout) {
this._resizeTimeout = window.setTimeout(() => {
this._resizeTimeout = null;
for (let event of this.resizeEventSet) event();
this.initMenuMobile();
this.initToc();
this.initSmoothScroll();
this.initMermaid()
}, 100);
}
}, false);
}
init() {
this.initMenuMobile();
this.initSwitchTheme();
this.initHighlight();
this.initTable();
this.initHeaderLink();
this.initMermaid();
this.initEcharts();
this.initTypeit();
this.initToc();
this.initSmoothScroll();
this.onScroll();
this.onResize();
}
}
const themeInit = () => {
const theme = new Theme();
theme.init();
};
if (document.readyState !== 'loading') {
themeInit();
} else {
document.addEventListener('DOMContentLoaded', themeInit, false);
}
})();