diff --git a/assets/sw.js b/assets/sw.js new file mode 100644 index 0000000..3ece6f3 --- /dev/null +++ b/assets/sw.js @@ -0,0 +1,242 @@ +const CACHE_VERSION = 1; + +const BASE_CACHE_FILES = ["/site.webmanifest"]; + +const OFFLINE_CACHE_FILES = [ + //"/images/logo.png", +]; + +const NOT_FOUND_CACHE_FILES = ["/404.html"]; + +const OFFLINE_PAGE = "/offline/index.html"; +const NOT_FOUND_PAGE = "/404.html"; + +const CACHE_VERSIONS = { + assets: "assets-v" + CACHE_VERSION, + content: "content-v" + CACHE_VERSION, + offline: "offline-v" + CACHE_VERSION, + notFound: "404-v" + CACHE_VERSION, +}; + +// Define MAX_TTL's in SECONDS for specific file extensions +const MAX_TTL = { + "/": 3600, + html: 3600, + json: 86400, + js: 86400, + css: 86400, +}; + +const CACHE_BLACKLIST = [ + (str) => { + return !str.startsWith("http://localhost"); + }, +]; + +const SUPPORTED_METHODS = ["GET"]; + +/** + * isBlackListed + * @param {string} url + * @returns {boolean} + */ +function isBlacklisted(url) { + return CACHE_BLACKLIST.length > 0 + ? !CACHE_BLACKLIST.filter((rule) => { + if (typeof rule === "function") { + return !rule(url); + } else { + return false; + } + }).length + : false; +} + +/** + * getFileExtension + * @param {string} url + * @returns {string} + */ +function getFileExtension(url) { + let extension = url.split(".").reverse()[0].split("?")[0]; + return extension.endsWith("/") ? "/" : extension; +} + +/** + * getTTL + * @param {string} url + */ +function getTTL(url) { + if (typeof url === "string") { + let extension = getFileExtension(url); + if (typeof MAX_TTL[extension] === "number") { + return MAX_TTL[extension]; + } else { + return null; + } + } else { + return null; + } +} + +/** + * installServiceWorker + * @returns {Promise} + */ +function installServiceWorker() { + return Promise.all([ + caches.open(CACHE_VERSIONS.assets).then((cache) => { + return cache.addAll(BASE_CACHE_FILES); + }), + caches.open(CACHE_VERSIONS.offline).then((cache) => { + return cache.addAll(OFFLINE_CACHE_FILES); + }), + caches.open(CACHE_VERSIONS.notFound).then((cache) => { + return cache.addAll(NOT_FOUND_CACHE_FILES); + }), + ]); +} + +/** + * cleanupLegacyCache + * @returns {Promise} + */ +function cleanupLegacyCache() { + let currentCaches = Object.keys(CACHE_VERSIONS).map((key) => { + return CACHE_VERSIONS[key]; + }); + + return new Promise((resolve, reject) => { + caches + .keys() + .then((keys) => { + return (legacyKeys = keys.filter((key) => { + return !~currentCaches.indexOf(key); + })); + }) + .then((legacy) => { + if (legacy.length) { + Promise.all( + legacy.map((legacyKey) => { + return caches.delete(legacyKey); + }) + ) + .then(() => { + resolve(); + }) + .catch((err) => { + reject(err); + }); + } else { + resolve(); + } + }) + .catch(() => { + reject(); + }); + }); +} + +self.addEventListener("install", (event) => { + event.waitUntil(installServiceWorker()); +}); + +// The activate handler takes care of cleaning up old caches. +self.addEventListener("activate", (event) => { + event.waitUntil( + Promise.all([cleanupLegacyCache()]).catch((err) => { + event.skipWaiting(); + }) + ); +}); + +self.addEventListener("fetch", (event) => { + event.respondWith( + caches.open(CACHE_VERSIONS.content).then((cache) => { + return cache + .match(event.request) + .then((response) => { + if (response) { + let headers = response.headers.entries(); + let date = null; + + for (let pair of headers) { + if (pair[0] === "date") { + date = new Date(pair[1]); + } + } + + if (date) { + let age = parseInt( + (new Date().getTime() - date.getTime()) / 1000 + ); + let ttl = getTTL(event.request.url); + + if (ttl && age > ttl) { + return new Promise((resolve) => { + return fetch(event.request) + .then((updatedResponse) => { + if (updatedResponse) { + cache.put(event.request, updatedResponse.clone()); + resolve(updatedResponse); + } else { + resolve(response); + } + }) + .catch(() => { + resolve(response); + }); + }).catch((err) => { + return response; + }); + } else { + return response; + } + } else { + return response; + } + } else { + return null; + } + }) + .then((response) => { + if (response) { + return response; + } else { + return fetch(event.request) + .then((response) => { + if (response.status < 400) { + if ( + ~SUPPORTED_METHODS.indexOf(event.request.method) && + !isBlacklisted(event.request.url) + ) { + cache.put(event.request, response.clone()); + } + return response; + } else { + return caches.open(CACHE_VERSIONS.notFound).then((cache) => { + return cache.match(NOT_FOUND_PAGE); + }); + } + }) + .then((response) => { + if (response) { + return response; + } + }) + .catch(() => { + return caches + .open(CACHE_VERSIONS.offline) + .then((offlineCache) => { + return offlineCache.match(OFFLINE_PAGE); + }); + }); + } + }) + .catch((error) => { + console.error(" Error in fetch handler:", error); + throw error; + }); + }) + ); +}); diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html new file mode 100644 index 0000000..d143880 --- /dev/null +++ b/layouts/partials/footer.html @@ -0,0 +1,207 @@ +{{/* variables for enabling/disabling parts of the footer */}} +{{ $footerEnabled := site.Params.footer.enable | default true }} +{{ $navigationEnabled := site.Params.footer.navigation.enable | default true }} +{{ $customMenusEnabled := site.Params.footer.navigation.customMenus | default true }} +{{ $contactMeEnabled := site.Params.footer.contactMe.enable | default true }} +{{ $newsletterEnabled := site.Params.footer.newsletter.enable | default true }} +{{ $credentialsEnabled := site.Params.footer.credentials.enable | default true }} +{{ $disclaimerEnabled := site.Params.footer.disclaimer.enable | default false }} + +{{/* Keep backward compatibility for the newsletter function */}} +{{ if site.Params.newsletter }} + {{ if site.Params.newsletter.enable }} + {{ $newsletterEnabled = true }} + {{ else }} + {{ $newsletterEnabled = false }} + {{ end }} +{{ end }} + +{{ if $footerEnabled }} + {{ $author:= site.Data.author }} + {{ if (index site.Data site.Language.Lang).author }} + {{ $author = (index site.Data site.Language.Lang).author }} + {{ end }} + + {{ $sections:= site.Data.sections }} + {{ if (index site.Data site.Language.Lang).sections }} + {{ $sections = (index site.Data site.Language.Lang).sections }} + {{ end }} + + {{ $customMenus := site.Params.customMenus }} + {{ if (index site.Data site.Language.Lang).site.customMenus }} + {{ $customMenus = (index site.Data site.Language.Lang).site.customMenus }} + {{ end }} + + {{ $copyrightNotice := now.Format "2006" | printf "© %s Copyright."}} + {{ if (index site.Data site.Language.Lang).site }} + {{ $siteConfig := (index site.Data site.Language.Lang).site }} + {{ if $siteConfig.copyright }} + {{ $copyrightNotice = $siteConfig.copyright }} + {{ end }} + {{ end }} + + + {{ $disclaimer := "" }} + {{ $siteConfig := (index site.Data site.Language.Lang).site }} + {{ if $siteConfig.disclaimer }} + {{ $disclaimer = $siteConfig.disclaimer }} + {{ end }} + + {{/* footer logos */}} + {{ $themeLogo := "/images/theme-logo.png" }} + {{ $hugoLogo := "/images/hugo-logo.svg" }} + + {{/* resize the logos. don't resize svg because it is not supported */}} + {{ $themeLogo:= resources.Get $themeLogo}} + {{ if and $themeLogo (ne $themeLogo.MediaType.SubType "svg") }} + {{ $themeLogo = $themeLogo.Resize "32x" }} + {{ end }} + {{ $themeLogo = $themeLogo.RelPermalink}} + + {{ $hugoLogo:= resources.Get $hugoLogo}} + {{ if and $hugoLogo (ne $hugoLogo.MediaType.SubType "svg")}} + {{ $hugoLogo = $hugoLogo.Resize "32x" }} + {{ end }} + {{ $hugoLogo = $hugoLogo.RelPermalink}} + + +{{end}} diff --git a/layouts/partials/header.html b/layouts/partials/header.html new file mode 100644 index 0000000..583b57b --- /dev/null +++ b/layouts/partials/header.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + +{{ if site.Params.darkMode.enable }} {{ end }} + + +{{/* add favicon only if the site author has provided the the favicon */}} {{ if +site.Params.logo.favicon }} {{ $favicon := site.Params.logo.favicon }} {{/* +resize the favicon. don't resize svg because it is not supported */}} {{ +$favicon = resources.Get $favicon }} {{ if and $favicon (ne +$favicon.MediaType.SubType "svg") }} {{ $favicon = $favicon.Resize "42x" }} {{ +end }} {{ $favicon = $favicon.RelPermalink}} + + +{{end}}