From 900bb5e845c3ad44defbd427cae3d44a4a43321f Mon Sep 17 00:00:00 2001 From: ivarlovlie Date: Sat, 25 Feb 2023 13:15:44 +0100 Subject: feat: Initial commit --- code/frontpage/assets/js/alert-init.js | 5 + code/frontpage/assets/js/alert.js | 20 ++++ code/frontpage/assets/js/app.js | 0 code/frontpage/assets/js/bootstrap.js | 2 + code/frontpage/assets/js/clipboard.js | 37 ++++++ code/frontpage/assets/js/darkmode-init.js | 21 ++++ code/frontpage/assets/js/darkmode.js | 38 +++++++ code/frontpage/assets/js/highlight.js | 26 +++++ code/frontpage/assets/js/index.js | 179 ++++++++++++++++++++++++++++++ code/frontpage/assets/js/instant.page.js | 1 + code/frontpage/assets/js/katex.js | 10 ++ code/frontpage/assets/js/lazysizes.js | 1 + code/frontpage/assets/js/mermaid.js | 11 ++ code/frontpage/assets/js/scroll-lock.js | 14 +++ code/frontpage/assets/js/to-top.js | 20 ++++ code/frontpage/assets/js/vendor/.gitkeep | 0 16 files changed, 385 insertions(+) create mode 100644 code/frontpage/assets/js/alert-init.js create mode 100644 code/frontpage/assets/js/alert.js create mode 100644 code/frontpage/assets/js/app.js create mode 100644 code/frontpage/assets/js/bootstrap.js create mode 100644 code/frontpage/assets/js/clipboard.js create mode 100644 code/frontpage/assets/js/darkmode-init.js create mode 100644 code/frontpage/assets/js/darkmode.js create mode 100644 code/frontpage/assets/js/highlight.js create mode 100644 code/frontpage/assets/js/index.js create mode 100644 code/frontpage/assets/js/instant.page.js create mode 100644 code/frontpage/assets/js/katex.js create mode 100644 code/frontpage/assets/js/lazysizes.js create mode 100644 code/frontpage/assets/js/mermaid.js create mode 100644 code/frontpage/assets/js/scroll-lock.js create mode 100644 code/frontpage/assets/js/to-top.js create mode 100644 code/frontpage/assets/js/vendor/.gitkeep (limited to 'code/frontpage/assets/js') diff --git a/code/frontpage/assets/js/alert-init.js b/code/frontpage/assets/js/alert-init.js new file mode 100644 index 0000000..af3ac32 --- /dev/null +++ b/code/frontpage/assets/js/alert-init.js @@ -0,0 +1,5 @@ +Object.keys(localStorage).forEach(function(key) { + if (/^global-alert-/.test(key)) { + document.documentElement.setAttribute('data-global-alert', 'closed'); + } +}); \ No newline at end of file diff --git a/code/frontpage/assets/js/alert.js b/code/frontpage/assets/js/alert.js new file mode 100644 index 0000000..1956103 --- /dev/null +++ b/code/frontpage/assets/js/alert.js @@ -0,0 +1,20 @@ +var announcement = document.getElementById('announcement'); + +if (announcement !== null) { + + var id = announcement.dataset.id; + + Object.keys(localStorage).forEach(function(key) { + if (/^global-alert-/.test(key)) { + if (key !== id ) { + localStorage.removeItem(key); + document.documentElement.removeAttribute('data-global-alert'); + } + } + }); + + announcement.addEventListener('closed.bs.alert', () => { + localStorage.setItem(id, 'closed'); + }); + +} \ No newline at end of file diff --git a/code/frontpage/assets/js/app.js b/code/frontpage/assets/js/app.js new file mode 100644 index 0000000..e69de29 diff --git a/code/frontpage/assets/js/bootstrap.js b/code/frontpage/assets/js/bootstrap.js new file mode 100644 index 0000000..8d6da8d --- /dev/null +++ b/code/frontpage/assets/js/bootstrap.js @@ -0,0 +1,2 @@ +import 'bootstrap/dist/js/bootstrap.bundle.min.js' +// import 'bootstrap/dist/js/bootstrap.min.js' diff --git a/code/frontpage/assets/js/clipboard.js b/code/frontpage/assets/js/clipboard.js new file mode 100644 index 0000000..55eec7b --- /dev/null +++ b/code/frontpage/assets/js/clipboard.js @@ -0,0 +1,37 @@ +import Clipboard from 'clipboard'; + +var pre = document.getElementsByTagName('pre'); + +for (var i = 0; i < pre.length; ++ i) +{ + var element = pre[i]; + var mermaid = element.getElementsByClassName('language-mermaid')[0]; + + if (mermaid == null) { + element.insertAdjacentHTML('afterbegin', ''); + } +} + +var clipboard = new Clipboard('.btn-copy', { + + target: function(trigger) { + return trigger.nextElementSibling; + }, + +}); + +clipboard.on('success', function(e) { + + /* + console.info('Action:', e.action); + console.info('Text:', e.text); + console.info('Trigger:', e.trigger); + */ + + e.clearSelection(); +}); + +clipboard.on('error', function(e) { + console.error('Action:', e.action); + console.error('Trigger:', e.trigger); +}); diff --git a/code/frontpage/assets/js/darkmode-init.js b/code/frontpage/assets/js/darkmode-init.js new file mode 100644 index 0000000..0f3508d --- /dev/null +++ b/code/frontpage/assets/js/darkmode-init.js @@ -0,0 +1,21 @@ +const globalDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; +const localMode = localStorage.getItem('theme'); + +if (globalDark && (localMode === null)) { + + localStorage.setItem('theme', 'dark'); + document.documentElement.setAttribute('data-dark-mode', ''); + +} + +if (globalDark && (localMode === 'dark')) { + + document.documentElement.setAttribute('data-dark-mode', ''); + +} + +if (localMode === 'dark') { + + document.documentElement.setAttribute('data-dark-mode', ''); + +} diff --git a/code/frontpage/assets/js/darkmode.js b/code/frontpage/assets/js/darkmode.js new file mode 100644 index 0000000..e81db47 --- /dev/null +++ b/code/frontpage/assets/js/darkmode.js @@ -0,0 +1,38 @@ +const mode = document.getElementById('mode'); + +if (mode !== null) { + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + + if (event.matches) { + + localStorage.setItem('theme', 'dark'); + document.documentElement.setAttribute('data-dark-mode', ''); + + } else { + + localStorage.setItem('theme', 'light'); + document.documentElement.removeAttribute('data-dark-mode'); + + } + + }) + + mode.addEventListener('click', () => { + + document.documentElement.toggleAttribute('data-dark-mode'); + localStorage.setItem('theme', document.documentElement.hasAttribute('data-dark-mode') ? 'dark' : 'light'); + + }); + + if (localStorage.getItem('theme') === 'dark') { + + document.documentElement.setAttribute('data-dark-mode', ''); + + } else { + + document.documentElement.removeAttribute('data-dark-mode'); + + } + +} diff --git a/code/frontpage/assets/js/highlight.js b/code/frontpage/assets/js/highlight.js new file mode 100644 index 0000000..4ad6017 --- /dev/null +++ b/code/frontpage/assets/js/highlight.js @@ -0,0 +1,26 @@ +import hljs from 'highlight.js/lib/core'; + +import javascript from 'highlight.js/lib/languages/javascript'; +import json from 'highlight.js/lib/languages/json'; +import bash from 'highlight.js/lib/languages/bash'; +import xml from 'highlight.js/lib/languages/xml'; +import ini from 'highlight.js/lib/languages/ini'; +import yaml from 'highlight.js/lib/languages/yaml'; +import markdown from 'highlight.js/lib/languages/markdown'; +import python from 'highlight.js/lib/languages/python'; + +hljs.registerLanguage('javascript', javascript); +hljs.registerLanguage('json', json); +hljs.registerLanguage('bash', bash); +hljs.registerLanguage('html', xml); +hljs.registerLanguage('ini', ini); +hljs.registerLanguage('toml', ini); +hljs.registerLanguage('yaml', yaml); +hljs.registerLanguage('md', markdown); +hljs.registerLanguage('python', python); + +document.addEventListener('DOMContentLoaded', () => { + document.querySelectorAll('pre code:not(.language-mermaid)').forEach((block) => { + hljs.highlightElement(block); + }); +}); diff --git a/code/frontpage/assets/js/index.js b/code/frontpage/assets/js/index.js new file mode 100644 index 0000000..15e09bb --- /dev/null +++ b/code/frontpage/assets/js/index.js @@ -0,0 +1,179 @@ +var suggestions = document.getElementById('suggestions'); +var search = document.getElementById('search'); + +if (search !== null) { + document.addEventListener('keydown', inputFocus); +} + +function inputFocus(e) { + if (e.ctrlKey && e.key === '/' ) { + e.preventDefault(); + search.focus(); + } + if (e.key === 'Escape' ) { + search.blur(); + suggestions.classList.add('d-none'); + } +} + +document.addEventListener('click', function(event) { + + var isClickInsideElement = suggestions.contains(event.target); + + if (!isClickInsideElement) { + suggestions.classList.add('d-none'); + } + +}); + +/* +Source: + - https://dev.to/shubhamprakash/trap-focus-using-javascript-6a3 +*/ + +document.addEventListener('keydown',suggestionFocus); + +function suggestionFocus(e) { + const suggestionsHidden = suggestions.classList.contains('d-none'); + if (suggestionsHidden) return; + + const focusableSuggestions= [...suggestions.querySelectorAll('a')]; + if (focusableSuggestions.length === 0) return; + + const index = focusableSuggestions.indexOf(document.activeElement); + + if (e.key === "ArrowUp") { + e.preventDefault(); + const nextIndex = index > 0 ? index - 1 : 0; + focusableSuggestions[nextIndex].focus(); + } + else if (e.key === "ArrowDown") { + e.preventDefault(); + const nextIndex= index + 1 < focusableSuggestions.length ? index + 1 : index; + focusableSuggestions[nextIndex].focus(); + } + +} + +/* +Source: + - https://github.com/nextapps-de/flexsearch#index-documents-field-search + - https://raw.githack.com/nextapps-de/flexsearch/master/demo/autocomplete.html +*/ + +(function(){ + + var index = new FlexSearch.Document({ + tokenize: "forward", + cache: 100, + document: { + id: 'id', + store: [ + "href", "title", "description" + ], + index: ["title", "description", "content"] + } + }); + + + // Not yet supported: https://github.com/nextapps-de/flexsearch#complex-documents + + /* + var docs = [ + {{ range $index, $page := (where .Site.Pages "Section" "docs") -}} + { + id: {{ $index }}, + href: "{{ .Permalink }}", + title: {{ .Title | jsonify }}, + description: {{ .Params.description | jsonify }}, + content: {{ .Content | jsonify }} + }, + {{ end -}} + ]; + */ + + // https://discourse.gohugo.io/t/range-length-or-last-element/3803/2 + + {{ $list := slice }} + {{- if and (isset .Site.Params.options "searchsectionsindex") (not (eq (len .Site.Params.options.searchSectionsIndex) 0)) }} + {{- if eq .Site.Params.options.searchSectionsIndex "ALL" }} + {{- $list = .Site.Pages }} + {{- else }} + {{- $list = (where .Site.Pages "Type" "in" .Site.Params.options.searchSectionsIndex) }} + {{- if (in .Site.Params.options.searchSectionsIndex "HomePage") }} + {{ $list = $list | append .Site.Home }} + {{- end }} + {{- end }} + {{- else }} + {{- $list = (where .Site.Pages "Section" "docs") }} + {{- end }} + + {{ $len := (len $list) -}} + + {{ range $index, $element := $list -}} + index.add( + { + id: {{ $index }}, + href: "{{ .RelPermalink }}", + title: {{ .Title | jsonify }}, + {{ with .Description -}} + description: {{ . | jsonify }}, + {{ else -}} + description: {{ .Summary | plainify | jsonify }}, + {{ end -}} + content: {{ .Plain | jsonify }} + } + ); + {{ end -}} + + search.addEventListener('input', show_results, true); + + function show_results(){ + const maxResult = 5; + var searchQuery = this.value; + var results = index.search(searchQuery, {limit: maxResult, enrich: true}); + + // flatten results since index.search() returns results for each indexed field + const flatResults = new Map(); // keyed by href to dedupe results + for (const result of results.flatMap(r => r.result)) { + if (flatResults.has(result.doc.href)) continue; + flatResults.set(result.doc.href, result.doc); + } + + suggestions.innerHTML = ""; + suggestions.classList.remove('d-none'); + + // inform user that no results were found + if (flatResults.size === 0 && searchQuery) { + const noResultsMessage = document.createElement('div') + noResultsMessage.innerHTML = `No results for "${searchQuery}"` + noResultsMessage.classList.add("suggestion__no-results"); + suggestions.appendChild(noResultsMessage); + return; + } + + // construct a list of suggestions + for(const [href, doc] of flatResults) { + const entry = document.createElement('div'); + suggestions.appendChild(entry); + + const a = document.createElement('a'); + a.href = href; + entry.appendChild(a); + + const title = document.createElement('span'); + title.textContent = doc.title; + title.classList.add("suggestion__title"); + a.appendChild(title); + + const description = document.createElement('span'); + description.textContent = doc.description; + description.classList.add("suggestion__description"); + a.appendChild(description); + + suggestions.appendChild(entry); + + if(suggestions.childElementCount == maxResult) break; + } + } +}()); diff --git a/code/frontpage/assets/js/instant.page.js b/code/frontpage/assets/js/instant.page.js new file mode 100644 index 0000000..b394bcc --- /dev/null +++ b/code/frontpage/assets/js/instant.page.js @@ -0,0 +1 @@ +import 'instant.page'; diff --git a/code/frontpage/assets/js/katex.js b/code/frontpage/assets/js/katex.js new file mode 100644 index 0000000..e0543ea --- /dev/null +++ b/code/frontpage/assets/js/katex.js @@ -0,0 +1,10 @@ +document.addEventListener('DOMContentLoaded', function() { + renderMathInElement(document.body, { + delimiters: [ + {left: '$$', right: '$$', display: true}, + {left: '$', right: '$', display: false}, + {left: '\\(', right: '\\)', display: false}, + {left: '\\[', right: '\\]', display: true}, + ], + }); +}); diff --git a/code/frontpage/assets/js/lazysizes.js b/code/frontpage/assets/js/lazysizes.js new file mode 100644 index 0000000..c12ed58 --- /dev/null +++ b/code/frontpage/assets/js/lazysizes.js @@ -0,0 +1 @@ +import 'lazysizes'; diff --git a/code/frontpage/assets/js/mermaid.js b/code/frontpage/assets/js/mermaid.js new file mode 100644 index 0000000..98d67e1 --- /dev/null +++ b/code/frontpage/assets/js/mermaid.js @@ -0,0 +1,11 @@ +import mermaid from 'mermaid'; + +var config = { + theme: 'default', + fontFamily: '-apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";', +}; + +document.addEventListener('DOMContentLoaded', () => { + mermaid.initialize(config); + mermaid.init(undefined, '.language-mermaid'); +}); diff --git a/code/frontpage/assets/js/scroll-lock.js b/code/frontpage/assets/js/scroll-lock.js new file mode 100644 index 0000000..069b8c2 --- /dev/null +++ b/code/frontpage/assets/js/scroll-lock.js @@ -0,0 +1,14 @@ +// Adds scroll position lock for default docs sidebar + +if (document.querySelector('#sidebar-default') !== null) { + let sidebar = document.getElementById('sidebar-default'); + + let pos = sessionStorage.getItem('sidebar-scroll'); + if (pos !== null) { + sidebar.scrollTop = parseInt(pos, 10); + } + + window.addEventListener('beforeunload', () => { + sessionStorage.setItem('sidebar-scroll', sidebar.scrollTop); + }); +} diff --git a/code/frontpage/assets/js/to-top.js b/code/frontpage/assets/js/to-top.js new file mode 100644 index 0000000..3287f43 --- /dev/null +++ b/code/frontpage/assets/js/to-top.js @@ -0,0 +1,20 @@ +var topbutton = document.getElementById('toTop'); + +if (topbutton !== null) { + + topbutton.style.display = 'none'; + window.onscroll = function() { + scrollFunction() + }; + +} + +function scrollFunction() { + + if (document.body.scrollTop > 40 || document.documentElement.scrollTop > 40) { + topbutton.style.display = 'block'; + } else { + topbutton.style.display = 'none'; + } + +} diff --git a/code/frontpage/assets/js/vendor/.gitkeep b/code/frontpage/assets/js/vendor/.gitkeep new file mode 100644 index 0000000..e69de29 -- cgit v1.3