Loading .gitignore 0 → 100644 +18 −0 Original line number Diff line number Diff line # See http://help.github.com/ignore-files/ for more about ignoring files. # # If you find yourself ignoring temporary files generated by your text editor # or operating system, you probably want to add a global ignore instead: # git config --global core.excludesfile ~/.gitignore_global .DS_Store .hugo_build.lock /.idea /resources /public /node_modules /data/public # Ignore code quality output markdownlint-cli2-codequality.json /scripts/issue_log No newline at end of file assets/js/notification-generator.js 0 → 100644 +77 −0 Original line number Diff line number Diff line (function() { function genNotification() { const id = crypto.randomUUID(); const type = document.getElementById("notificationType").value; const icon = (() => { div = document.createElement('div') div.innerHTML = document.getElementById("notificationIcon").value; return div.firstChild.classList.value; })(); const title = document.getElementById("notificationTitle").value; const url = document.getElementById("applyToURL").value; const messageHTML = document.getElementById("notificationHTML").value; let now = new Date(); let expiry = new Date(now.getTime() + (1*24*60*60*1000)) if (document.getElementById("expiryDate").value != "") expiry = new Date(Date.parse(document.getElementById("expiryDate").value)); const autohide = document.getElementById("autoHide").checked; const nowStr = now.toISOString().split('T')[0] + " " + now.toTimeString().split(" ")[0]; const expiryStr = expiry.toISOString().split('T')[0] + " " + expiry.toTimeString().split(" ")[0]; let n = { "id": id, "type": type, "icon": icon, "title": title, "url": url, "messageHTML": messageHTML, "posted": nowStr, "expires": expiryStr, "autohide": autohide } if(url === "*") delete n['url']; return n } async function getPageList() { let response = await fetch("/sitemap.xml"); let data = await response.text(); generatePageList(data); } function generatePageList(xml) { const siteMapDoc = parser.parseFromString(xml,"text/xml"); const pages = siteMapDoc.getElementsByTagName("url"); Array.prototype.slice.call(pages).forEach(e => { // This happens in local dev where there's no git information // for the current file so no lastmod element. page = document.createElement("option") loc = e.getElementsByTagName("loc")[0].textContent; page.value = loc.split(window.location.origin)[1]; document.getElementById("pagesList").appendChild(page); }); } document.getElementById("testBtn").onclick = () => { renderNotification(genNotification(), false); } document.getElementById('createBtn').onclick = () => { document.getElementsByTagName('pre')[0].textContent = JSON.stringify(genNotification(),null,2); } document.getElementById('copyBtn').onclick = () => { navigator.clipboard.writeText(JSON.stringify(genNotification(),null,2)) renderNotification({ "id": crypto.randomUUID(), "type": "success", "icon": "fa-solid fa-clipboard", "title": "JSON Copied to Clipboard", "messageHTML": "<p>The notification json has been copied to the clipboard. Please add it to the end of the notification.json file and raise a new MR to share your notification with the world.</p>", "expires": new Date(), "autohide": true }) } document.getElementsByTagName('pre')[0].textContent = JSON.stringify(genNotification(),null,2); const parser = new DOMParser(); getPageList(); })(); assets/js/notification-preferences.js +1 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ $( document ).ready(() => { notificationPreferences.showNotifications = false; setCookie("notification-preferences", JSON.stringify(notificationPreferences), 365); delAllNotificationCookies(); stopNotifications(); notifications.stop(); } function formControlDisabled(disable) { Loading assets/js/notifications.js +20 −16 Original line number Diff line number Diff line Loading @@ -99,7 +99,6 @@ async function processNotifications() { upcomingNotifications.push(n); processedNotifications.push(n.id); }); notifications.forEach(n => { renderNotification(n); }) Loading Loading @@ -157,8 +156,13 @@ function renderNotification(notification, withCookie=true) { `; if(withCookie) { toastEl.addEventListener('hidden.bs.toast', () => { if(document.getElementById(id+"-check") != null) if(document.getElementById(id+"-check") != null) { document.getElementById(id+"-check").checked=true; e = document.getElementById(id+"-markBtn"); e.textContent = "Unread"; e.classList.remove('btn-success'); e.classList.add("btn-danger"); } switch(type) { case "warning": setCookie("toast-"+id, true, notificationPreferences.warningHide); Loading Loading @@ -276,17 +280,17 @@ $( document ).ready(() => { if(notificationPreferences.showNotifications) { startNotifications() } }); export { getCookie as "getCookie", setCookie as "setCookie", renderNotification as "notifications.render", startNotifications as "notifications.start", stopNotifications as "nofifications.stop", startPageUpdates as "notifications.startPageUpdates", stopPageUpdates as "notifications.stopPageUpdates", notificationPreferences as "notifications.preferences", processedNotifications as "notifications.processed", allToasts as "notifications.toasts" } // Set gloabal scope items globalThis.getCookie = getCookie; globalThis.setCookie = setCookie; globalThis.notificationPreferences = notificationPreferences; globalThis.renderNotification = renderNotification; globalThis.stopNotifications = stopNotifications; globalThis.startNotifications = startNotifications; globalThis.startPageUpdates = startPageUpdates; globalThis.stopPageUpdates = stopPageUpdates; globalThis.processedNotifications = processedNotifications; globalThis.allToasts = allToasts; globalThis.notificationProcess = notificationProcess; }); assets/js/old-offline-search.js 0 → 100644 +179 −0 Original line number Diff line number Diff line // Adapted from code by Matt Walters https://www.mattwalters.net/posts/2018-03-28-hugo-and-lunr/ (function ($) { 'use strict'; $(document).ready(function () { const $searchInput = $('.td-search__input'); const $searchParam = 'search'; let query = getParameterByName($searchParam)?.trim() || ''; // // Options for popover // $searchInput.data('html', true); $searchInput.data('placement', 'bottom'); $searchInput.data( 'template', '<div class="popover td-offline-search-results" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body" style="padding: 0"></div></div>' ); // // Register handler // $searchInput.on('change', (event) => { render($(event.target)); // Hide keyboard on mobile browser $searchInput.blur(); }); // Prevent reloading page by enter key on sidebar search. $searchInput.closest('form').on('submit', () => { return false; }); // // Lunr // let idx = null; // Lunr index const resultDetails = new Map(); // Will hold the data for the search results (titles and summaries) // Set up for an Ajax call to request the JSON data file that is created by Hugo's build process $.ajax($searchInput.data('offline-search-index-json-src')).then( (data) => { idx = lunr(function () { this.ref('ref'); // If you added more searchable fields to the search index, list them here. // Here you can specify searchable fields to the search index - e.g. individual toxonomies for you project // With "boost" you can add weighting for specific (default weighting without boost: 1) this.field('title', { boost: 5 }); this.field('categories', { boost: 3 }); this.field('tags', { boost: 3 }); // this.field('projects', { boost: 3 }); // example for an individual toxonomy called projects this.field('description', { boost: 2 }); this.field('body'); data.forEach((doc) => { this.add(doc); resultDetails.set(doc.ref, { title: doc.title, excerpt: doc.excerpt, }); }); }); if (query) { $searchInput.first().val(query); $searchInput.first().trigger('change'); } } ); const render = ($targetSearchInput) => { // Dispose the previous result $searchInput.popover('dispose'); // // Search // if (idx === null) { return; } const searchQuery = $targetSearchInput.val(); if (searchQuery === '') { return; } const results = idx .query((q) => { const tokens = lunr.tokenizer(searchQuery.toLowerCase()); tokens.forEach((token) => { const queryString = token.toString(); q.term(queryString, { boost: 100, }); q.term(queryString, { wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING, boost: 10, }); q.term(queryString, { editDistance: 2, }); }); }) .slice( 0, $targetSearchInput.data('offline-search-max-results') ); // // Make result html // const $html = $('<div class="list-group">') .css({ maxHeight: `calc(100vh - ${ $targetSearchInput.offset().top - $(window).scrollTop() + 180 }px)`, overflowY: 'auto', }); if (results.length === 0) { $html.append( $('<div class="list-group-item">').text(`No results found for query "${searchQuery}"`) ); } else { results.forEach((r, i) => { const doc = resultDetails.get(r.ref); const href = $searchInput.data('offline-search-base-href') + r.ref.replace(/^\//, ''); $html.append(` <a href="${href}" tabindex="${i+1}" class="list-group-item list-group-item-action"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">${doc.title}</h5> <small class="text-muted">3 days ago</small> </div> <p class="mb-1">${doc.excerpt}</p> <small class="text-muted">${r.ref}</small> </a> `); }); } // Enable inline styles in popover. const whiteList = $.fn.tooltip.Constructor.Default.whiteList; whiteList['*'].push('style', 'tabindex'); $targetSearchInput .data('content', $html[0].outerHTML) .popover({ whiteList: whiteList }) .popover('show'); updateQueryParam(searchQuery); }; function getParameterByName(name, url) { if (!url) url = window.location.href; return new URL(url).searchParams.get(name); } function updateQueryParam(query) { let url = new URL(window.location) url.searchParams.set($searchParam, query); history.pushState('', '', url.toString()); } }); })(jQuery); Loading
.gitignore 0 → 100644 +18 −0 Original line number Diff line number Diff line # See http://help.github.com/ignore-files/ for more about ignoring files. # # If you find yourself ignoring temporary files generated by your text editor # or operating system, you probably want to add a global ignore instead: # git config --global core.excludesfile ~/.gitignore_global .DS_Store .hugo_build.lock /.idea /resources /public /node_modules /data/public # Ignore code quality output markdownlint-cli2-codequality.json /scripts/issue_log No newline at end of file
assets/js/notification-generator.js 0 → 100644 +77 −0 Original line number Diff line number Diff line (function() { function genNotification() { const id = crypto.randomUUID(); const type = document.getElementById("notificationType").value; const icon = (() => { div = document.createElement('div') div.innerHTML = document.getElementById("notificationIcon").value; return div.firstChild.classList.value; })(); const title = document.getElementById("notificationTitle").value; const url = document.getElementById("applyToURL").value; const messageHTML = document.getElementById("notificationHTML").value; let now = new Date(); let expiry = new Date(now.getTime() + (1*24*60*60*1000)) if (document.getElementById("expiryDate").value != "") expiry = new Date(Date.parse(document.getElementById("expiryDate").value)); const autohide = document.getElementById("autoHide").checked; const nowStr = now.toISOString().split('T')[0] + " " + now.toTimeString().split(" ")[0]; const expiryStr = expiry.toISOString().split('T')[0] + " " + expiry.toTimeString().split(" ")[0]; let n = { "id": id, "type": type, "icon": icon, "title": title, "url": url, "messageHTML": messageHTML, "posted": nowStr, "expires": expiryStr, "autohide": autohide } if(url === "*") delete n['url']; return n } async function getPageList() { let response = await fetch("/sitemap.xml"); let data = await response.text(); generatePageList(data); } function generatePageList(xml) { const siteMapDoc = parser.parseFromString(xml,"text/xml"); const pages = siteMapDoc.getElementsByTagName("url"); Array.prototype.slice.call(pages).forEach(e => { // This happens in local dev where there's no git information // for the current file so no lastmod element. page = document.createElement("option") loc = e.getElementsByTagName("loc")[0].textContent; page.value = loc.split(window.location.origin)[1]; document.getElementById("pagesList").appendChild(page); }); } document.getElementById("testBtn").onclick = () => { renderNotification(genNotification(), false); } document.getElementById('createBtn').onclick = () => { document.getElementsByTagName('pre')[0].textContent = JSON.stringify(genNotification(),null,2); } document.getElementById('copyBtn').onclick = () => { navigator.clipboard.writeText(JSON.stringify(genNotification(),null,2)) renderNotification({ "id": crypto.randomUUID(), "type": "success", "icon": "fa-solid fa-clipboard", "title": "JSON Copied to Clipboard", "messageHTML": "<p>The notification json has been copied to the clipboard. Please add it to the end of the notification.json file and raise a new MR to share your notification with the world.</p>", "expires": new Date(), "autohide": true }) } document.getElementsByTagName('pre')[0].textContent = JSON.stringify(genNotification(),null,2); const parser = new DOMParser(); getPageList(); })();
assets/js/notification-preferences.js +1 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ $( document ).ready(() => { notificationPreferences.showNotifications = false; setCookie("notification-preferences", JSON.stringify(notificationPreferences), 365); delAllNotificationCookies(); stopNotifications(); notifications.stop(); } function formControlDisabled(disable) { Loading
assets/js/notifications.js +20 −16 Original line number Diff line number Diff line Loading @@ -99,7 +99,6 @@ async function processNotifications() { upcomingNotifications.push(n); processedNotifications.push(n.id); }); notifications.forEach(n => { renderNotification(n); }) Loading Loading @@ -157,8 +156,13 @@ function renderNotification(notification, withCookie=true) { `; if(withCookie) { toastEl.addEventListener('hidden.bs.toast', () => { if(document.getElementById(id+"-check") != null) if(document.getElementById(id+"-check") != null) { document.getElementById(id+"-check").checked=true; e = document.getElementById(id+"-markBtn"); e.textContent = "Unread"; e.classList.remove('btn-success'); e.classList.add("btn-danger"); } switch(type) { case "warning": setCookie("toast-"+id, true, notificationPreferences.warningHide); Loading Loading @@ -276,17 +280,17 @@ $( document ).ready(() => { if(notificationPreferences.showNotifications) { startNotifications() } }); export { getCookie as "getCookie", setCookie as "setCookie", renderNotification as "notifications.render", startNotifications as "notifications.start", stopNotifications as "nofifications.stop", startPageUpdates as "notifications.startPageUpdates", stopPageUpdates as "notifications.stopPageUpdates", notificationPreferences as "notifications.preferences", processedNotifications as "notifications.processed", allToasts as "notifications.toasts" } // Set gloabal scope items globalThis.getCookie = getCookie; globalThis.setCookie = setCookie; globalThis.notificationPreferences = notificationPreferences; globalThis.renderNotification = renderNotification; globalThis.stopNotifications = stopNotifications; globalThis.startNotifications = startNotifications; globalThis.startPageUpdates = startPageUpdates; globalThis.stopPageUpdates = stopPageUpdates; globalThis.processedNotifications = processedNotifications; globalThis.allToasts = allToasts; globalThis.notificationProcess = notificationProcess; });
assets/js/old-offline-search.js 0 → 100644 +179 −0 Original line number Diff line number Diff line // Adapted from code by Matt Walters https://www.mattwalters.net/posts/2018-03-28-hugo-and-lunr/ (function ($) { 'use strict'; $(document).ready(function () { const $searchInput = $('.td-search__input'); const $searchParam = 'search'; let query = getParameterByName($searchParam)?.trim() || ''; // // Options for popover // $searchInput.data('html', true); $searchInput.data('placement', 'bottom'); $searchInput.data( 'template', '<div class="popover td-offline-search-results" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body" style="padding: 0"></div></div>' ); // // Register handler // $searchInput.on('change', (event) => { render($(event.target)); // Hide keyboard on mobile browser $searchInput.blur(); }); // Prevent reloading page by enter key on sidebar search. $searchInput.closest('form').on('submit', () => { return false; }); // // Lunr // let idx = null; // Lunr index const resultDetails = new Map(); // Will hold the data for the search results (titles and summaries) // Set up for an Ajax call to request the JSON data file that is created by Hugo's build process $.ajax($searchInput.data('offline-search-index-json-src')).then( (data) => { idx = lunr(function () { this.ref('ref'); // If you added more searchable fields to the search index, list them here. // Here you can specify searchable fields to the search index - e.g. individual toxonomies for you project // With "boost" you can add weighting for specific (default weighting without boost: 1) this.field('title', { boost: 5 }); this.field('categories', { boost: 3 }); this.field('tags', { boost: 3 }); // this.field('projects', { boost: 3 }); // example for an individual toxonomy called projects this.field('description', { boost: 2 }); this.field('body'); data.forEach((doc) => { this.add(doc); resultDetails.set(doc.ref, { title: doc.title, excerpt: doc.excerpt, }); }); }); if (query) { $searchInput.first().val(query); $searchInput.first().trigger('change'); } } ); const render = ($targetSearchInput) => { // Dispose the previous result $searchInput.popover('dispose'); // // Search // if (idx === null) { return; } const searchQuery = $targetSearchInput.val(); if (searchQuery === '') { return; } const results = idx .query((q) => { const tokens = lunr.tokenizer(searchQuery.toLowerCase()); tokens.forEach((token) => { const queryString = token.toString(); q.term(queryString, { boost: 100, }); q.term(queryString, { wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING, boost: 10, }); q.term(queryString, { editDistance: 2, }); }); }) .slice( 0, $targetSearchInput.data('offline-search-max-results') ); // // Make result html // const $html = $('<div class="list-group">') .css({ maxHeight: `calc(100vh - ${ $targetSearchInput.offset().top - $(window).scrollTop() + 180 }px)`, overflowY: 'auto', }); if (results.length === 0) { $html.append( $('<div class="list-group-item">').text(`No results found for query "${searchQuery}"`) ); } else { results.forEach((r, i) => { const doc = resultDetails.get(r.ref); const href = $searchInput.data('offline-search-base-href') + r.ref.replace(/^\//, ''); $html.append(` <a href="${href}" tabindex="${i+1}" class="list-group-item list-group-item-action"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">${doc.title}</h5> <small class="text-muted">3 days ago</small> </div> <p class="mb-1">${doc.excerpt}</p> <small class="text-muted">${r.ref}</small> </a> `); }); } // Enable inline styles in popover. const whiteList = $.fn.tooltip.Constructor.Default.whiteList; whiteList['*'].push('style', 'tabindex'); $targetSearchInput .data('content', $html[0].outerHTML) .popover({ whiteList: whiteList }) .popover('show'); updateQueryParam(searchQuery); }; function getParameterByName(name, url) { if (!url) url = window.location.href; return new URL(url).searchParams.get(name); } function updateQueryParam(query) { let url = new URL(window.location) url.searchParams.set($searchParam, query); history.pushState('', '', url.toString()); } }); })(jQuery);