diff options
Diffstat (limited to 'themes/blowfish/assets/js')
-rw-r--r-- | themes/blowfish/assets/js/appearance.js | 101 | ||||
-rw-r--r-- | themes/blowfish/assets/js/chart.js | 13 | ||||
-rw-r--r-- | themes/blowfish/assets/js/code.js | 66 | ||||
-rw-r--r-- | themes/blowfish/assets/js/mermaid.js | 20 | ||||
-rw-r--r-- | themes/blowfish/assets/js/page.js | 106 | ||||
-rw-r--r-- | themes/blowfish/assets/js/process.js | 71 | ||||
-rw-r--r-- | themes/blowfish/assets/js/rtl.js | 3 | ||||
-rw-r--r-- | themes/blowfish/assets/js/search.js | 188 | ||||
-rw-r--r-- | themes/blowfish/assets/js/shortcodes/gallery.js | 22 | ||||
-rw-r--r-- | themes/blowfish/assets/js/zen-mode.js | 57 |
10 files changed, 647 insertions, 0 deletions
diff --git a/themes/blowfish/assets/js/appearance.js b/themes/blowfish/assets/js/appearance.js new file mode 100644 index 0000000..1da4b69 --- /dev/null +++ b/themes/blowfish/assets/js/appearance.js @@ -0,0 +1,101 @@ +const sitePreference = document.documentElement.getAttribute("data-default-appearance"); +const userPreference = localStorage.getItem("appearance"); + +if ((sitePreference === "dark" && userPreference === null) || userPreference === "dark") { + document.documentElement.classList.add("dark"); +} + +if (document.documentElement.getAttribute("data-auto-appearance") === "true") { + if ( + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches && + userPreference !== "light" + ) { + document.documentElement.classList.add("dark"); + } + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => { + if (event.matches) { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + }); +} + +window.addEventListener("DOMContentLoaded", (event) => { + const switcher = document.getElementById("appearance-switcher"); + const switcherMobile = document.getElementById("appearance-switcher-mobile"); + + updateMeta(); + this.updateLogo?.(getTargetAppearance()); + + if (switcher) { + switcher.addEventListener("click", () => { + document.documentElement.classList.toggle("dark"); + var targetAppearance = getTargetAppearance(); + localStorage.setItem( + "appearance", + targetAppearance + ); + updateMeta(); + this.updateLogo?.(targetAppearance); + }); + switcher.addEventListener("contextmenu", (event) => { + event.preventDefault(); + localStorage.removeItem("appearance"); + }); + } + if (switcherMobile) { + switcherMobile.addEventListener("click", () => { + document.documentElement.classList.toggle("dark"); + var targetAppearance = getTargetAppearance(); + localStorage.setItem( + "appearance", + targetAppearance + ); + updateMeta(); + this.updateLogo?.(targetAppearance); + }); + switcherMobile.addEventListener("contextmenu", (event) => { + event.preventDefault(); + localStorage.removeItem("appearance"); + }); + } +}); + + +var updateMeta = () => { + var elem, style; + elem = document.querySelector('body'); + style = getComputedStyle(elem); + document.querySelector('meta[name="theme-color"]').setAttribute('content', style.backgroundColor); +} + +{{ if and (.Site.Params.Logo) (.Site.Params.SecondaryLogo) }} +{{ $primaryLogo := resources.Get .Site.Params.Logo }} +{{ $secondaryLogo := resources.Get .Site.Params.SecondaryLogo }} +{{ if and ($primaryLogo) ($secondaryLogo) }} +var updateLogo = (targetAppearance) => { + var elems; + elems = document.querySelectorAll("img.logo") + targetLogoPath = + targetAppearance == "{{ .Site.Params.DefaultAppearance }}" ? + "{{ $primaryLogo.RelPermalink }}" : "{{ $secondaryLogo.RelPermalink }}" + for (const elem of elems) { + elem.setAttribute("src", targetLogoPath) + } +} +{{ end }} +{{- end }} + +var getTargetAppearance = () => { + return document.documentElement.classList.contains("dark") ? "dark" : "light" +} + +window.addEventListener("DOMContentLoaded", (event) => { + const scroller = document.getElementById("top-scroller"); + const footer = document.getElementById("site-footer"); + if(scroller && footer && scroller.getBoundingClientRect().top > footer.getBoundingClientRect().top) { + scroller.hidden = true; + } +}); diff --git a/themes/blowfish/assets/js/chart.js b/themes/blowfish/assets/js/chart.js new file mode 100644 index 0000000..ccf950d --- /dev/null +++ b/themes/blowfish/assets/js/chart.js @@ -0,0 +1,13 @@ +function css(name) { + return "rgb(" + getComputedStyle(document.documentElement).getPropertyValue(name) + ")"; +} + +Chart.defaults.font.size = 14; +Chart.defaults.backgroundColor = css("--color-primary-300"); +Chart.defaults.elements.point.borderColor = css("--color-primary-400"); +Chart.defaults.elements.bar.borderColor = css("--color-primary-500"); +Chart.defaults.elements.bar.borderWidth = 1; +Chart.defaults.elements.line.borderColor = css("--color-primary-400"); +Chart.defaults.elements.arc.backgroundColor = css("--color-primary-200"); +Chart.defaults.elements.arc.borderColor = css("--color-primary-500"); +Chart.defaults.elements.arc.borderWidth = 1; diff --git a/themes/blowfish/assets/js/code.js b/themes/blowfish/assets/js/code.js new file mode 100644 index 0000000..88ae18d --- /dev/null +++ b/themes/blowfish/assets/js/code.js @@ -0,0 +1,66 @@ +var scriptBundle = document.getElementById("script-bundle"); +var copyText = scriptBundle && scriptBundle.getAttribute("data-copy")? scriptBundle.getAttribute("data-copy") : "Copy"; +var copiedText = scriptBundle && scriptBundle.getAttribute("data-copied")? scriptBundle.getAttribute("data-copied") : "Copied"; + +function createCopyButton(highlightDiv) { + const button = document.createElement("button"); + button.className = "copy-button"; + button.type = "button"; + button.ariaLabel = copyText; + button.innerText = copyText; + button.addEventListener("click", () => copyCodeToClipboard(button, highlightDiv)); + addCopyButtonToDom(button, highlightDiv); +} + +async function copyCodeToClipboard(button, highlightDiv) { + const codeToCopy = highlightDiv.querySelector(":last-child").innerText; + try { + result = await navigator.permissions.query({ name: "clipboard-write" }); + if (result.state == "granted" || result.state == "prompt") { + await navigator.clipboard.writeText(codeToCopy); + } else { + copyCodeBlockExecCommand(codeToCopy, highlightDiv); + } + } catch (_) { + copyCodeBlockExecCommand(codeToCopy, highlightDiv); + } finally { + codeWasCopied(button); + } +} + +function copyCodeBlockExecCommand(codeToCopy, highlightDiv) { + const textArea = document.createElement("textArea"); + textArea.contentEditable = "true"; + textArea.readOnly = "false"; + textArea.className = "copy-textarea"; + textArea.value = codeToCopy; + highlightDiv.insertBefore(textArea, highlightDiv.firstChild); + const range = document.createRange(); + range.selectNodeContents(textArea); + const sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + textArea.setSelectionRange(0, 999999); + document.execCommand("copy"); + highlightDiv.removeChild(textArea); +} + +function codeWasCopied(button) { + button.blur(); + button.innerText = copiedText; + setTimeout(function () { + button.innerText = copyText; + }, 2000); +} + +function addCopyButtonToDom(button, highlightDiv) { + highlightDiv.insertBefore(button, highlightDiv.firstChild); + const wrapper = document.createElement("div"); + wrapper.className = "highlight-wrapper"; + highlightDiv.parentNode.insertBefore(wrapper, highlightDiv); + wrapper.appendChild(highlightDiv); +} + +window.addEventListener("DOMContentLoaded", (event) => { + document.querySelectorAll(".highlight").forEach((highlightDiv) => createCopyButton(highlightDiv)); +}); diff --git a/themes/blowfish/assets/js/mermaid.js b/themes/blowfish/assets/js/mermaid.js new file mode 100644 index 0000000..cd41e5a --- /dev/null +++ b/themes/blowfish/assets/js/mermaid.js @@ -0,0 +1,20 @@ +function css(name) { + return "rgb(" + getComputedStyle(document.documentElement).getPropertyValue(name) + ")"; +} + +mermaid.initialize({ + theme: "base", + themeVariables: { + background: css("--color-neutral"), + primaryColor: css("--color-primary-200"), + secondaryColor: css("--color-secondary-200"), + tertiaryColor: css("--color-neutral-100"), + primaryBorderColor: css("--color-primary-400"), + secondaryBorderColor: css("--color-secondary-400"), + tertiaryBorderColor: css("--color-neutral-400"), + lineColor: css("--color-neutral-600"), + fontFamily: + "ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif", + fontSize: "16px", + }, +});
\ No newline at end of file diff --git a/themes/blowfish/assets/js/page.js b/themes/blowfish/assets/js/page.js new file mode 100644 index 0000000..e3cb979 --- /dev/null +++ b/themes/blowfish/assets/js/page.js @@ -0,0 +1,106 @@ +var liked_page = false +var id = oid ? oid.replaceAll("/", "-") : oid +var id_likes = oid_likes ? oid_likes.replaceAll("/", "-") : oid_likes + +if (typeof auth !== 'undefined') { + + var viewed = localStorage.getItem(id); + + if (!viewed) { + auth.signInAnonymously() + .then(() => { + var docRef = db.collection('views').doc(id) + localStorage.setItem(id, true); + docRef.get().then((doc) => { + if (doc.exists) { + db.collection('views').doc(id).update({ + views: firebase.firestore.FieldValue.increment(1) + }); + } else { + db.collection('views').doc(id).set({ views: 1 }) + } + }).catch((error) => { + console.log("Error getting document:", error); + }); + }) + .catch((error) => { + var errorCode = error.code; + var errorMessage = error.message; + console.error(errorCode, errorMessage) + }); + } + + var liked = localStorage.getItem(id_likes); + + if (liked) { + liked_page = true + document.querySelectorAll("span[id='button_likes_heart']")[0].style.display = "" + document.querySelectorAll("span[id='button_likes_emtpty_heart']")[0].style.display = "none" + document.querySelectorAll("span[id='button_likes_text']")[0].innerText = "" + } + +} + +function like_article(id_likes) { + auth.signInAnonymously() + .then(() => { + var docRef = db.collection('likes').doc(id_likes) + docRef.get().then((doc) => { + liked_page = true + localStorage.setItem(id_likes, true); + document.querySelectorAll("span[id='button_likes_heart']")[0].style.display = "" + document.querySelectorAll("span[id='button_likes_emtpty_heart']")[0].style.display = "none" + document.querySelectorAll("span[id='button_likes_text']")[0].innerText = "" + if (doc.exists) { + db.collection('likes').doc(id_likes).update({ + likes: firebase.firestore.FieldValue.increment(1) + }); + } else { + db.collection('likes').doc(id_likes).set({ likes: 1 }) + } + }).catch((error) => { + console.log("Error getting document:", error); + }); + }) + .catch((error) => { + var errorCode = error.code; + var errorMessage = error.message; + console.error(errorCode, errorMessage) + }); +} + +function remove_like_article(id_likes) { + auth.signInAnonymously() + .then(() => { + var docRef = db.collection('likes').doc(id_likes) + docRef.get().then((doc) => { + liked_page = false + localStorage.removeItem(id_likes); + document.querySelectorAll("span[id='button_likes_heart']")[0].style.display = "none" + document.querySelectorAll("span[id='button_likes_emtpty_heart']")[0].style.display = "" + document.querySelectorAll("span[id='button_likes_text']")[0].innerText = "\xa0Like" + if (doc.exists) { + db.collection('likes').doc(id_likes).update({ + likes: firebase.firestore.FieldValue.increment(-1) + }); + } else { + db.collection('likes').doc(id_likes).set({ likes: 0 }) + } + }).catch((error) => { + console.log("Error getting document:", error); + }); + }) + .catch((error) => { + var errorCode = error.code; + var errorMessage = error.message; + console.error(errorCode, errorMessage) + }); +} + +function process_article() { + if (!liked_page) { + like_article(id_likes) + } else { + remove_like_article(id_likes) + } +}
\ No newline at end of file diff --git a/themes/blowfish/assets/js/process.js b/themes/blowfish/assets/js/process.js new file mode 100644 index 0000000..a91afa7 --- /dev/null +++ b/themes/blowfish/assets/js/process.js @@ -0,0 +1,71 @@ +if (typeof auth !== 'undefined') { + var viewsCollection = db.collection('views'); + var likesCollection = db.collection('likes'); + + function numberWithCommas(x) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + } + + function toggleLoaders(node){ + var classesString = node.className; + if(classesString == "") return + var classes = classesString.split(" "); + for(var i in classes){ + node.classList.toggle(classes[i]) + } + } + + var update_views = function (node, id) { + viewsCollection.doc(id).onSnapshot(doc => { + var data = doc.data(); + if (data) { + node.innerText = numberWithCommas(data.views) + } else { + node.innerText = 0 + } + toggleLoaders(node) + }) + } + + var update_likes = function (node, id) { + likesCollection.doc(id).onSnapshot(doc => { + var data = doc.data(); + if (data) { + node.innerText = numberWithCommas(data.likes) + } else { + node.innerText = 0 + } + toggleLoaders(node) + + }) + } + + + auth.signInAnonymously() + .then(() => { + var views_nodes = document.querySelectorAll("span[id^='views_']") + + for (var i in views_nodes) { + var node = views_nodes[i] + var id = node.id ? node.id.replaceAll("/", "-") : node.id + if (id) { + update_views(node, id) + } + } + + var likes_nodes = document.querySelectorAll("span[id^='likes_']") + + for (var i in likes_nodes) { + var node = likes_nodes[i] + var id = node.id ? node.id.replaceAll("/", "-") : node.id + if (id) { + update_likes(node, id) + } + } + }) + .catch((error) => { + var errorCode = error.code; + var errorMessage = error.message; + console.error(errorCode, errorMessage) + }); +}
\ No newline at end of file diff --git a/themes/blowfish/assets/js/rtl.js b/themes/blowfish/assets/js/rtl.js new file mode 100644 index 0000000..26852d4 --- /dev/null +++ b/themes/blowfish/assets/js/rtl.js @@ -0,0 +1,3 @@ +window.addEventListener("DOMContentLoaded", (event) => { + document.querySelectorAll("pre, .highlight-wrapper").forEach((tag) => (tag.dir = "auto")); +}); diff --git a/themes/blowfish/assets/js/search.js b/themes/blowfish/assets/js/search.js new file mode 100644 index 0000000..d95dba3 --- /dev/null +++ b/themes/blowfish/assets/js/search.js @@ -0,0 +1,188 @@ +var fuse; +var showButton = document.getElementById("search-button"); +var showButtonMobile = document.getElementById("search-button-mobile"); +var hideButton = document.getElementById("close-search-button"); +var wrapper = document.getElementById("search-wrapper"); +var modal = document.getElementById("search-modal"); +var input = document.getElementById("search-query"); +var output = document.getElementById("search-results"); +var first = output.firstChild; +var last = output.lastChild; +var searchVisible = false; +var indexed = false; +var hasResults = false; + +// Listen for events +showButton? showButton.addEventListener("click", displaySearch) : null; +showButtonMobile? showButtonMobile.addEventListener("click", displaySearch) : null; +hideButton.addEventListener("click", hideSearch); +wrapper.addEventListener("click", hideSearch); +modal.addEventListener("click", function (event) { + event.stopPropagation(); + event.stopImmediatePropagation(); + return false; +}); +document.addEventListener("keydown", function (event) { + // Forward slash to open search wrapper + if (event.key == "/") { + if (!searchVisible) { + event.preventDefault(); + displaySearch(); + } + } + + // Esc to close search wrapper + if (event.key == "Escape") { + hideSearch(); + } + + // Down arrow to move down results list + if (event.key == "ArrowDown") { + if (searchVisible && hasResults) { + event.preventDefault(); + if (document.activeElement == input) { + first.focus(); + } else if (document.activeElement == last) { + last.focus(); + } else { + document.activeElement.parentElement.nextSibling.firstElementChild.focus(); + } + } + } + + // Up arrow to move up results list + if (event.key == "ArrowUp") { + if (searchVisible && hasResults) { + event.preventDefault(); + if (document.activeElement == input) { + input.focus(); + } else if (document.activeElement == first) { + input.focus(); + } else { + document.activeElement.parentElement.previousSibling.firstElementChild.focus(); + } + } + } + + // Enter to get to results + if (event.key == "Enter") { + if (searchVisible && hasResults) { + event.preventDefault(); + if (document.activeElement == input) { + first.focus(); + } else { + document.activeElement.click(); + } + }else{ + event.preventDefault(); + } + } + +}); + +// Update search on each keypress +input.onkeyup = function (event) { + executeQuery(this.value); +}; + +function displaySearch() { + if (!indexed) { + buildIndex(); + } + if (!searchVisible) { + document.body.style.overflow = "hidden"; + wrapper.style.visibility = "visible"; + input.focus(); + searchVisible = true; + } +} + +function hideSearch() { + if (searchVisible) { + document.body.style.overflow = "visible"; + wrapper.style.visibility = "hidden"; + input.value = ""; + output.innerHTML = ""; + document.activeElement.blur(); + searchVisible = false; + } +} + +function fetchJSON(path, callback) { + var httpRequest = new XMLHttpRequest(); + httpRequest.onreadystatechange = function () { + if (httpRequest.readyState === 4) { + if (httpRequest.status === 200) { + var data = JSON.parse(httpRequest.responseText); + if (callback) callback(data); + } + } + }; + httpRequest.open("GET", path); + httpRequest.send(); +} + +function buildIndex() { + var baseURL = wrapper.getAttribute("data-url"); + baseURL = baseURL.replace(/\/?$/, '/'); + fetchJSON(baseURL + "index.json", function (data) { + var options = { + shouldSort: true, + ignoreLocation: true, + threshold: 0.0, + includeMatches: true, + keys: [ + { name: "title", weight: 0.8 }, + { name: "section", weight: 0.2 }, + { name: "summary", weight: 0.6 }, + { name: "content", weight: 0.4 }, + ], + }; + /*var finalIndex = []; + for (var i in data) { + if(data[i].type != "users" && data[i].type != "tags" && data[i].type != "categories"){ + finalIndex.push(data[i]); + } + }*/ + fuse = new Fuse(data, options); + indexed = true; + }); +} + +function executeQuery(term) { + let results = fuse.search(term); + let resultsHTML = ""; + + if (results.length > 0) { + results.forEach(function (value, key) { + var title = value.item.externalUrl? value.item.title + '<span class="text-xs ml-2 align-center cursor-default text-neutral-400 dark:text-neutral-500">'+value.item.externalUrl+'</span>' : value.item.title; + var linkconfig = value.item.externalUrl? 'target="_blank" rel="noopener" href="'+value.item.externalUrl+'"' : 'href="'+value.item.permalink+'"'; + resultsHTML = + resultsHTML + + `<li class="mb-2"> + <a class="flex items-center px-3 py-2 rounded-md appearance-none bg-neutral-100 dark:bg-neutral-700 focus:bg-primary-100 hover:bg-primary-100 dark:hover:bg-primary-900 dark:focus:bg-primary-900 focus:outline-dotted focus:outline-transparent focus:outline-2" + ${linkconfig} tabindex="0"> + <div class="grow"> + <div class="-mb-1 text-lg font-bold"> + ${title} + </div> + <div class="text-sm text-neutral-500 dark:text-neutral-400">${value.item.section}<span class="px-2 text-primary-500">·</span>${value.item.date}</span></div> + <div class="text-sm italic">${value.item.summary}</div> + </div> + <div class="ml-2 ltr:block rtl:hidden text-neutral-500">→</div> + <div class="mr-2 ltr:hidden rtl:block text-neutral-500">←</div> + </a> + </li>`; + }); + hasResults = true; + } else { + resultsHTML = ""; + hasResults = false; + } + + output.innerHTML = resultsHTML; + if (results.length > 0) { + first = output.firstChild.firstElementChild; + last = output.lastChild.firstElementChild; + } +} diff --git a/themes/blowfish/assets/js/shortcodes/gallery.js b/themes/blowfish/assets/js/shortcodes/gallery.js new file mode 100644 index 0000000..0c6ac39 --- /dev/null +++ b/themes/blowfish/assets/js/shortcodes/gallery.js @@ -0,0 +1,22 @@ +function _getDefaultPackeryOptions() { + return { + percentPosition: true, + gutter: 5, + resize: true + }; +} + +(function init() { + + $(window).on("load", function () { + let packeries = []; + let nodeGalleries = document.querySelectorAll('.gallery'); + + nodeGalleries.forEach(nodeGallery => { + // TODO : implement a reader of Packery configuration _getPackeryOptions; for example by reading data-attribute + let packery = new Packery(nodeGallery, _getDefaultPackeryOptions()); + packeries.push(packery); + }); + console.groupEnd(); + }); +})();
\ No newline at end of file diff --git a/themes/blowfish/assets/js/zen-mode.js b/themes/blowfish/assets/js/zen-mode.js new file mode 100644 index 0000000..62ccc66 --- /dev/null +++ b/themes/blowfish/assets/js/zen-mode.js @@ -0,0 +1,57 @@ +function _toogleZenMode(zendModeButton) { + // Nodes selection + const body = document.querySelector('body'); + const tocRight = document.querySelector('.toc-right'); + const tocInside = document.querySelector('.toc-inside'); + const articleContent = document.querySelector('.article-content'); + const header = document.querySelector('#single_header'); + + + // Add semantic class into body tag + body.classList.toggle('zen-mode-enable'); + + // Show/Hide 'toc right' and 'toc inside' + tocRight.classList.toggle('lg:block'); + tocInside.classList.toggle('lg:hidden'); + + // Change width of article content + articleContent.classList.toggle('max-w-fit'); + articleContent.classList.toggle('max-w-prose'); + + // Read i18n title from data-attributes + const titleI18nDisable = zendModeButton.getAttribute('data-title-i18n-disable'); + const titleI18nEnable = zendModeButton.getAttribute('data-title-i18n-enable'); + + if (body.classList.contains('zen-mode-enable')) { + // Persist configuration + //localStorage.setItem('blowfish-zen-mode-enabled', 'true'); + + // Change title to enable + zendModeButton.setAttribute('title', titleI18nEnable) + // Auto-scroll to title article + window.scrollTo(window.scrollX, header.getBoundingClientRect().top - 90); + } else { + //localStorage.setItem('blowfish-zen-mode-enabled', 'false'); + zendModeButton.setAttribute('title', titleI18nDisable); + document.querySelector('body').scrollIntoView(); + } +} + +function _registerZendModeButtonClick(zendModeButton) { + zendModeButton.addEventListener('click', function (event) { + event.preventDefault(); + + // Toggle zen-mode + _toogleZenMode(zendModeButton); + }); +} + +(function init() { + window.addEventListener("DOMContentLoaded", (event) => { + // Register click on 'zen-mode-button' node element + const zendModeButton = document.getElementById('zen-mode-button'); + if(zendModeButton !== null && zendModeButton !== undefined) { + _registerZendModeButtonClick(zendModeButton); + } + }); +})();
\ No newline at end of file |