
footer has involved a lot of config keys since our fork, sometimes the toc scroll behavior is broken due to cached footer is inconsistant with the current file
245 lines
8.6 KiB
HTML
245 lines
8.6 KiB
HTML
{{- if not (.Param "hideFooter") }}
|
|
<footer class="footer">
|
|
{{- if site.Copyright }}
|
|
<span>{{ site.Copyright | markdownify }}</span>
|
|
{{- else }}
|
|
<span>© {{ now.Year }} <a href="{{ "" | absLangURL }}">{{ site.Title }}</a></span>
|
|
{{- end -}}
|
|
<span style="display: inline-block; margin-left: 1em;">
|
|
<a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA</a>
|
|
</span>
|
|
<span style="display: inline-block; margin-left: 1em;">
|
|
Powered by
|
|
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
|
|
<a href="https://github.com/reorx/hugo-PaperModX" rel="noopener" target="_blank">PaperModX</a>
|
|
</span>
|
|
</footer>
|
|
{{- end }}
|
|
|
|
{{- if (not site.Params.disableScrollToTop) }}
|
|
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
|
|
<path d="M12 6H0l6-6z" />
|
|
</svg>
|
|
</a>
|
|
{{- end }}
|
|
|
|
{{- partial "extend_footer.html" . }}
|
|
|
|
<script>
|
|
(function() {
|
|
let menu = document.getElementById('menu')
|
|
if (menu) {
|
|
menu.scrollLeft = localStorage.getItem("menu-scroll-position");
|
|
menu.onscroll = function () {
|
|
localStorage.setItem("menu-scroll-position", menu.scrollLeft);
|
|
}
|
|
}
|
|
|
|
const disableSmoothScroll = '{{- if (.Param "DisableSmoothScroll") -}}1{{- end -}}' == '1';
|
|
const enableInstantClick = '{{- if (.Param "EnableInstantClick") -}}1{{- end -}}' == '1';
|
|
// instant click and smooth scroll are mutually exclusive
|
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches || disableSmoothScroll || enableInstantClick) {
|
|
return;
|
|
}
|
|
// only run this code if smooth scroll should be enabled
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener("click", function (e) {
|
|
e.preventDefault();
|
|
var id = this.getAttribute("href").substr(1);
|
|
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
|
|
behavior: "smooth"
|
|
});
|
|
if (id === "top") {
|
|
history.replaceState(null, null, " ");
|
|
} else {
|
|
history.pushState(null, null, `#${id}`);
|
|
}
|
|
});
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
{{- if (not site.Params.disableScrollToTop) }}
|
|
<script>
|
|
var mybutton = document.getElementById("top-link");
|
|
window.onscroll = function () {
|
|
if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
|
|
mybutton.style.visibility = "visible";
|
|
mybutton.style.opacity = "1";
|
|
} else {
|
|
mybutton.style.visibility = "hidden";
|
|
mybutton.style.opacity = "0";
|
|
}
|
|
};
|
|
|
|
</script>
|
|
{{- end }}
|
|
|
|
{{- if (not site.Params.disableThemeToggle) }}
|
|
<script>
|
|
document.getElementById("theme-toggle").addEventListener("click", () => {
|
|
if (document.body.className.includes("dark")) {
|
|
document.body.classList.remove('dark');
|
|
localStorage.setItem("pref-theme", 'light');
|
|
} else {
|
|
document.body.classList.add('dark');
|
|
localStorage.setItem("pref-theme", 'dark');
|
|
}
|
|
})
|
|
|
|
</script>
|
|
{{- end }}
|
|
|
|
{{- if (and (eq .Kind "page") (ne .Layout "archives") (ne .Layout "search") (.Param "ShowCodeCopyButtons")) }}
|
|
<script>
|
|
document.querySelectorAll('pre > code').forEach((codeblock) => {
|
|
const container = codeblock.parentNode.parentNode;
|
|
|
|
const copybutton = document.createElement('button');
|
|
copybutton.classList.add('copy-code');
|
|
copybutton.innerText = '{{- i18n "code_copy" | default "copy" }}';
|
|
|
|
function copyingDone() {
|
|
copybutton.innerText = '{{- i18n "code_copied" | default "copied!" }}';
|
|
setTimeout(() => {
|
|
copybutton.innerText = '{{- i18n "code_copy" | default "copy" }}';
|
|
}, 2000);
|
|
}
|
|
|
|
copybutton.addEventListener('click', (cb) => {
|
|
if ('clipboard' in navigator) {
|
|
navigator.clipboard.writeText(codeblock.textContent);
|
|
copyingDone();
|
|
return;
|
|
}
|
|
|
|
const range = document.createRange();
|
|
range.selectNodeContents(codeblock);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
try {
|
|
document.execCommand('copy');
|
|
copyingDone();
|
|
} catch (e) { };
|
|
selection.removeRange(range);
|
|
});
|
|
|
|
if (container.classList.contains("highlight")) {
|
|
container.appendChild(copybutton);
|
|
} else if (container.parentNode.firstChild == container) {
|
|
// td containing LineNos
|
|
} else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
|
|
// table containing LineNos and code
|
|
codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
|
|
} else {
|
|
// code blocks not having highlight as parent class
|
|
codeblock.parentNode.appendChild(copybutton);
|
|
}
|
|
});
|
|
</script>
|
|
{{- end }}
|
|
|
|
|
|
<script>
|
|
// NOTE use closure instead of DOMContentLoaded because of InstantClick,
|
|
// every page loaded by InstantClick should run the <script> tags again.
|
|
(function() {
|
|
const enableTocScroll = '{{- if (and (eq .Kind "page") (.Content) (.Param "ShowToc") (.Param "TocSide")) }}1{{ end }}' == '1'
|
|
let scrollListeners = window.scrollListeners
|
|
if (scrollListeners === undefined) {
|
|
scrollListeners = []
|
|
window.scrollListeners = scrollListeners
|
|
}
|
|
|
|
if (!enableTocScroll || !document.querySelector('.toc')) {
|
|
// console.log('disable toc scroll', scrollListeners)
|
|
for (const listener of scrollListeners) {
|
|
window.removeEventListener('scroll', listener)
|
|
}
|
|
window.scrollListeners = []
|
|
return
|
|
}
|
|
// console.log('enable toc scroll')
|
|
|
|
const headings = document.querySelectorAll('h1[id],h2[id],h3[id],h4[id],h5[id]');
|
|
const activeClass = 'active';
|
|
|
|
// Make the first header active
|
|
let activeHeading = headings[0];
|
|
getLinkByHeading(activeHeading).classList.add(activeClass);
|
|
|
|
const onScroll = () => {
|
|
const passedHeadings = [];
|
|
for (const h of headings) {
|
|
// 5 px as a buffer
|
|
if (getOffsetTop(h) < 5) {
|
|
passedHeadings.push(h)
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (passedHeadings.length > 0) {
|
|
newActiveHeading = passedHeadings[passedHeadings.length - 1];
|
|
} else {
|
|
newActiveHeading = headings[0];
|
|
}
|
|
if (activeHeading != newActiveHeading) {
|
|
getLinkByHeading(activeHeading).classList.remove(activeClass);
|
|
activeHeading = newActiveHeading;
|
|
getLinkByHeading(activeHeading).classList.add(activeClass);
|
|
}
|
|
}
|
|
|
|
let timer = null;
|
|
const scrollListener = () => {
|
|
if (timer !== null) {
|
|
clearTimeout(timer)
|
|
}
|
|
timer = setTimeout(onScroll, 50)
|
|
}
|
|
window.addEventListener('scroll', scrollListener, false);
|
|
scrollListeners.push(scrollListener)
|
|
|
|
function getLinkByHeading(heading) {
|
|
const id = encodeURI(heading.getAttribute('id')).toLowerCase();
|
|
return document.querySelector(`.toc ul li a[href="#${id}"]`);
|
|
}
|
|
|
|
function getOffsetTop(heading) {
|
|
if (!heading.getClientRects().length) {
|
|
return 0;
|
|
}
|
|
let rect = heading.getBoundingClientRect();
|
|
return rect.top
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
{{- /* Search */}}
|
|
{{- if (eq .Layout `search`) -}}
|
|
<link crossorigin="anonymous" rel="preload" as="fetch" href="../index.json">
|
|
{{- $fastsearch := resources.Get "js/fastsearch.js" | js.Build (dict "params" (dict "fuseOpts" site.Params.fuseOpts)) }}
|
|
{{- $fusejs := resources.Get "js/fuse.basic.min.js" }}
|
|
{{- if not site.Params.assets.disableFingerprinting }}
|
|
{{- $search := (slice $fusejs $fastsearch ) | resources.Concat "assets/js/search.js" | minify | fingerprint }}
|
|
<script defer crossorigin="anonymous" src="{{ $search.RelPermalink }}" integrity="{{ $search.Data.Integrity }}"></script>
|
|
{{- else }}
|
|
{{- $search := (slice $fusejs $fastsearch ) | resources.Concat "assets/js/search.js" | minify }}
|
|
<script defer crossorigin="anonymous" src="{{ $search.RelPermalink }}"></script>
|
|
{{- end }}
|
|
|
|
{{- else }}
|
|
|
|
{{- /* InstantClick.js */}}
|
|
{{- if (.Param "EnableInstantClick") }}
|
|
{{- $instantclick := slice (resources.Get "js/instantclick.js") | resources.Concat "assets/js/instantclick.js" | minify }}
|
|
<script src="{{ $instantclick.RelPermalink }}" data-no-instant
|
|
{{- if site.Params.assets.disableFingerprinting }}integrity="{{ $instantclick.Data.Integrity }}"{{- end }}
|
|
></script>
|
|
<script data-no-instant>InstantClick.init();</script>
|
|
{{- end }}
|
|
|
|
{{- end -}}
|