use maintained theme
This commit is contained in:
parent
640572ecab
commit
0c7cb4486a
1779 changed files with 41468 additions and 26258 deletions
979
themes/CodeIT/src/js/theme.js
Normal file
979
themes/CodeIT/src/js/theme.js
Normal file
|
@ -0,0 +1,979 @@
|
|||
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;
|
||||
}
|
||||
|
||||
animateCSS(element, animation, reserved, callback) {
|
||||
if (!Array.isArray(animation)) animation = [animation];
|
||||
element.classList.add("animated", ...animation);
|
||||
const handler = () => {
|
||||
element.classList.remove("animated", ...animation);
|
||||
element.removeEventListener("animationend", handler);
|
||||
if (typeof callback === "function") callback();
|
||||
};
|
||||
if (!reserved) element.addEventListener("animationend", handler, false);
|
||||
}
|
||||
}
|
||||
|
||||
class Theme {
|
||||
constructor() {
|
||||
this.config = window.config;
|
||||
this.data = this.config.data;
|
||||
this.isDark = document.body.getAttribute("theme") === "dark";
|
||||
this.util = new Util();
|
||||
this.newScrollTop = this.util.getScrollTop();
|
||||
this.oldScrollTop = this.newScrollTop;
|
||||
this.scrollEventSet = new Set();
|
||||
this.resizeEventSet = new Set();
|
||||
this.switchThemeEventSet = new Set();
|
||||
this.clickMaskEventSet = new Set();
|
||||
if (window.objectFitImages) objectFitImages();
|
||||
}
|
||||
|
||||
initSVGIcon() {
|
||||
this.util.forEach(document.querySelectorAll("[data-svg-src]"), ($icon) => {
|
||||
fetch($icon.getAttribute("data-svg-src"))
|
||||
.then((response) => response.text())
|
||||
.then((svg) => {
|
||||
const $temp = document.createElement("div");
|
||||
$temp.insertAdjacentHTML("afterbegin", svg);
|
||||
const $svg = $temp.firstChild;
|
||||
$svg.setAttribute("data-svg-src", $icon.getAttribute("data-svg-src"));
|
||||
$svg.classList.add("icon");
|
||||
const $titleElements = $svg.getElementsByTagName("title");
|
||||
if ($titleElements.length) $svg.removeChild($titleElements[0]);
|
||||
$icon.parentElement.replaceChild($svg, $icon);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initTwemoji() {
|
||||
if (this.config.twemoji) twemoji.parse(document.body);
|
||||
}
|
||||
|
||||
initMenuMobile() {
|
||||
const $menuToggleMobile = document.getElementById("menu-toggle-mobile");
|
||||
const $menuMobile = document.getElementById("menu-mobile");
|
||||
$menuToggleMobile.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
document.body.classList.toggle("blur");
|
||||
$menuToggleMobile.classList.toggle("active");
|
||||
$menuMobile.classList.toggle("active");
|
||||
},
|
||||
false
|
||||
);
|
||||
this._menuMobileOnClickMask =
|
||||
this._menuMobileOnClickMask ||
|
||||
(() => {
|
||||
$menuToggleMobile.classList.remove("active");
|
||||
$menuMobile.classList.remove("active");
|
||||
});
|
||||
this.clickMaskEventSet.add(this._menuMobileOnClickMask);
|
||||
}
|
||||
|
||||
initSwitchTheme() {
|
||||
this.util.forEach(
|
||||
document.getElementsByClassName("theme-switch"),
|
||||
($themeSwitch) => {
|
||||
$themeSwitch.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (document.body.getAttribute("theme") === "dark")
|
||||
document.body.setAttribute("theme", "light");
|
||||
else document.body.setAttribute("theme", "dark");
|
||||
this.isDark = !this.isDark;
|
||||
window.localStorage &&
|
||||
localStorage.setItem("theme", this.isDark ? "dark" : "light");
|
||||
for (let event of this.switchThemeEventSet) event();
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
initSearch() {
|
||||
const searchConfig = this.config.search;
|
||||
const isMobile = this.util.isMobile();
|
||||
if (
|
||||
!searchConfig ||
|
||||
(isMobile && this._searchMobileOnce) ||
|
||||
(!isMobile && this._searchDesktopOnce)
|
||||
)
|
||||
return;
|
||||
|
||||
const maxResultLength = searchConfig.maxResultLength
|
||||
? searchConfig.maxResultLength
|
||||
: 10;
|
||||
const snippetLength = searchConfig.snippetLength
|
||||
? searchConfig.snippetLength
|
||||
: 50;
|
||||
const highlightTag = searchConfig.highlightTag
|
||||
? searchConfig.highlightTag
|
||||
: "em";
|
||||
|
||||
const suffix = isMobile ? "mobile" : "desktop";
|
||||
const $header = document.getElementById(`header-${suffix}`);
|
||||
const $searchInput = document.getElementById(`search-input-${suffix}`);
|
||||
const $searchToggle = document.getElementById(`search-toggle-${suffix}`);
|
||||
const $searchLoading = document.getElementById(`search-loading-${suffix}`);
|
||||
const $searchClear = document.getElementById(`search-clear-${suffix}`);
|
||||
if (isMobile) {
|
||||
this._searchMobileOnce = true;
|
||||
$searchInput.addEventListener(
|
||||
"focus",
|
||||
() => {
|
||||
document.body.classList.add("blur");
|
||||
$header.classList.add("open");
|
||||
},
|
||||
false
|
||||
);
|
||||
document.getElementById("search-cancel-mobile").addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
$header.classList.remove("open");
|
||||
document.body.classList.remove("blur");
|
||||
document
|
||||
.getElementById("menu-toggle-mobile")
|
||||
.classList.remove("active");
|
||||
document.getElementById("menu-mobile").classList.remove("active");
|
||||
$searchLoading.style.display = "none";
|
||||
$searchClear.style.display = "none";
|
||||
this._searchMobile && this._searchMobile.autocomplete.setVal("");
|
||||
},
|
||||
false
|
||||
);
|
||||
$searchClear.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
$searchClear.style.display = "none";
|
||||
this._searchMobile && this._searchMobile.autocomplete.setVal("");
|
||||
},
|
||||
false
|
||||
);
|
||||
this._searchMobileOnClickMask =
|
||||
this._searchMobileOnClickMask ||
|
||||
(() => {
|
||||
$header.classList.remove("open");
|
||||
$searchLoading.style.display = "none";
|
||||
$searchClear.style.display = "none";
|
||||
this._searchMobile && this._searchMobile.autocomplete.setVal("");
|
||||
});
|
||||
this.clickMaskEventSet.add(this._searchMobileOnClickMask);
|
||||
} else {
|
||||
this._searchDesktopOnce = true;
|
||||
$searchToggle.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
document.body.classList.add("blur");
|
||||
$header.classList.add("open");
|
||||
$searchInput.focus();
|
||||
},
|
||||
false
|
||||
);
|
||||
$searchClear.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
$searchClear.style.display = "none";
|
||||
this._searchDesktop && this._searchDesktop.autocomplete.setVal("");
|
||||
},
|
||||
false
|
||||
);
|
||||
this._searchDesktopOnClickMask =
|
||||
this._searchDesktopOnClickMask ||
|
||||
(() => {
|
||||
$header.classList.remove("open");
|
||||
$searchLoading.style.display = "none";
|
||||
$searchClear.style.display = "none";
|
||||
this._searchDesktop && this._searchDesktop.autocomplete.setVal("");
|
||||
});
|
||||
this.clickMaskEventSet.add(this._searchDesktopOnClickMask);
|
||||
}
|
||||
$searchInput.addEventListener(
|
||||
"input",
|
||||
() => {
|
||||
if ($searchInput.value === "") $searchClear.style.display = "none";
|
||||
else $searchClear.style.display = "inline";
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
const initAutosearch = () => {
|
||||
const autosearch = autocomplete(
|
||||
`#search-input-${suffix}`,
|
||||
{
|
||||
hint: false,
|
||||
autoselect: true,
|
||||
dropdownMenuContainer: `#search-dropdown-${suffix}`,
|
||||
clearOnSelected: true,
|
||||
cssClasses: { noPrefix: true },
|
||||
debug: true,
|
||||
},
|
||||
{
|
||||
name: "search",
|
||||
source: (query, callback) => {
|
||||
$searchLoading.style.display = "inline";
|
||||
$searchClear.style.display = "none";
|
||||
const finish = (results) => {
|
||||
$searchLoading.style.display = "none";
|
||||
$searchClear.style.display = "inline";
|
||||
callback(results);
|
||||
};
|
||||
if (searchConfig.type === "lunr") {
|
||||
const search = () => {
|
||||
if (lunr.queryHandler) query = lunr.queryHandler(query);
|
||||
const results = {};
|
||||
this._index
|
||||
.search(query)
|
||||
.forEach(({ ref, matchData: { metadata } }) => {
|
||||
const matchData = this._indexData[ref];
|
||||
let { uri, title, content: context } = matchData;
|
||||
if (results[uri]) return;
|
||||
let position = 0;
|
||||
Object.values(metadata).forEach(({ content }) => {
|
||||
if (content) {
|
||||
const matchPosition = content.position[0][0];
|
||||
if (matchPosition < position || position === 0)
|
||||
position = matchPosition;
|
||||
}
|
||||
});
|
||||
position -= snippetLength / 5;
|
||||
if (position > 0) {
|
||||
position +=
|
||||
context.substr(position, 20).lastIndexOf(" ") + 1;
|
||||
context = "..." + context.substr(position, snippetLength);
|
||||
} else {
|
||||
context = context.substr(0, snippetLength);
|
||||
}
|
||||
Object.keys(metadata).forEach((key) => {
|
||||
title = title.replace(
|
||||
new RegExp(`(${key})`, "gi"),
|
||||
`<${highlightTag}>$1</${highlightTag}>`
|
||||
);
|
||||
context = context.replace(
|
||||
new RegExp(`(${key})`, "gi"),
|
||||
`<${highlightTag}>$1</${highlightTag}>`
|
||||
);
|
||||
});
|
||||
results[uri] = {
|
||||
uri: uri,
|
||||
title: title,
|
||||
date: matchData.date,
|
||||
context: context,
|
||||
};
|
||||
});
|
||||
return Object.values(results).slice(0, maxResultLength);
|
||||
};
|
||||
if (!this._index) {
|
||||
fetch(searchConfig.lunrIndexURL)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const indexData = {};
|
||||
this._index = lunr(function () {
|
||||
if (searchConfig.lunrLanguageCode)
|
||||
this.use(lunr[searchConfig.lunrLanguageCode]);
|
||||
this.ref("objectID");
|
||||
this.field("title", { boost: 50 });
|
||||
this.field("tags", { boost: 20 });
|
||||
this.field("categories", { boost: 20 });
|
||||
this.field("content", { boost: 10 });
|
||||
this.metadataWhitelist = ["position"];
|
||||
data.forEach((record) => {
|
||||
indexData[record.objectID] = record;
|
||||
this.add(record);
|
||||
});
|
||||
});
|
||||
this._indexData = indexData;
|
||||
finish(search());
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
finish([]);
|
||||
});
|
||||
} else finish(search());
|
||||
} else if (searchConfig.type === "algolia") {
|
||||
this._algoliaIndex =
|
||||
this._algoliaIndex ||
|
||||
algoliasearch(
|
||||
searchConfig.algoliaAppID,
|
||||
searchConfig.algoliaSearchKey
|
||||
).initIndex(searchConfig.algoliaIndex);
|
||||
this._algoliaIndex
|
||||
.search(query, {
|
||||
offset: 0,
|
||||
length: maxResultLength * 8,
|
||||
attributesToHighlight: ["title"],
|
||||
attributesToSnippet: [`content:${snippetLength}`],
|
||||
highlightPreTag: `<${highlightTag}>`,
|
||||
highlightPostTag: `</${highlightTag}>`,
|
||||
})
|
||||
.then(({ hits }) => {
|
||||
const results = {};
|
||||
hits.forEach(
|
||||
({
|
||||
uri,
|
||||
date,
|
||||
_highlightResult: { title },
|
||||
_snippetResult: { content },
|
||||
}) => {
|
||||
if (
|
||||
results[uri] &&
|
||||
results[uri].context.length > content.value
|
||||
)
|
||||
return;
|
||||
results[uri] = {
|
||||
uri: uri,
|
||||
title: title.value,
|
||||
date: date,
|
||||
context: content.value,
|
||||
};
|
||||
}
|
||||
);
|
||||
finish(Object.values(results).slice(0, maxResultLength));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
finish([]);
|
||||
});
|
||||
}
|
||||
},
|
||||
templates: {
|
||||
suggestion: ({ title, date, context }) =>
|
||||
`<div><span class="suggestion-title">${title}</span><span class="suggestion-date">${date}</span></div><div class="suggestion-context">${context}</div>`,
|
||||
empty: ({ query }) =>
|
||||
`<div class="search-empty">${searchConfig.noResultsFound}: <span class="search-query">"${query}"</span></div>`,
|
||||
footer: ({}) => {
|
||||
const { searchType, icon, href } =
|
||||
searchConfig.type === "algolia"
|
||||
? {
|
||||
searchType: "algolia",
|
||||
icon: '<i class="fab fa-algolia fa-fw"></i>',
|
||||
href: "https://www.algolia.com/",
|
||||
}
|
||||
: {
|
||||
searchType: "Lunr.js",
|
||||
icon: "",
|
||||
href: "https://lunrjs.com/",
|
||||
};
|
||||
return `<div class="search-footer">Search by <a href="${href}" rel="noopener noreferrer" target="_blank">${icon} ${searchType}</a></div>`;
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
autosearch.on(
|
||||
"autocomplete:selected",
|
||||
(_event, suggestion, _dataset, _context) => {
|
||||
window.location.assign(suggestion.uri);
|
||||
}
|
||||
);
|
||||
if (isMobile) this._searchMobile = autosearch;
|
||||
else this._searchDesktop = autosearch;
|
||||
};
|
||||
if (
|
||||
searchConfig.lunrSegmentitURL &&
|
||||
!document.getElementById("lunr-segmentit")
|
||||
) {
|
||||
const script = document.createElement("script");
|
||||
script.id = "lunr-segmentit";
|
||||
script.type = "text/javascript";
|
||||
script.src = searchConfig.lunrSegmentitURL;
|
||||
script.async = true;
|
||||
if (script.readyState) {
|
||||
script.onreadystatechange = () => {
|
||||
if (
|
||||
script.readyState == "loaded" ||
|
||||
script.readyState == "complete"
|
||||
) {
|
||||
script.onreadystatechange = null;
|
||||
initAutosearch();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
script.onload = () => {
|
||||
initAutosearch();
|
||||
};
|
||||
}
|
||||
document.body.appendChild(script);
|
||||
} else initAutosearch();
|
||||
}
|
||||
|
||||
initDetails() {
|
||||
this.util.forEach(
|
||||
document.getElementsByClassName("details"),
|
||||
($details) => {
|
||||
const $summary = $details.getElementsByClassName("details-summary")[0];
|
||||
$summary.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
$details.classList.toggle("open");
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
initLightGallery() {
|
||||
if (this.config.lightGallery)
|
||||
lightGallery(
|
||||
document.getElementById("content"),
|
||||
this.config.lightGallery
|
||||
);
|
||||
}
|
||||
|
||||
initHighlight() {
|
||||
this.util.forEach(
|
||||
document.querySelectorAll(".highlight > pre.chroma"),
|
||||
($preChroma) => {
|
||||
const $chroma = document.createElement("div");
|
||||
$chroma.className = $preChroma.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);
|
||||
$preChroma.parentElement.replaceChild($chroma, $preChroma);
|
||||
$td.appendChild($preChroma);
|
||||
}
|
||||
);
|
||||
this.util.forEach(
|
||||
document.querySelectorAll(".highlight > .chroma"),
|
||||
($chroma) => {
|
||||
const $codeElements = $chroma.querySelectorAll("pre.chroma > code");
|
||||
if ($codeElements.length) {
|
||||
const $code = $codeElements[$codeElements.length - 1];
|
||||
const $header = document.createElement("div");
|
||||
$header.className = "code-header " + $code.className.toLowerCase();
|
||||
const $title = document.createElement("span");
|
||||
$title.classList.add("code-title");
|
||||
$title.insertAdjacentHTML(
|
||||
"afterbegin",
|
||||
'<i class="arrow fas fa-chevron-right fa-fw"></i>'
|
||||
);
|
||||
$title.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
$chroma.classList.toggle("open");
|
||||
},
|
||||
false
|
||||
);
|
||||
$header.appendChild($title);
|
||||
const $ellipses = document.createElement("span");
|
||||
$ellipses.insertAdjacentHTML(
|
||||
"afterbegin",
|
||||
'<i class="fas fa-ellipsis-h fa-fw"></i>'
|
||||
);
|
||||
$ellipses.classList.add("ellipses");
|
||||
$ellipses.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
$chroma.classList.add("open");
|
||||
},
|
||||
false
|
||||
);
|
||||
$header.appendChild($ellipses);
|
||||
const $copy = document.createElement("span");
|
||||
$copy.insertAdjacentHTML(
|
||||
"afterbegin",
|
||||
'<i class="far fa-copy fa-fw"></i>'
|
||||
);
|
||||
$copy.classList.add("copy");
|
||||
const code = $code.innerText;
|
||||
if (
|
||||
this.config.code.maxShownLines < 0 ||
|
||||
code.split("\n").length < this.config.code.maxShownLines + 2
|
||||
)
|
||||
$chroma.classList.add("open");
|
||||
if (this.config.code.copyTitle) {
|
||||
$copy.setAttribute("data-clipboard-text", code);
|
||||
$copy.title = this.config.code.copyTitle;
|
||||
const clipboard = new ClipboardJS($copy);
|
||||
clipboard.on("success", (_e) => {
|
||||
this.util.animateCSS($code, "flash");
|
||||
});
|
||||
$header.appendChild($copy);
|
||||
}
|
||||
$chroma.insertBefore($header, $chroma.firstChild);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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(".single .content > h" + num),
|
||||
($header) => {
|
||||
$header.classList.add("headerLink");
|
||||
$header.insertAdjacentHTML(
|
||||
"afterbegin",
|
||||
`<a href="#${$header.id}" class="header-mark"></a>`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
initToc() {
|
||||
const $tocCore = document.getElementById("TableOfContents");
|
||||
if ($tocCore === null) return;
|
||||
if (
|
||||
document.getElementById("toc-static").getAttribute("kept") ||
|
||||
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];
|
||||
const rect = $page.getBoundingClientRect();
|
||||
$toc.style.left = `${rect.left + rect.width + 20}px`;
|
||||
$toc.style.maxWidth = `${$page.getBoundingClientRect().left - 20}px`;
|
||||
$toc.style.visibility = "visible";
|
||||
const $tocLinkElements = $tocCore.querySelectorAll("a:first-child");
|
||||
const $tocLiElements = $tocCore.getElementsByTagName("li");
|
||||
const $headerLinkElements = document.getElementsByClassName("headerLink");
|
||||
const headerIsFixed =
|
||||
document.body.getAttribute("header-desktop") !== "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);
|
||||
if (this.newScrollTop < minScrollTop) {
|
||||
$toc.style.position = "absolute";
|
||||
$toc.style.top = `${minTocTop}px`;
|
||||
} else if (this.newScrollTop > maxScrollTop) {
|
||||
$toc.style.position = "absolute";
|
||||
$toc.style.top = `${maxTocTop}px`;
|
||||
} else {
|
||||
$toc.style.position = "fixed";
|
||||
$toc.style.top = `${TOP_SPACING}px`;
|
||||
}
|
||||
|
||||
this.util.forEach($tocLinkElements, ($tocLink) => {
|
||||
$tocLink.classList.remove("active");
|
||||
});
|
||||
this.util.forEach($tocLiElements, ($tocLi) => {
|
||||
$tocLi.classList.remove("has-active");
|
||||
});
|
||||
const INDEX_SPACING = 20 + (headerIsFixed ? headerHeight : 0);
|
||||
let activeTocIndex = $headerLinkElements.length - 1;
|
||||
for (let i = 0; i < $headerLinkElements.length - 1; i++) {
|
||||
const thisTop = $headerLinkElements[i].getBoundingClientRect().top;
|
||||
const nextTop = $headerLinkElements[i + 1].getBoundingClientRect()
|
||||
.top;
|
||||
if (
|
||||
(i == 0 && thisTop > INDEX_SPACING) ||
|
||||
(thisTop <= INDEX_SPACING && nextTop > INDEX_SPACING)
|
||||
) {
|
||||
activeTocIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (activeTocIndex !== -1) {
|
||||
$tocLinkElements[activeTocIndex].classList.add("active");
|
||||
let $parent = $tocLinkElements[activeTocIndex].parentElement;
|
||||
while ($parent !== $tocCore) {
|
||||
$parent.classList.add("has-active");
|
||||
$parent = $parent.parentElement.parentElement;
|
||||
}
|
||||
}
|
||||
});
|
||||
this._tocOnScroll();
|
||||
this.scrollEventSet.add(this._tocOnScroll);
|
||||
}
|
||||
}
|
||||
|
||||
initMath() {
|
||||
if (this.config.math) renderMathInElement(document.body, this.config.math);
|
||||
}
|
||||
|
||||
initMermaid() {
|
||||
const $mermaidElements = document.getElementsByClassName("mermaid");
|
||||
if ($mermaidElements.length) {
|
||||
mermaid.initialize({ startOnLoad: false, theme: "null" });
|
||||
this.util.forEach($mermaidElements, ($mermaid) => {
|
||||
mermaid.mermaidAPI.render(
|
||||
"svg-" + $mermaid.id,
|
||||
this.data[$mermaid.id],
|
||||
(svgCode) => {
|
||||
$mermaid.insertAdjacentHTML("afterbegin", svgCode);
|
||||
},
|
||||
$mermaid
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
initEcharts() {
|
||||
this._echartsOnSwitchTheme =
|
||||
this._echartsOnSwitchTheme ||
|
||||
(() => {
|
||||
this._echartsArr = this._echartsArr || [];
|
||||
for (let i = 0; i < this._echartsArr.length; i++) {
|
||||
this._echartsArr[i].dispose();
|
||||
}
|
||||
this._echartsArr = [];
|
||||
this.util.forEach(
|
||||
document.getElementsByClassName("echarts"),
|
||||
($echarts) => {
|
||||
const chart = echarts.init(
|
||||
$echarts,
|
||||
this.isDark ? "dark" : "macarons",
|
||||
{ renderer: "svg" }
|
||||
);
|
||||
chart.setOption(JSON.parse(this.data[$echarts.id]));
|
||||
this._echartsArr.push(chart);
|
||||
}
|
||||
);
|
||||
});
|
||||
this.switchThemeEventSet.add(this._echartsOnSwitchTheme);
|
||||
this._echartsOnSwitchTheme();
|
||||
this._echartsOnResize =
|
||||
this._echartsOnResize ||
|
||||
(() => {
|
||||
for (let i = 0; i < this._echartsArr.length; i++) {
|
||||
this._echartsArr[i].resize();
|
||||
}
|
||||
});
|
||||
this.resizeEventSet.add(this._echartsOnResize);
|
||||
}
|
||||
|
||||
initMapbox() {
|
||||
if (this.config.mapbox) {
|
||||
mapboxgl.accessToken = this.config.mapbox.accessToken;
|
||||
mapboxgl.setRTLTextPlugin(this.config.mapbox.RTLTextPlugin);
|
||||
this._mapboxArr = this._mapboxArr || [];
|
||||
this.util.forEach(
|
||||
document.getElementsByClassName("mapbox"),
|
||||
($mapbox) => {
|
||||
const {
|
||||
lng,
|
||||
lat,
|
||||
zoom,
|
||||
lightStyle,
|
||||
darkStyle,
|
||||
marked,
|
||||
navigation,
|
||||
geolocate,
|
||||
scale,
|
||||
fullscreen,
|
||||
} = this.data[$mapbox.id];
|
||||
const mapbox = new mapboxgl.Map({
|
||||
container: $mapbox,
|
||||
center: [lng, lat],
|
||||
zoom: zoom,
|
||||
minZoom: 0.2,
|
||||
style: this.isDark ? darkStyle : lightStyle,
|
||||
attributionControl: false,
|
||||
});
|
||||
if (marked) {
|
||||
new mapboxgl.Marker().setLngLat([lng, lat]).addTo(mapbox);
|
||||
}
|
||||
if (navigation) {
|
||||
mapbox.addControl(new mapboxgl.NavigationControl(), "bottom-right");
|
||||
}
|
||||
if (geolocate) {
|
||||
mapbox.addControl(
|
||||
new mapboxgl.GeolocateControl({
|
||||
positionOptions: {
|
||||
enableHighAccuracy: true,
|
||||
},
|
||||
showUserLocation: true,
|
||||
trackUserLocation: true,
|
||||
}),
|
||||
"bottom-right"
|
||||
);
|
||||
}
|
||||
if (scale) {
|
||||
mapbox.addControl(new mapboxgl.ScaleControl());
|
||||
}
|
||||
if (fullscreen) {
|
||||
mapbox.addControl(new mapboxgl.FullscreenControl());
|
||||
}
|
||||
mapbox.addControl(new MapboxLanguage());
|
||||
this._mapboxArr.push(mapbox);
|
||||
}
|
||||
);
|
||||
this._mapboxOnSwitchTheme =
|
||||
this._mapboxOnSwitchTheme ||
|
||||
(() => {
|
||||
this.util.forEach(this._mapboxArr, (mapbox) => {
|
||||
const $mapbox = mapbox.getContainer();
|
||||
const { lightStyle, darkStyle } = this.data[$mapbox.id];
|
||||
mapbox.setStyle(this.isDark ? darkStyle : lightStyle);
|
||||
mapbox.addControl(new MapboxLanguage());
|
||||
});
|
||||
});
|
||||
this.switchThemeEventSet.add(this._mapboxOnSwitchTheme);
|
||||
}
|
||||
}
|
||||
|
||||
initTypeit() {
|
||||
if (this.config.typeit) {
|
||||
const typeitConfig = this.config.typeit;
|
||||
const speed = typeitConfig.speed ? typeitConfig.speed : 100;
|
||||
const cursorSpeed = typeitConfig.cursorSpeed
|
||||
? typeitConfig.cursorSpeed
|
||||
: 1000;
|
||||
const cursorChar = typeitConfig.cursorChar
|
||||
? typeitConfig.cursorChar
|
||||
: "|";
|
||||
Object.values(typeitConfig.data).forEach((group) => {
|
||||
const typeone = (i) => {
|
||||
const id = group[i];
|
||||
const instance = new TypeIt(`#${id}`, {
|
||||
strings: this.data[id],
|
||||
speed: speed,
|
||||
lifeLike: true,
|
||||
cursorSpeed: cursorSpeed,
|
||||
cursorChar: cursorChar,
|
||||
waitUntilVisible: true,
|
||||
afterComplete: () => {
|
||||
if (i === group.length - 1) {
|
||||
if (typeitConfig.duration >= 0)
|
||||
window.setTimeout(() => {
|
||||
instance.destroy();
|
||||
}, typeitConfig.duration);
|
||||
return;
|
||||
}
|
||||
instance.destroy();
|
||||
typeone(i + 1);
|
||||
},
|
||||
}).go();
|
||||
};
|
||||
typeone(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
initComment() {
|
||||
if (this.config.comment) {
|
||||
if (this.config.comment.gitalk) {
|
||||
this.config.comment.gitalk.body = decodeURI(window.location.href);
|
||||
const gitalk = new Gitalk(this.config.comment.gitalk);
|
||||
gitalk.render("gitalk");
|
||||
}
|
||||
if (this.config.comment.valine) new Valine(this.config.comment.valine);
|
||||
if (this.config.comment.utterances) {
|
||||
const utterancesConfig = this.config.comment.utterances;
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://utteranc.es/client.js";
|
||||
script.type = "text/javascript";
|
||||
script.setAttribute("repo", utterancesConfig.repo);
|
||||
script.setAttribute("issue-term", utterancesConfig.issueTerm);
|
||||
if (utterancesConfig.label)
|
||||
script.setAttribute("label", utterancesConfig.label);
|
||||
script.setAttribute(
|
||||
"theme",
|
||||
this.isDark ? utterancesConfig.darkTheme : utterancesConfig.lightTheme
|
||||
);
|
||||
script.crossOrigin = "anonymous";
|
||||
script.async = true;
|
||||
document.getElementById("utterances").appendChild(script);
|
||||
this._utterancesOnSwitchTheme =
|
||||
this._utterancesOnSwitchTheme ||
|
||||
(() => {
|
||||
const message = {
|
||||
type: "set-theme",
|
||||
theme: this.isDark
|
||||
? utterancesConfig.darkTheme
|
||||
: utterancesConfig.lightTheme,
|
||||
};
|
||||
const iframe = document.querySelector(".utterances-frame");
|
||||
iframe.contentWindow.postMessage(message, "https://utteranc.es");
|
||||
});
|
||||
this.switchThemeEventSet.add(this._utterancesOnSwitchTheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initSmoothScroll() {
|
||||
if (SmoothScroll)
|
||||
new SmoothScroll('[href^="#"]', {
|
||||
speed: 300,
|
||||
speedAsDuration: true,
|
||||
header: "#header-desktop",
|
||||
});
|
||||
}
|
||||
|
||||
initCookieconsent() {
|
||||
if (this.config.cookieconsent)
|
||||
cookieconsent.initialise(this.config.cookieconsent);
|
||||
}
|
||||
|
||||
onScroll() {
|
||||
const $headers = [];
|
||||
if (document.body.getAttribute("header-desktop") === "auto")
|
||||
$headers.push(document.getElementById("header-desktop"));
|
||||
if (document.body.getAttribute("header-mobile") === "auto")
|
||||
$headers.push(document.getElementById("header-mobile"));
|
||||
if (document.getElementById("comments")) {
|
||||
const $viewComments = document.getElementById("view-comments");
|
||||
$viewComments.href = `#comments`;
|
||||
$viewComments.style.display = "block";
|
||||
}
|
||||
const $fixedButtons = document.getElementById("fixed-buttons");
|
||||
const ACCURACY = 20,
|
||||
MINIMUM = 100;
|
||||
window.addEventListener(
|
||||
"scroll",
|
||||
() => {
|
||||
this.newScrollTop = this.util.getScrollTop();
|
||||
const scroll = this.newScrollTop - this.oldScrollTop;
|
||||
const isMobile = this.util.isMobile();
|
||||
this.util.forEach($headers, ($header) => {
|
||||
if (scroll > ACCURACY) {
|
||||
$header.classList.remove("fadeInDown");
|
||||
this.util.animateCSS($header, ["fadeOutUp", "faster"], true);
|
||||
} else if (scroll < -ACCURACY) {
|
||||
$header.classList.remove("fadeOutUp");
|
||||
this.util.animateCSS($header, ["fadeInDown", "faster"], true);
|
||||
}
|
||||
});
|
||||
if (this.newScrollTop > MINIMUM) {
|
||||
if (isMobile && scroll > ACCURACY) {
|
||||
$fixedButtons.classList.remove("fadeIn");
|
||||
this.util.animateCSS($fixedButtons, ["fadeOut", "faster"], true);
|
||||
} else if (!isMobile || scroll < -ACCURACY) {
|
||||
$fixedButtons.style.display = "block";
|
||||
$fixedButtons.classList.remove("fadeOut");
|
||||
this.util.animateCSS($fixedButtons, ["fadeIn", "faster"], true);
|
||||
}
|
||||
} else {
|
||||
if (!isMobile) {
|
||||
$fixedButtons.classList.remove("fadeIn");
|
||||
this.util.animateCSS($fixedButtons, ["fadeOut", "faster"], true);
|
||||
}
|
||||
$fixedButtons.style.display = "none";
|
||||
}
|
||||
for (let event of this.scrollEventSet) event();
|
||||
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.initToc();
|
||||
this.initMermaid();
|
||||
this.initSearch();
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
onClickMask() {
|
||||
document.getElementById("mask").addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
for (let event of this.clickMaskEventSet) event();
|
||||
document.body.classList.remove("blur");
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
try {
|
||||
this.initSVGIcon();
|
||||
this.initTwemoji();
|
||||
this.initMenuMobile();
|
||||
this.initSwitchTheme();
|
||||
this.initSearch();
|
||||
this.initDetails();
|
||||
this.initLightGallery();
|
||||
this.initHighlight();
|
||||
this.initTable();
|
||||
this.initHeaderLink();
|
||||
this.initSmoothScroll();
|
||||
this.initMath();
|
||||
this.initMermaid();
|
||||
this.initEcharts();
|
||||
this.initTypeit();
|
||||
this.initMapbox();
|
||||
this.initCookieconsent();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
window.setTimeout(() => {
|
||||
this.initToc();
|
||||
this.initComment();
|
||||
|
||||
this.onScroll();
|
||||
this.onResize();
|
||||
this.onClickMask();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
const themeInit = () => {
|
||||
const theme = new Theme();
|
||||
theme.init();
|
||||
};
|
||||
|
||||
if (document.readyState !== "loading") {
|
||||
themeInit();
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", themeInit, false);
|
||||
}
|
12
themes/CodeIT/src/lib/lunr.segmentit.js
Normal file
12
themes/CodeIT/src/lib/lunr.segmentit.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { Segment, useDefault } from "segmentit";
|
||||
|
||||
const segmentit = useDefault(new Segment());
|
||||
lunr.segmentit = segmentit;
|
||||
lunr.queryHandler = (query) => {
|
||||
if (/^[\u4e00-\u9fa5]+$/.test(query))
|
||||
query = lunr.segmentit
|
||||
.doSegment(query)
|
||||
.map((seg) => "+" + seg.w)
|
||||
.join(" ");
|
||||
return query;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue