|
@@ -16,9 +16,9 @@ import {
|
|
|
onMediaQueryListEvent,
|
|
|
} from './wowchemy-theming';
|
|
|
|
|
|
-const searchEnabled = (typeof search_config !== 'undefined');
|
|
|
+const searchEnabled = typeof search_config !== 'undefined';
|
|
|
|
|
|
-console.debug(`Environment: ${hugoEnvironment}`)
|
|
|
+console.debug(`Environment: ${hugoEnvironment}`);
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
* Responsive scrolling for URL hashes.
|
|
@@ -41,20 +41,25 @@ function getNavBarHeight() {
|
|
|
function scrollToAnchor(target, duration = 600) {
|
|
|
// If `target` is undefined or HashChangeEvent object, set it to window's hash.
|
|
|
// Decode the hash as browsers can encode non-ASCII characters (e.g. Chinese symbols).
|
|
|
- target = (typeof target === 'undefined' || typeof target === 'object') ? decodeURIComponent(window.location.hash) : target;
|
|
|
+ target =
|
|
|
+ typeof target === 'undefined' || typeof target === 'object' ? decodeURIComponent(window.location.hash) : target;
|
|
|
|
|
|
// If target element exists, scroll to it taking into account fixed navigation bar offset.
|
|
|
if ($(target).length) {
|
|
|
// Escape special chars from IDs, such as colons found in Markdown footnote links.
|
|
|
- target = '#' + $.escapeSelector(target.substring(1)); // Previously, `target = target.replace(/:/g, '\\:');`
|
|
|
+ target = '#' + $.escapeSelector(target.substring(1)); // Previously, `target = target.replace(/:/g, '\\:');`
|
|
|
|
|
|
- let elementOffset = Math.ceil($(target).offset().top - getNavBarHeight()); // Round up to highlight right ID!
|
|
|
+ let elementOffset = Math.ceil($(target).offset().top - getNavBarHeight()); // Round up to highlight right ID!
|
|
|
$('body').addClass('scrolling');
|
|
|
- $('html, body').animate({
|
|
|
- scrollTop: elementOffset
|
|
|
- }, duration, function () {
|
|
|
- $('body').removeClass('scrolling');
|
|
|
- });
|
|
|
+ $('html, body').animate(
|
|
|
+ {
|
|
|
+ scrollTop: elementOffset,
|
|
|
+ },
|
|
|
+ duration,
|
|
|
+ function () {
|
|
|
+ $('body').removeClass('scrolling');
|
|
|
+ },
|
|
|
+ );
|
|
|
} else {
|
|
|
console.debug('Cannot scroll to target `#' + target + '`. ID not found!');
|
|
|
}
|
|
@@ -73,13 +78,14 @@ function fixScrollspy() {
|
|
|
|
|
|
function removeQueryParamsFromUrl() {
|
|
|
if (window.history.replaceState) {
|
|
|
- let urlWithoutSearchParams = window.location.protocol + "//" + window.location.host + window.location.pathname + window.location.hash;
|
|
|
+ let urlWithoutSearchParams =
|
|
|
+ window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.hash;
|
|
|
window.history.replaceState({path: urlWithoutSearchParams}, '', urlWithoutSearchParams);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Check for hash change event and fix responsive offset for hash links (e.g. Markdown footnotes).
|
|
|
-window.addEventListener("hashchange", scrollToAnchor);
|
|
|
+window.addEventListener('hashchange', scrollToAnchor);
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
* Add smooth scrolling to all links inside the main navbar.
|
|
@@ -90,22 +96,25 @@ $('#navbar-main li.nav-item a.nav-link, .js-scroll').on('click', function (event
|
|
|
let hash = this.hash;
|
|
|
|
|
|
// If we are on a widget page and the navbar link is to a section on the same page.
|
|
|
- if (this.pathname === window.location.pathname && hash && $(hash).length && ($(".js-widget-page").length > 0)) {
|
|
|
+ if (this.pathname === window.location.pathname && hash && $(hash).length && $('.js-widget-page').length > 0) {
|
|
|
// Prevent default click behavior.
|
|
|
event.preventDefault();
|
|
|
|
|
|
// Use jQuery's animate() method for smooth page scrolling.
|
|
|
// The numerical parameter specifies the time (ms) taken to scroll to the specified hash.
|
|
|
- let elementOffset = Math.ceil($(hash).offset().top - getNavBarHeight()); // Round up to highlight right ID!
|
|
|
+ let elementOffset = Math.ceil($(hash).offset().top - getNavBarHeight()); // Round up to highlight right ID!
|
|
|
|
|
|
// Uncomment to debug.
|
|
|
// let scrollTop = $(window).scrollTop();
|
|
|
// let scrollDelta = (elementOffset - scrollTop);
|
|
|
// console.debug('Scroll Delta: ' + scrollDelta);
|
|
|
|
|
|
- $('html, body').animate({
|
|
|
- scrollTop: elementOffset
|
|
|
- }, 800);
|
|
|
+ $('html, body').animate(
|
|
|
+ {
|
|
|
+ scrollTop: elementOffset,
|
|
|
+ },
|
|
|
+ 800,
|
|
|
+ );
|
|
|
}
|
|
|
});
|
|
|
|
|
@@ -145,21 +154,23 @@ if ($grid_pubs.length) {
|
|
|
percentPosition: true,
|
|
|
masonry: {
|
|
|
// Use Bootstrap compatible grid layout.
|
|
|
- columnWidth: '.grid-sizer'
|
|
|
+ columnWidth: '.grid-sizer',
|
|
|
},
|
|
|
filter: function () {
|
|
|
let $this = $(this);
|
|
|
let searchResults = searchRegex ? $this.text().match(searchRegex) : true;
|
|
|
let filterResults = filterValues ? $this.is(filterValues) : true;
|
|
|
return searchResults && filterResults;
|
|
|
- }
|
|
|
+ },
|
|
|
});
|
|
|
|
|
|
// Filter by search term.
|
|
|
- let $quickSearch = $('.filter-search').keyup(debounce(function () {
|
|
|
- searchRegex = new RegExp($quickSearch.val(), 'gi');
|
|
|
- $grid_pubs.isotope();
|
|
|
- }));
|
|
|
+ let $quickSearch = $('.filter-search').keyup(
|
|
|
+ debounce(function () {
|
|
|
+ searchRegex = new RegExp($quickSearch.val(), 'gi');
|
|
|
+ $grid_pubs.isotope();
|
|
|
+ }),
|
|
|
+ );
|
|
|
|
|
|
$('.pub-filters').on('change', function () {
|
|
|
let $this = $(this);
|
|
@@ -177,7 +188,7 @@ if ($grid_pubs.length) {
|
|
|
$grid_pubs.isotope();
|
|
|
|
|
|
// If filtering by publication type, update the URL hash to enable direct linking to results.
|
|
|
- if (filterGroup === "pubtype") {
|
|
|
+ if (filterGroup === 'pubtype') {
|
|
|
// Set hash URL to current filter.
|
|
|
let url = $(this).val();
|
|
|
if (url.substr(0, 9) === '.pubtype-') {
|
|
@@ -218,8 +229,7 @@ function concatValues(obj) {
|
|
|
// Filter publications according to hash in URL.
|
|
|
function filter_publications() {
|
|
|
// Check for Isotope publication layout.
|
|
|
- if (!$grid_pubs.length)
|
|
|
- return
|
|
|
+ if (!$grid_pubs.length) return;
|
|
|
|
|
|
let urlHash = window.location.hash.replace('#', '');
|
|
|
let filterValue = '*';
|
|
@@ -242,8 +252,8 @@ function filter_publications() {
|
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
-* Google Maps or OpenStreetMap via Leaflet.
|
|
|
-* --------------------------------------------------------------------------- */
|
|
|
+ * Google Maps or OpenStreetMap via Leaflet.
|
|
|
+ * --------------------------------------------------------------------------- */
|
|
|
|
|
|
function initMap() {
|
|
|
if ($('#map').length) {
|
|
@@ -263,11 +273,11 @@ function initMap() {
|
|
|
zoomControl: true,
|
|
|
zoomControlOpt: {
|
|
|
style: 'SMALL',
|
|
|
- position: 'TOP_LEFT'
|
|
|
+ position: 'TOP_LEFT',
|
|
|
},
|
|
|
streetViewControl: false,
|
|
|
mapTypeControl: false,
|
|
|
- gestureHandling: "cooperative",
|
|
|
+ gestureHandling: 'cooperative',
|
|
|
});
|
|
|
|
|
|
map.addMarker({
|
|
@@ -275,30 +285,36 @@ function initMap() {
|
|
|
lng: lng,
|
|
|
click: function (e) {
|
|
|
let url = 'https://www.google.com/maps/place/' + encodeURIComponent(address) + '/@' + lat + ',' + lng + '/';
|
|
|
- window.open(url, '_blank')
|
|
|
+ window.open(url, '_blank');
|
|
|
},
|
|
|
- title: address
|
|
|
- })
|
|
|
+ title: address,
|
|
|
+ });
|
|
|
} else {
|
|
|
let map = new L.map('map').setView([lat, lng], zoom);
|
|
|
if (map_provider == 3 && api_key.length) {
|
|
|
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
|
|
|
- attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://mapbox.com">Mapbox</a>',
|
|
|
+ attribution:
|
|
|
+ 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://mapbox.com">Mapbox</a>',
|
|
|
tileSize: 512,
|
|
|
maxZoom: 18,
|
|
|
zoomOffset: -1,
|
|
|
id: 'mapbox/streets-v11',
|
|
|
- accessToken: api_key
|
|
|
+ accessToken: api_key,
|
|
|
}).addTo(map);
|
|
|
} else {
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
|
maxZoom: 19,
|
|
|
- attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
|
+ attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
|
}).addTo(map);
|
|
|
}
|
|
|
let marker = L.marker([lat, lng]).addTo(map);
|
|
|
let url = lat + ',' + lng + '#map=' + zoom + '/' + lat + '/' + lng + '&layers=N';
|
|
|
- marker.bindPopup(address + '<p><a href="https://www.openstreetmap.org/directions?engine=osrm_car&route=' + url + '">Routing via OpenStreetMap</a></p>');
|
|
|
+ marker.bindPopup(
|
|
|
+ address +
|
|
|
+ '<p><a href="https://www.openstreetmap.org/directions?engine=osrm_car&route=' +
|
|
|
+ url +
|
|
|
+ '">Routing via OpenStreetMap</a></p>',
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -309,19 +325,21 @@ function initMap() {
|
|
|
|
|
|
function printLatestRelease(selector, repo) {
|
|
|
if (hugoEnvironment === 'production') {
|
|
|
- $.getJSON('https://api.github.com/repos/' + repo + '/tags').done(function (json) {
|
|
|
- let release = json[0];
|
|
|
- $(selector).append(' ' + release.name);
|
|
|
- }).fail(function (jqxhr, textStatus, error) {
|
|
|
- let err = textStatus + ", " + error;
|
|
|
- console.log("Request Failed: " + err);
|
|
|
- });
|
|
|
+ $.getJSON('https://api.github.com/repos/' + repo + '/tags')
|
|
|
+ .done(function (json) {
|
|
|
+ let release = json[0];
|
|
|
+ $(selector).append(' ' + release.name);
|
|
|
+ })
|
|
|
+ .fail(function (jqxhr, textStatus, error) {
|
|
|
+ let err = textStatus + ', ' + error;
|
|
|
+ console.log('Request Failed: ' + err);
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
-* Toggle search dialog.
|
|
|
-* --------------------------------------------------------------------------- */
|
|
|
+ * Toggle search dialog.
|
|
|
+ * --------------------------------------------------------------------------- */
|
|
|
|
|
|
function toggleSearchDialog() {
|
|
|
if ($('body').hasClass('searching')) {
|
|
@@ -339,8 +357,8 @@ function toggleSearchDialog() {
|
|
|
if (!$('#fancybox-style-noscroll').length && document.body.scrollHeight > window.innerHeight) {
|
|
|
$('head').append(
|
|
|
'<style id="fancybox-style-noscroll">.compensate-for-scrollbar{margin-right:' +
|
|
|
- (window.innerWidth - document.documentElement.clientWidth) +
|
|
|
- 'px;}</style>'
|
|
|
+ (window.innerWidth - document.documentElement.clientWidth) +
|
|
|
+ 'px;}</style>',
|
|
|
);
|
|
|
$('body').addClass('compensate-for-scrollbar');
|
|
|
}
|
|
@@ -353,8 +371,8 @@ function toggleSearchDialog() {
|
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
-* Normalize Bootstrap Carousel Slide Heights.
|
|
|
-* --------------------------------------------------------------------------- */
|
|
|
+ * Normalize Bootstrap Carousel Slide Heights.
|
|
|
+ * --------------------------------------------------------------------------- */
|
|
|
|
|
|
function normalizeCarouselSlideHeights() {
|
|
|
$('.carousel').each(function () {
|
|
@@ -363,16 +381,21 @@ function normalizeCarouselSlideHeights() {
|
|
|
// Reset all slide heights.
|
|
|
items.css('min-height', 0);
|
|
|
// Normalize all slide heights.
|
|
|
- let maxHeight = Math.max.apply(null, items.map(function () {
|
|
|
- return $(this).outerHeight()
|
|
|
- }).get());
|
|
|
+ let maxHeight = Math.max.apply(
|
|
|
+ null,
|
|
|
+ items
|
|
|
+ .map(function () {
|
|
|
+ return $(this).outerHeight();
|
|
|
+ })
|
|
|
+ .get(),
|
|
|
+ );
|
|
|
items.css('min-height', maxHeight + 'px');
|
|
|
- })
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
-* Fix Hugo's Goldmark output and Mermaid code blocks.
|
|
|
-* --------------------------------------------------------------------------- */
|
|
|
+ * Fix Hugo's Goldmark output and Mermaid code blocks.
|
|
|
+ * --------------------------------------------------------------------------- */
|
|
|
|
|
|
/**
|
|
|
* Fix Hugo's Goldmark output.
|
|
@@ -453,30 +476,32 @@ $(window).on('load', function () {
|
|
|
itemSelector: '.isotope-item',
|
|
|
layoutMode: layout,
|
|
|
masonry: {
|
|
|
- gutter: 20
|
|
|
+ gutter: 20,
|
|
|
},
|
|
|
- filter: filterText
|
|
|
+ filter: filterText,
|
|
|
});
|
|
|
|
|
|
// Filter Isotope items when a toolbar filter button is clicked.
|
|
|
let isoFilterButtons = isoSection.querySelectorAll('.project-filters a');
|
|
|
- isoFilterButtons.forEach(button => button.addEventListener('click', (e) => {
|
|
|
- e.preventDefault();
|
|
|
- let selector = button.getAttribute('data-filter');
|
|
|
-
|
|
|
- // Apply filter
|
|
|
- console.debug(`Updating Isotope filter to ${selector}`);
|
|
|
- iso.arrange({filter: selector});
|
|
|
-
|
|
|
- // Update active toolbar filter button
|
|
|
- button.classList.remove('active');
|
|
|
- button.classList.add('active');
|
|
|
- let buttonSiblings = getSiblings(button);
|
|
|
- buttonSiblings.forEach(buttonSibling => {
|
|
|
- buttonSibling.classList.remove('active');
|
|
|
- buttonSibling.classList.remove('all');
|
|
|
- });
|
|
|
- }));
|
|
|
+ isoFilterButtons.forEach((button) =>
|
|
|
+ button.addEventListener('click', (e) => {
|
|
|
+ e.preventDefault();
|
|
|
+ let selector = button.getAttribute('data-filter');
|
|
|
+
|
|
|
+ // Apply filter
|
|
|
+ console.debug(`Updating Isotope filter to ${selector}`);
|
|
|
+ iso.arrange({filter: selector});
|
|
|
+
|
|
|
+ // Update active toolbar filter button
|
|
|
+ button.classList.remove('active');
|
|
|
+ button.classList.add('active');
|
|
|
+ let buttonSiblings = getSiblings(button);
|
|
|
+ buttonSiblings.forEach((buttonSibling) => {
|
|
|
+ buttonSibling.classList.remove('active');
|
|
|
+ buttonSibling.classList.remove('all');
|
|
|
+ });
|
|
|
+ }),
|
|
|
+ );
|
|
|
|
|
|
// Check if all Isotope instances have loaded.
|
|
|
incrementIsotopeCounter();
|
|
@@ -513,8 +538,8 @@ $(window).on('load', function () {
|
|
|
let modal = $('#modal');
|
|
|
modal.find('.modal-body code').load(filename, function (response, status, xhr) {
|
|
|
if (status == 'error') {
|
|
|
- let msg = "Error: ";
|
|
|
- $('#modal-error').html(msg + xhr.status + " " + xhr.statusText);
|
|
|
+ let msg = 'Error: ';
|
|
|
+ $('#modal-error').html(msg + xhr.status + ' ' + xhr.statusText);
|
|
|
} else {
|
|
|
$('.js-download-cite').attr('href', filename);
|
|
|
}
|
|
@@ -551,7 +576,7 @@ $(window).on('load', function () {
|
|
|
|
|
|
// Parse Wowchemy keyboard shortcuts.
|
|
|
document.addEventListener('keyup', (event) => {
|
|
|
- if (event.code === "Escape") {
|
|
|
+ if (event.code === 'Escape') {
|
|
|
const body = document.body;
|
|
|
if (body.classList.contains('searching')) {
|
|
|
// Close search dialog.
|
|
@@ -559,13 +584,13 @@ $(window).on('load', function () {
|
|
|
}
|
|
|
}
|
|
|
// Use `key` to check for slash. Otherwise, with `code` we need to check for modifiers.
|
|
|
- if (event.key === "/" ) {
|
|
|
- let focusedElement = (
|
|
|
- document.hasFocus() &&
|
|
|
- document.activeElement !== document.body &&
|
|
|
- document.activeElement !== document.documentElement &&
|
|
|
- document.activeElement
|
|
|
- ) || null;
|
|
|
+ if (event.key === '/') {
|
|
|
+ let focusedElement =
|
|
|
+ (document.hasFocus() &&
|
|
|
+ document.activeElement !== document.body &&
|
|
|
+ document.activeElement !== document.documentElement &&
|
|
|
+ document.activeElement) ||
|
|
|
+ null;
|
|
|
let isInputFocused = focusedElement instanceof HTMLInputElement || focusedElement instanceof HTMLTextAreaElement;
|
|
|
if (searchEnabled && !isInputFocused) {
|
|
|
// Open search dialog.
|
|
@@ -597,15 +622,15 @@ let linkLight = document.querySelector('.js-set-theme-light');
|
|
|
let linkDark = document.querySelector('.js-set-theme-dark');
|
|
|
let linkAuto = document.querySelector('.js-set-theme-auto');
|
|
|
if (linkLight && linkDark && linkAuto) {
|
|
|
- linkLight.addEventListener('click', event => {
|
|
|
+ linkLight.addEventListener('click', (event) => {
|
|
|
event.preventDefault();
|
|
|
changeThemeModeClick(0);
|
|
|
});
|
|
|
- linkDark.addEventListener('click', event => {
|
|
|
+ linkDark.addEventListener('click', (event) => {
|
|
|
event.preventDefault();
|
|
|
changeThemeModeClick(1);
|
|
|
});
|
|
|
- linkAuto.addEventListener('click', event => {
|
|
|
+ linkAuto.addEventListener('click', (event) => {
|
|
|
event.preventDefault();
|
|
|
changeThemeModeClick(2);
|
|
|
});
|
|
@@ -615,7 +640,7 @@ if (linkLight && linkDark && linkAuto) {
|
|
|
// Live update of day/night mode on system preferences update (no refresh required).
|
|
|
// Note: since we listen only for *dark* events, we won't detect other scheme changes such as light to no-preference.
|
|
|
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
|
-darkModeMediaQuery.addEventListener("change", (event) => {
|
|
|
+darkModeMediaQuery.addEventListener('change', (event) => {
|
|
|
onMediaQueryListEvent(event);
|
|
|
});
|
|
|
|