From 48123e21b176f860777f4b712c7bd13737903ec7 Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Mon, 1 Nov 2021 04:10:20 +0000 Subject: [PATCH] Fix search, barely --- themes/academic/assets/js/academic-search.js | 45 +- themes/academic/assets/js/academic.js | 1364 ++++++++--------- themes/academic/assets/js/vendor/fuse.min.js | 9 + themes/academic/assets/js/vendor/mark.min.js | 7 + themes/academic/assets/scss/ansol.scss | 14 + themes/academic/layouts/_default/baseof.html | 5 +- themes/academic/layouts/partials/search.html | 56 +- themes/academic/layouts/partials/site_js.html | 26 +- 8 files changed, 702 insertions(+), 824 deletions(-) create mode 100644 themes/academic/assets/js/vendor/fuse.min.js create mode 100644 themes/academic/assets/js/vendor/mark.min.js diff --git a/themes/academic/assets/js/academic-search.js b/themes/academic/assets/js/academic-search.js index 1a22753..0020be2 100644 --- a/themes/academic/assets/js/academic-search.js +++ b/themes/academic/assets/js/academic-search.js @@ -50,11 +50,11 @@ function updateURL(url) { // Pre-process new search query. function initSearch(force, fuse) { - let query = $("#search-query").val(); + let query = document.querySelector("#search-query").value; // If query deleted, clear results. - if ( query.length < 1) { - $('#search-hits').empty(); + if (query.length < 1) { + document.querySelector('#search-hits').innerHTML = ""; } // Check for timer event (enter key not pressed) and query less than minimum length required. @@ -62,7 +62,7 @@ function initSearch(force, fuse) { return; // Do search. - $('#search-hits').empty(); + document.querySelector('#search-hits').innerHTML = ""; searchAcademic(query, fuse); let newURL = window.location.protocol + "//" + window.location.host + window.location.pathname + '?q=' + encodeURIComponent(query) + window.location.hash; updateURL(newURL); @@ -74,16 +74,17 @@ function searchAcademic(query, fuse) { // console.log({"results": results}); if (results.length > 0) { - $('#search-hits').append('

' + results.length + ' ' + i18n.results + '

'); + document.querySelector('#search-hits').insertAdjacentHTML('beforeend', '

' + results.length + ' ' + i18n.results + '

'); parseResults(query, results); } else { - $('#search-hits').append('
' + i18n.no_results + '
'); + document.querySelector('#search-hits').insertAdjacentHTML('beforeend', '
' + i18n.no_results + '
'); } } // Parse search results. function parseResults(query, results) { - $.each( results, function(key, value) { + console.log(results); + results.forEach(function (value, key) { let content_key = value.item.section; let content = ""; let snippet = ""; @@ -99,7 +100,7 @@ function parseResults(query, results) { if ( fuseOptions.tokenize ) { snippetHighlights.push(query); } else { - $.each( value.matches, function(matchKey, matchValue) { + value.matches.forEach(function(matchValue, matchKey) { if (matchValue.key == "content") { let start = (matchValue.indices[0][0]-summaryLength>0) ? matchValue.indices[0][0]-summaryLength : 0; let end = (matchValue.indices[0][1]+summaryLength response.json()).then(function (search_index) { let fuse = new Fuse(search_index, fuseOptions); // On page load, check for search query in URL. if (query = getSearchQuery('q')) { - $("body").addClass('searching'); - $('.search-results').css({opacity: 0, visibility: "visible"}).animate({opacity: 1},200); - $("#search-query").val(query); - $("#search-query").focus(); + document.querySelector("body").classList.add('searching'); + document.querySelector("#search-query").value = query; + document.querySelector("#search-query").focus(); initSearch(true, fuse); } // On search box key up, process query. - $('#search-query').keyup(function (e) { - clearTimeout($.data(this, 'searchTimer')); // Ensure only one timer runs! + document.querySelector('#search-query').addEventListener('keyup', function (e) { + clearTimeout(this.dataset.searchTimer); if (e.keyCode == 13) { initSearch(true, fuse); } else { - $(this).data('searchTimer', setTimeout(function () { - initSearch(false, fuse); - }, 250)); + this.dataset.searchTimer = setTimeout(function () { initSearch(false, fuse); }, 250); } }); }); diff --git a/themes/academic/assets/js/academic.js b/themes/academic/assets/js/academic.js index a658b0e..10c7b26 100644 --- a/themes/academic/assets/js/academic.js +++ b/themes/academic/assets/js/academic.js @@ -5,793 +5,657 @@ * Core JS functions and initialization. **************************************************/ -(function ($) { +// Filter by search term. +let $quickSearch = $('.filter-search').keyup(debounce(function () { + searchRegex = new RegExp($quickSearch.val(), 'gi'); + $grid_pubs.isotope(); +})); - /* --------------------------------------------------------------------------- - * Responsive scrolling for URL hashes. - * --------------------------------------------------------------------------- */ +// Debounce input to prevent spamming filter requests. +function debounce(fn, threshold) { + let timeout; + threshold = threshold || 100; + return function debounced() { + clearTimeout(timeout); + let args = arguments; + let _this = this; - // Dynamically get responsive navigation bar height for offsetting Scrollspy. - function getNavBarHeight() { - let $navbar = $('#navbar-main'); - let navbar_offset = $navbar.outerHeight(); - console.debug('Navbar height: ' + navbar_offset); - return navbar_offset; + function delayed() { + fn.apply(_this, args); + } + + timeout = setTimeout(delayed, threshold); + }; +} + +// Flatten object by concatenating values. +function concatValues(obj) { + let value = ''; + for (let prop in obj) { + value += obj[prop]; + } + return value; +} + +$('.pub-filters').on('change', function () { + let $this = $(this); + + // Get group key. + let filterGroup = $this[0].getAttribute('data-filter-group'); + + // Set filter for group. + pubFilters[filterGroup] = this.value; + + // Combine filters. + filterValues = concatValues(pubFilters); + + // Activate filters. + $grid_pubs.isotope(); + + // If filtering by publication type, update the URL hash to enable direct linking to results. + if (filterGroup == "pubtype") { + // Set hash URL to current filter. + let url = $(this).val(); + if (url.substr(0, 9) == '.pubtype-') { + window.location.hash = url.substr(9); + } else { + window.location.hash = ''; + } + } +}); + +// Filter publications according to hash in URL. +function filter_publications() { + let urlHash = window.location.hash.replace('#', ''); + let filterValue = '*'; + + // Check if hash is numeric. + if (urlHash != '' && !isNaN(urlHash)) { + filterValue = '.pubtype-' + urlHash; } - /** - * Responsive hash scrolling. - * Check for a URL hash as an anchor. - * If it exists on current page, scroll to it responsively. - * If `target` argument omitted (e.g. after event), assume it's the window's hash. - */ - function scrollToAnchor(target) { - // 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; + // Set filter. + let filterGroup = 'pubtype'; + pubFilters[filterGroup] = filterValue; + filterValues = concatValues(pubFilters); - // 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, '\\:');` + // Activate filters. + $grid_pubs.isotope(); - let elementOffset = Math.ceil($(target).offset().top - getNavBarHeight()); // Round up to highlight right ID! - $('body').addClass('scrolling'); - $('html, body').animate({ - scrollTop: elementOffset - }, 600, function () { - $('body').removeClass('scrolling'); + // Set selected option. + $('.pubtype-select').val(filterValue); +} + +/* --------------------------------------------------------------------------- +* Google Maps or OpenStreetMap via Leaflet. +* --------------------------------------------------------------------------- */ + +function initMap() { + if ($('#map').length) { + let map_provider = $('#map-provider').val(); + let lat = $('#map-lat').val(); + let lng = $('#map-lng').val(); + let zoom = parseInt($('#map-zoom').val()); + let address = $('#map-dir').val(); + let api_key = $('#map-api-key').val(); + + if (map_provider == 1) { + let map = new GMaps({ + div: '#map', + lat: lat, + lng: lng, + zoom: zoom, + zoomControl: true, + zoomControlOpt: { + style: 'SMALL', + position: 'TOP_LEFT' + }, + panControl: false, + streetViewControl: false, + mapTypeControl: false, + overviewMapControl: false, + scrollwheel: true, + draggable: true }); + + map.addMarker({ + lat: lat, + lng: lng, + click: function (e) { + let url = 'https://www.google.com/maps/place/' + encodeURIComponent(address) + '/@' + lat + ',' + lng + '/'; + window.open(url, '_blank') + }, + title: address + }) } else { - console.debug('Cannot scroll to target `#' + target + '`. ID not found!'); - } - } - - // Make Scrollspy responsive. - function fixScrollspy() { - let $body = $('body'); - let data = $body.data('bs.scrollspy'); - if (data) { - data._config.offset = getNavBarHeight(); - $body.data('bs.scrollspy', data); - $body.scrollspy('refresh'); - } - } - - function removeQueryParamsFromUrl() { - if (window.history.replaceState) { - 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); - - /* --------------------------------------------------------------------------- - * Add smooth scrolling to all links inside the main navbar. - * --------------------------------------------------------------------------- */ - - $('#navbar-main li.nav-item a.nav-link').on('click', function (event) { - // Store requested URL hash. - 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)) { - // 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! - - // Uncomment to debug. - // let scrollTop = $(window).scrollTop(); - // let scrollDelta = (elementOffset - scrollTop); - // console.debug('Scroll Delta: ' + scrollDelta); - - $('html, body').animate({ - scrollTop: elementOffset - }, 800); - } - }); - - /* --------------------------------------------------------------------------- - * Hide mobile collapsable menu on clicking a link. - * --------------------------------------------------------------------------- */ - - $(document).on('click', '.navbar-collapse.show', function (e) { - //get the element that was clicked, even if the element that is inside the element is e.target - let targetElement = $(e.target).is('a') ? $(e.target) : $(e.target).parent(); - - if (targetElement.is('a') && targetElement.attr('class') != 'dropdown-toggle') { - $(this).collapse('hide'); - } - }); - - /* --------------------------------------------------------------------------- - * Filter publications. - * --------------------------------------------------------------------------- */ - - // Active publication filters. - let pubFilters = {}; - - // Search term. - let searchRegex; - - // Filter values (concatenated). - let filterValues; - - // Publication container. - let $grid_pubs = $('#container-publications'); - - // Initialise Isotope. - $grid_pubs.isotope({ - itemSelector: '.isotope-item', - percentPosition: true, - masonry: { - // Use Bootstrap compatible grid layout. - 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(); - })); - - // Debounce input to prevent spamming filter requests. - function debounce(fn, threshold) { - let timeout; - threshold = threshold || 100; - return function debounced() { - clearTimeout(timeout); - let args = arguments; - let _this = this; - - function delayed() { - fn.apply(_this, args); - } - - timeout = setTimeout(delayed, threshold); - }; - } - - // Flatten object by concatenating values. - function concatValues(obj) { - let value = ''; - for (let prop in obj) { - value += obj[prop]; - } - return value; - } - - $('.pub-filters').on('change', function () { - let $this = $(this); - - // Get group key. - let filterGroup = $this[0].getAttribute('data-filter-group'); - - // Set filter for group. - pubFilters[filterGroup] = this.value; - - // Combine filters. - filterValues = concatValues(pubFilters); - - // Activate filters. - $grid_pubs.isotope(); - - // If filtering by publication type, update the URL hash to enable direct linking to results. - if (filterGroup == "pubtype") { - // Set hash URL to current filter. - let url = $(this).val(); - if (url.substr(0, 9) == '.pubtype-') { - window.location.hash = url.substr(9); + let map = new L.map('map').setView([lat, lng], zoom); + if (map_provider == 3 && api_key.length) { + L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', { + attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', + maxZoom: 18, + id: 'mapbox.streets', + accessToken: api_key + }).addTo(map); } else { - window.location.hash = ''; - } - } - }); - - // Filter publications according to hash in URL. - function filter_publications() { - let urlHash = window.location.hash.replace('#', ''); - let filterValue = '*'; - - // Check if hash is numeric. - if (urlHash != '' && !isNaN(urlHash)) { - filterValue = '.pubtype-' + urlHash; - } - - // Set filter. - let filterGroup = 'pubtype'; - pubFilters[filterGroup] = filterValue; - filterValues = concatValues(pubFilters); - - // Activate filters. - $grid_pubs.isotope(); - - // Set selected option. - $('.pubtype-select').val(filterValue); - } - - /* --------------------------------------------------------------------------- - * Google Maps or OpenStreetMap via Leaflet. - * --------------------------------------------------------------------------- */ - - function initMap() { - if ($('#map').length) { - let map_provider = $('#map-provider').val(); - let lat = $('#map-lat').val(); - let lng = $('#map-lng').val(); - let zoom = parseInt($('#map-zoom').val()); - let address = $('#map-dir').val(); - let api_key = $('#map-api-key').val(); - - if (map_provider == 1) { - let map = new GMaps({ - div: '#map', - lat: lat, - lng: lng, - zoom: zoom, - zoomControl: true, - zoomControlOpt: { - style: 'SMALL', - position: 'TOP_LEFT' - }, - panControl: false, - streetViewControl: false, - mapTypeControl: false, - overviewMapControl: false, - scrollwheel: true, - draggable: true - }); - - map.addMarker({ - lat: lat, - lng: lng, - click: function (e) { - let url = 'https://www.google.com/maps/place/' + encodeURIComponent(address) + '/@' + lat + ',' + lng + '/'; - window.open(url, '_blank') - }, - title: address - }) - } else { - let map = new L.map('map').setView([lat, lng], zoom); - if (map_provider == 3 && api_key.length) { - L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', { - attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', - maxZoom: 18, - id: 'mapbox.streets', - accessToken: api_key - }).addTo(map); - } else { - L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 19, - attribution: '© OpenStreetMap' - }).addTo(map); - } - let marker = L.marker([lat, lng]).addTo(map); - let url = lat + ',' + lng + '#map=' + zoom + '/' + lat + '/' + lng + '&layers=N'; - marker.bindPopup(address + '

Routing via OpenStreetMap

'); + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 19, + attribution: '© OpenStreetMap' + }).addTo(map); } + let marker = L.marker([lat, lng]).addTo(map); + let url = lat + ',' + lng + '#map=' + zoom + '/' + lat + '/' + lng + '&layers=N'; + marker.bindPopup(address + '

Routing via OpenStreetMap

'); } } +} - /* --------------------------------------------------------------------------- - * GitHub API. - * --------------------------------------------------------------------------- */ - - function printLatestRelease(selector, repo) { - $.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. - * --------------------------------------------------------------------------- */ - - function toggleSearchDialog() { - if ($('body').hasClass('searching')) { - // Clear search query and hide search modal. - $('[id=search-query]').blur(); - $('body').removeClass('searching compensate-for-scrollbar'); - - // Remove search query params from URL as user has finished searching. - removeQueryParamsFromUrl(); - - // Prevent fixed positioned elements (e.g. navbar) moving due to scrollbars. - $('#fancybox-style-noscroll').remove(); - } else { - // Prevent fixed positioned elements (e.g. navbar) moving due to scrollbars. - if (!$('#fancybox-style-noscroll').length && document.body.scrollHeight > window.innerHeight) { - $('head').append( - '' - ); - $('body').addClass('compensate-for-scrollbar'); - } - - // Show search modal. - $('body').addClass('searching'); - $('.search-results').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 200); - $('#search-query').focus(); - } - } - - /* --------------------------------------------------------------------------- - * Change Theme Mode (0: Day, 1: Night, 2: Auto). - * --------------------------------------------------------------------------- */ - - function canChangeTheme() { - // If the theme changer component is present, then user is allowed to change the theme variation. - return $('.js-theme-selector').length; - } - - function getThemeMode() { - return parseInt(localStorage.getItem('dark_mode') || 2); - } - - function changeThemeModeClick(newMode) { - console.info('Request to set theme.'); - if (!canChangeTheme()) { - console.info('Cannot set theme - admin disabled theme selector.'); - return; - } - let isDarkTheme; - switch (newMode) { - case 0: - localStorage.setItem('dark_mode', '1'); - isDarkTheme = 1; - console.info('User changed theme variation to Dark.'); - showActiveTheme(0); - break; - case 1: - localStorage.setItem('dark_mode', '2'); - if (window.matchMedia('(prefers-color-scheme: dark)').matches) { - // The visitor prefers dark themes and switching to the dark variation is allowed by admin. - isDarkTheme = 1; - } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { - // The visitor prefers light themes and switching to the dark variation is allowed by admin. - isDarkTheme = 0; - } else { - isDarkTheme = isSiteThemeDark; // Use the site's default theme variation based on `light` in the theme file. - } - console.info('User changed theme variation to Auto.'); - showActiveTheme(1); - break; - default: - localStorage.setItem('dark_mode', '0'); - isDarkTheme = 0; - console.info('User changed theme variation to Light.'); - showActiveTheme(2); - break; - } - renderThemeVariation(isDarkTheme); - } - - function showActiveTheme(mode){ - switch (mode) { - case 0: - // Dark. - $('.js-set-theme-light').removeClass('dropdown-item-active'); - $('.js-set-theme-dark').addClass('dropdown-item-active'); - $('.js-set-theme-auto').removeClass('dropdown-item-active'); - break; - case 1: - // Auto. - $('.js-set-theme-light').removeClass('dropdown-item-active'); - $('.js-set-theme-dark').removeClass('dropdown-item-active'); - $('.js-set-theme-auto').addClass('dropdown-item-active'); - break; - default: - // Light. - $('.js-set-theme-light').addClass('dropdown-item-active'); - $('.js-set-theme-dark').removeClass('dropdown-item-active'); - $('.js-set-theme-auto').removeClass('dropdown-item-active'); - break; - } - } - - function getThemeVariation() { - if (!canChangeTheme()) { - return isSiteThemeDark; // Use the site's default theme variation based on `light` in the theme file. - } - let currentThemeMode = getThemeMode(); - let isDarkTheme; - switch (currentThemeMode) { - case 0: - isDarkTheme = 0; - break; - case 1: - isDarkTheme = 1; - break; - default: - if (window.matchMedia('(prefers-color-scheme: dark)').matches) { - // The visitor prefers dark themes and switching to the dark variation is allowed by admin. - isDarkTheme = 1; - } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { - // The visitor prefers light themes and switching to the dark variation is allowed by admin. - isDarkTheme = 0; - } else { - isDarkTheme = isSiteThemeDark; // Use the site's default theme variation based on `light` in the theme file. - } - break; - } - return isDarkTheme; - } - - /** - * Render theme variation (day or night). - * - * @param {int} isDarkTheme - TODO: convert to boolean. - * @param {boolean} init - * @returns {undefined} - */ - function renderThemeVariation(isDarkTheme, init = false) { - // Is code highlighting enabled in site config? - const codeHlEnabled = $('link[title=hl-light]').length > 0; - const codeHlLight = $('link[title=hl-light]')[0]; - const codeHlDark = $('link[title=hl-dark]')[0]; - const diagramEnabled = $('script[title=mermaid]').length > 0; - - // Check if re-render required. - if (!init) { - // If request to render light when light variation already rendered, return. - // If request to render dark when dark variation already rendered, return. - if ((isDarkTheme === 0 && !$('body').hasClass('dark')) || (isDarkTheme === 1 && $('body').hasClass('dark'))) { - return; - } - } - - if (isDarkTheme === 0) { - if (!init) { - // Only fade in the page when changing the theme variation. - $('body').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 500); - } - $('body').removeClass('dark'); - if (codeHlEnabled) { - codeHlLight.disabled = false; - codeHlDark.disabled = true; - } - if (diagramEnabled) { - if (init) { - mermaid.initialize({theme: 'default', securityLevel: 'loose'}); - } else { - // Have to reload to re-initialise Mermaid with the new theme and re-parse the Mermaid code blocks. - location.reload(); - } - } - } else if (isDarkTheme === 1) { - if (!init) { - // Only fade in the page when changing the theme variation. - $('body').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 500); - } - $('body').addClass('dark'); - if (codeHlEnabled) { - codeHlLight.disabled = true; - codeHlDark.disabled = false; - } - if (diagramEnabled) { - if (init) { - mermaid.initialize({theme: 'dark', securityLevel: 'loose'}); - } else { - // Have to reload to re-initialise Mermaid with the new theme and re-parse the Mermaid code blocks. - location.reload(); - } - } - } - } - - function initThemeVariation() { - // If theme changer component present, set its icon according to the theme mode (day, night, or auto). - if (canChangeTheme) { - let themeMode = getThemeMode(); - switch (themeMode) { - case 0: - showActiveTheme(2); - console.info('Initialize theme variation to Light.'); - break; - case 1: - showActiveTheme(0); - console.info('Initialize theme variation to Dark.'); - break; - default: - showActiveTheme(1); - console.info('Initialize theme variation to Auto.'); - break; - } - } - // Render the day or night theme. - let isDarkTheme = getThemeVariation(); - renderThemeVariation(isDarkTheme, true); - } - - /* --------------------------------------------------------------------------- - * Normalize Bootstrap Carousel Slide Heights. - * --------------------------------------------------------------------------- */ - - function normalizeCarouselSlideHeights() { - $('.carousel').each(function () { - // Get carousel slides. - let items = $('.carousel-item', this); - // 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()); - items.css('min-height', maxHeight + 'px'); - }) - } - - /* --------------------------------------------------------------------------- - * Fix Hugo's Goldmark output and Mermaid code blocks. +/* --------------------------------------------------------------------------- + * GitHub API. * --------------------------------------------------------------------------- */ - /** - * Fix Hugo's Goldmark output. - */ - function fixHugoOutput() { - // Fix Goldmark table of contents. - // - Must be performed prior to initializing ScrollSpy. - $('#TableOfContents').addClass('nav flex-column'); - $('#TableOfContents li').addClass('nav-item'); - $('#TableOfContents li a').addClass('nav-link'); - - // Fix Goldmark task lists (remove bullet points). - $("input[type='checkbox'][disabled]").parents('ul').addClass('task-list'); - } - - /** - * Fix Mermaid.js clash with Highlight.js. - * Refactor Mermaid code blocks as divs to prevent Highlight parsing them and enable Mermaid to parse them. - */ - function fixMermaid() { - let mermaids = []; - [].push.apply(mermaids, document.getElementsByClassName('language-mermaid')); - for (let i = 0; i < mermaids.length; i++) { - $(mermaids[i]).unwrap('pre'); // Remove
 wrapper.
-      $(mermaids[i]).replaceWith(function () {
-        // Convert  block to 
and add `mermaid` class so that Mermaid will parse it. - return $("
").append($(this).contents()).addClass('mermaid'); - }); - } - } - - /* --------------------------------------------------------------------------- - * On document ready. - * --------------------------------------------------------------------------- */ - - $(document).ready(function () { - fixHugoOutput(); - fixMermaid(); - - // Initialise code highlighting if enabled for this page. - // Note: this block should be processed after the Mermaid code-->div conversion. - if (code_highlighting) { - hljs.initHighlighting(); - } - - // Initialize theme variation. - initThemeVariation(); - - // Change theme mode. - $('.js-set-theme-light').click(function (e) { - e.preventDefault(); - changeThemeModeClick(2); - }); - $('.js-set-theme-dark').click(function (e) { - e.preventDefault(); - changeThemeModeClick(0); - }); - $('.js-set-theme-auto').click(function (e) { - e.preventDefault(); - changeThemeModeClick(1); - }); - - // 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.addListener((e) => { - if (!canChangeTheme()) { - // Changing theme variation is not allowed by admin. - return; - } - const darkModeOn = e.matches; - console.log(`OS dark mode preference changed to ${darkModeOn ? '🌒 on' : '☀️ off'}.`); - let currentThemeVariation = parseInt(localStorage.getItem('dark_mode') || 2); - let isDarkTheme; - if (currentThemeVariation === 2) { - if (window.matchMedia('(prefers-color-scheme: dark)').matches) { - // The visitor prefers dark themes. - isDarkTheme = 1; - } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { - // The visitor prefers light themes. - isDarkTheme = 0; - } else { - // The visitor does not have a day or night preference, so use the theme's default setting. - isDarkTheme = isSiteThemeDark; - } - renderThemeVariation(isDarkTheme); - } - }); +function printLatestRelease(selector, repo) { + $.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); }); +} - /* --------------------------------------------------------------------------- - * On window loaded. - * --------------------------------------------------------------------------- */ +/* --------------------------------------------------------------------------- +* Toggle search dialog. +* --------------------------------------------------------------------------- */ - $(window).on('load', function () { - // Filter projects. - $('.projects-container').each(function (index, container) { - let $container = $(container); - let $section = $container.closest('section'); - let layout; - if ($section.find('.isotope').hasClass('js-layout-row')) { - layout = 'fitRows'; +function toggleSearchDialog() { + if ($('body').hasClass('searching')) { + // Clear search query and hide search modal. + $('[id=search-query]').blur(); + $('body').removeClass('searching compensate-for-scrollbar'); + + // Remove search query params from URL as user has finished searching. + removeQueryParamsFromUrl(); + + // Prevent fixed positioned elements (e.g. navbar) moving due to scrollbars. + $('#fancybox-style-noscroll').remove(); + } else { + // Prevent fixed positioned elements (e.g. navbar) moving due to scrollbars. + if (!$('#fancybox-style-noscroll').length && document.body.scrollHeight > window.innerHeight) { + $('head').append( + '' + ); + $('body').addClass('compensate-for-scrollbar'); + } + + // Show search modal. + $('body').addClass('searching'); + $('.search-results').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 200); + $('#search-query').focus(); + } +} + +/* --------------------------------------------------------------------------- +* Change Theme Mode (0: Day, 1: Night, 2: Auto). +* --------------------------------------------------------------------------- */ + +function canChangeTheme() { + // If the theme changer component is present, then user is allowed to change the theme variation. + return $('.js-theme-selector').length; +} + +function getThemeMode() { + return parseInt(localStorage.getItem('dark_mode') || 2); +} + +function changeThemeModeClick(newMode) { + console.info('Request to set theme.'); + if (!canChangeTheme()) { + console.info('Cannot set theme - admin disabled theme selector.'); + return; + } + let isDarkTheme; + switch (newMode) { + case 0: + localStorage.setItem('dark_mode', '1'); + isDarkTheme = 1; + console.info('User changed theme variation to Dark.'); + showActiveTheme(0); + break; + case 1: + localStorage.setItem('dark_mode', '2'); + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + // The visitor prefers dark themes and switching to the dark variation is allowed by admin. + isDarkTheme = 1; + } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { + // The visitor prefers light themes and switching to the dark variation is allowed by admin. + isDarkTheme = 0; } else { - layout = 'masonry'; + isDarkTheme = isSiteThemeDark; // Use the site's default theme variation based on `light` in the theme file. } + console.info('User changed theme variation to Auto.'); + showActiveTheme(1); + break; + default: + localStorage.setItem('dark_mode', '0'); + isDarkTheme = 0; + console.info('User changed theme variation to Light.'); + showActiveTheme(2); + break; + } + renderThemeVariation(isDarkTheme); +} - $container.imagesLoaded(function () { - // Initialize Isotope after all images have loaded. - $container.isotope({ - itemSelector: '.isotope-item', - layoutMode: layout, - masonry: { - gutter: 20 - }, - filter: $section.find('.default-project-filter').text() - }); +function showActiveTheme(mode){ + switch (mode) { + case 0: + // Dark. + $('.js-set-theme-light').removeClass('dropdown-item-active'); + $('.js-set-theme-dark').addClass('dropdown-item-active'); + $('.js-set-theme-auto').removeClass('dropdown-item-active'); + break; + case 1: + // Auto. + $('.js-set-theme-light').removeClass('dropdown-item-active'); + $('.js-set-theme-dark').removeClass('dropdown-item-active'); + $('.js-set-theme-auto').addClass('dropdown-item-active'); + break; + default: + // Light. + $('.js-set-theme-light').addClass('dropdown-item-active'); + $('.js-set-theme-dark').removeClass('dropdown-item-active'); + $('.js-set-theme-auto').removeClass('dropdown-item-active'); + break; + } +} - // Filter items when filter link is clicked. - $section.find('.project-filters a').click(function () { - let selector = $(this).attr('data-filter'); - $container.isotope({filter: selector}); - $(this).removeClass('active').addClass('active').siblings().removeClass('active all'); - return false; - }); +function getThemeVariation() { + if (!canChangeTheme()) { + return isSiteThemeDark; // Use the site's default theme variation based on `light` in the theme file. + } + let currentThemeMode = getThemeMode(); + let isDarkTheme; + switch (currentThemeMode) { + case 0: + isDarkTheme = 0; + break; + case 1: + isDarkTheme = 1; + break; + default: + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + // The visitor prefers dark themes and switching to the dark variation is allowed by admin. + isDarkTheme = 1; + } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { + // The visitor prefers light themes and switching to the dark variation is allowed by admin. + isDarkTheme = 0; + } else { + isDarkTheme = isSiteThemeDark; // Use the site's default theme variation based on `light` in the theme file. + } + break; + } + return isDarkTheme; +} - // If window hash is set, scroll to hash. - // Placing this within `imagesLoaded` prevents scrolling to the wrong location due to dynamic image loading - // affecting page layout and position of the target anchor ID. - // Note: If there are multiple project widgets on a page, ideally only perform this once after images - // from *all* project widgets have finished loading. - if (window.location.hash) { - scrollToAnchor(); - } - }); - }); +/** + * Render theme variation (day or night). + * + * @param {int} isDarkTheme - TODO: convert to boolean. + * @param {boolean} init + * @returns {undefined} + */ +function renderThemeVariation(isDarkTheme, init = false) { + // Is code highlighting enabled in site config? + const codeHlEnabled = $('link[title=hl-light]').length > 0; + const codeHlLight = $('link[title=hl-light]')[0]; + const codeHlDark = $('link[title=hl-dark]')[0]; + const diagramEnabled = $('script[title=mermaid]').length > 0; - // Enable publication filter for publication index page. - if ($('.pub-filters-select')) { - filter_publications(); - // Useful for changing hash manually (e.g. in development): - // window.addEventListener('hashchange', filter_publications, false); + // Check if re-render required. + if (!init) { + // If request to render light when light variation already rendered, return. + // If request to render dark when dark variation already rendered, return. + if ((isDarkTheme === 0 && !$('body').hasClass('dark')) || (isDarkTheme === 1 && $('body').hasClass('dark'))) { + return; } + } - // Scroll to top of page. - $('.back-to-top').click(function (event) { - event.preventDefault(); - $('html, body').animate({ - 'scrollTop': 0 - }, 800, function () { - window.location.hash = ""; - }); - }); - - // Load citation modal on 'Cite' click. - $('.js-cite-modal').click(function (e) { - e.preventDefault(); - let filename = $(this).attr('data-filename'); - 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); - } else { - $('.js-download-cite').attr('href', filename); - } - }); - modal.modal('show'); - }); - - // Copy citation text on 'Copy' click. - $('.js-copy-cite').click(function (e) { - e.preventDefault(); - // Get selection. - let range = document.createRange(); - let code_node = document.querySelector('#modal .modal-body'); - range.selectNode(code_node); - window.getSelection().addRange(range); - try { - // Execute the copy command. - document.execCommand('copy'); - } catch (e) { - console.log('Error: citation copy failed.'); + if (isDarkTheme === 0) { + if (!init) { + // Only fade in the page when changing the theme variation. + $('body').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 500); + } + $('body').removeClass('dark'); + if (codeHlEnabled) { + codeHlLight.disabled = false; + codeHlDark.disabled = true; + } + if (diagramEnabled) { + if (init) { + mermaid.initialize({theme: 'default', securityLevel: 'loose'}); + } else { + // Have to reload to re-initialise Mermaid with the new theme and re-parse the Mermaid code blocks. + location.reload(); } - // Remove selection. - window.getSelection().removeRange(range); - }); - - // Initialise Google Maps if necessary. - initMap(); - - // Print latest version of GitHub projects. - let githubReleaseSelector = '.js-github-release'; - if ($(githubReleaseSelector).length > 0) - printLatestRelease(githubReleaseSelector, $(githubReleaseSelector).data('repo')); - - // On search icon click toggle search dialog. - $('.js-search').click(function (e) { - e.preventDefault(); - toggleSearchDialog(); - }); - $(document).on('keydown', function (e) { - if (e.which == 27) { - // `Esc` key pressed. - if ($('body').hasClass('searching')) { - toggleSearchDialog(); - } - } else if (e.which == 191 && e.shiftKey == false && !$('input,textarea').is(':focus')) { - // `/` key pressed outside of text input. - e.preventDefault(); - toggleSearchDialog(); + } + } else if (isDarkTheme === 1) { + if (!init) { + // Only fade in the page when changing the theme variation. + $('body').css({opacity: 0, visibility: 'visible'}).animate({opacity: 1}, 500); + } + $('body').addClass('dark'); + if (codeHlEnabled) { + codeHlLight.disabled = true; + codeHlDark.disabled = false; + } + if (diagramEnabled) { + if (init) { + mermaid.initialize({theme: 'dark', securityLevel: 'loose'}); + } else { + // Have to reload to re-initialise Mermaid with the new theme and re-parse the Mermaid code blocks. + location.reload(); } - }); + } + } +} +function initThemeVariation() { + // If theme changer component present, set its icon according to the theme mode (day, night, or auto). + if (canChangeTheme) { + let themeMode = getThemeMode(); + switch (themeMode) { + case 0: + showActiveTheme(2); + console.info('Initialize theme variation to Light.'); + break; + case 1: + showActiveTheme(0); + console.info('Initialize theme variation to Dark.'); + break; + default: + showActiveTheme(1); + console.info('Initialize theme variation to Auto.'); + break; + } + } + // Render the day or night theme. + let isDarkTheme = getThemeVariation(); + renderThemeVariation(isDarkTheme, true); +} + +/* --------------------------------------------------------------------------- +* Normalize Bootstrap Carousel Slide Heights. +* --------------------------------------------------------------------------- */ + +function normalizeCarouselSlideHeights() { + $('.carousel').each(function () { + // Get carousel slides. + let items = $('.carousel-item', this); + // 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()); + items.css('min-height', maxHeight + 'px'); + }) +} + +/* --------------------------------------------------------------------------- +* Fix Hugo's Goldmark output and Mermaid code blocks. +* --------------------------------------------------------------------------- */ + +/** + * Fix Hugo's Goldmark output. + */ +function fixHugoOutput() { + // Fix Goldmark table of contents. + // - Must be performed prior to initializing ScrollSpy. + $('#TableOfContents').addClass('nav flex-column'); + $('#TableOfContents li').addClass('nav-item'); + $('#TableOfContents li a').addClass('nav-link'); + + // Fix Goldmark task lists (remove bullet points). + $("input[type='checkbox'][disabled]").parents('ul').addClass('task-list'); +} + +/** + * Fix Mermaid.js clash with Highlight.js. + * Refactor Mermaid code blocks as divs to prevent Highlight parsing them and enable Mermaid to parse them. + */ +function fixMermaid() { + let mermaids = []; + [].push.apply(mermaids, document.getElementsByClassName('language-mermaid')); + for (let i = 0; i < mermaids.length; i++) { + $(mermaids[i]).unwrap('pre'); // Remove
 wrapper.
+    $(mermaids[i]).replaceWith(function () {
+      // Convert  block to 
and add `mermaid` class so that Mermaid will parse it. + return $("
").append($(this).contents()).addClass('mermaid'); + }); + } +} + +/* --------------------------------------------------------------------------- + * On document ready. + * --------------------------------------------------------------------------- */ + +$(document).ready(function () { + fixHugoOutput(); + fixMermaid(); + + // Initialise code highlighting if enabled for this page. + // Note: this block should be processed after the Mermaid code-->div conversion. + if (code_highlighting) { + hljs.initHighlighting(); + } + + // Initialize theme variation. + initThemeVariation(); + + // Change theme mode. + $('.js-set-theme-light').click(function (e) { + e.preventDefault(); + changeThemeModeClick(2); + }); + $('.js-set-theme-dark').click(function (e) { + e.preventDefault(); + changeThemeModeClick(0); + }); + $('.js-set-theme-auto').click(function (e) { + e.preventDefault(); + changeThemeModeClick(1); }); - // Normalize Bootstrap carousel slide heights. - $(window).on('load resize orientationchange', normalizeCarouselSlideHeights); + // 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.addListener((e) => { + if (!canChangeTheme()) { + // Changing theme variation is not allowed by admin. + return; + } + const darkModeOn = e.matches; + console.log(`OS dark mode preference changed to ${darkModeOn ? '🌒 on' : '☀️ off'}.`); + let currentThemeVariation = parseInt(localStorage.getItem('dark_mode') || 2); + let isDarkTheme; + if (currentThemeVariation === 2) { + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + // The visitor prefers dark themes. + isDarkTheme = 1; + } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { + // The visitor prefers light themes. + isDarkTheme = 0; + } else { + // The visitor does not have a day or night preference, so use the theme's default setting. + isDarkTheme = isSiteThemeDark; + } + renderThemeVariation(isDarkTheme); + } + }); +}); - // Automatic main menu dropdowns on mouse over. - $('body').on('mouseenter mouseleave', '.dropdown', function (e) { - var dropdown = $(e.target).closest('.dropdown'); - var menu = $('.dropdown-menu', dropdown); - dropdown.addClass('show'); - menu.addClass('show'); - setTimeout(function () { - dropdown[dropdown.is(':hover') ? 'addClass' : 'removeClass']('show'); - menu[dropdown.is(':hover') ? 'addClass' : 'removeClass']('show'); - }, 300); +/* --------------------------------------------------------------------------- + * On window loaded. + * --------------------------------------------------------------------------- */ - // Re-initialize Scrollspy with dynamic navbar height offset. - fixScrollspy(); +$(window).on('load', function () { + // Filter projects. + $('.projects-container').each(function (index, container) { + let $container = $(container); + let $section = $container.closest('section'); + let layout; + if ($section.find('.isotope').hasClass('js-layout-row')) { + layout = 'fitRows'; + } else { + layout = 'masonry'; + } - if (window.location.hash) { - // When accessing homepage from another page and `#top` hash is set, show top of page (no hash). - if (window.location.hash == "#top") { - window.location.hash = "" - } else if (!$('.projects-container').length) { - // If URL contains a hash and there are no dynamically loaded images on the page, - // immediately scroll to target ID taking into account responsive offset. - // Otherwise, wait for `imagesLoaded()` to complete before scrolling to hash to prevent scrolling to wrong - // location. + $container.imagesLoaded(function () { + // Initialize Isotope after all images have loaded. + $container.isotope({ + itemSelector: '.isotope-item', + layoutMode: layout, + masonry: { + gutter: 20 + }, + filter: $section.find('.default-project-filter').text() + }); + + // Filter items when filter link is clicked. + $section.find('.project-filters a').click(function () { + let selector = $(this).attr('data-filter'); + $container.isotope({filter: selector}); + $(this).removeClass('active').addClass('active').siblings().removeClass('active all'); + return false; + }); + + // If window hash is set, scroll to hash. + // Placing this within `imagesLoaded` prevents scrolling to the wrong location due to dynamic image loading + // affecting page layout and position of the target anchor ID. + // Note: If there are multiple project widgets on a page, ideally only perform this once after images + // from *all* project widgets have finished loading. + if (window.location.hash) { scrollToAnchor(); } - } - - // Call `fixScrollspy` when window is resized. - let resizeTimer; - $(window).resize(function () { - clearTimeout(resizeTimer); - resizeTimer = setTimeout(fixScrollspy, 200); }); }); + // Enable publication filter for publication index page. + if ($('.pub-filters-select')) { + filter_publications(); + // Useful for changing hash manually (e.g. in development): + // window.addEventListener('hashchange', filter_publications, false); + } + + // Scroll to top of page. + $('.back-to-top').click(function (event) { + event.preventDefault(); + $('html, body').animate({ + 'scrollTop': 0 + }, 800, function () { + window.location.hash = ""; + }); + }); + + // Load citation modal on 'Cite' click. + $('.js-cite-modal').click(function (e) { + e.preventDefault(); + let filename = $(this).attr('data-filename'); + 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); + } else { + $('.js-download-cite').attr('href', filename); + } + }); + modal.modal('show'); + }); + + // Copy citation text on 'Copy' click. + $('.js-copy-cite').click(function (e) { + e.preventDefault(); + // Get selection. + let range = document.createRange(); + let code_node = document.querySelector('#modal .modal-body'); + range.selectNode(code_node); + window.getSelection().addRange(range); + try { + // Execute the copy command. + document.execCommand('copy'); + } catch (e) { + console.log('Error: citation copy failed.'); + } + // Remove selection. + window.getSelection().removeRange(range); + }); + + // Initialise Google Maps if necessary. + initMap(); + + // Print latest version of GitHub projects. + let githubReleaseSelector = '.js-github-release'; + if ($(githubReleaseSelector).length > 0) + printLatestRelease(githubReleaseSelector, $(githubReleaseSelector).data('repo')); + + // On search icon click toggle search dialog. + $('.js-search').click(function (e) { + e.preventDefault(); + toggleSearchDialog(); + }); + $(document).on('keydown', function (e) { + if (e.which == 27) { + // `Esc` key pressed. + if ($('body').hasClass('searching')) { + toggleSearchDialog(); + } + } else if (e.which == 191 && e.shiftKey == false && !$('input,textarea').is(':focus')) { + // `/` key pressed outside of text input. + e.preventDefault(); + toggleSearchDialog(); + } + }); + +}); + +// Normalize Bootstrap carousel slide heights. +$(window).on('load resize orientationchange', normalizeCarouselSlideHeights); + +// Automatic main menu dropdowns on mouse over. +$('body').on('mouseenter mouseleave', '.dropdown', function (e) { + var dropdown = $(e.target).closest('.dropdown'); + var menu = $('.dropdown-menu', dropdown); + dropdown.addClass('show'); + menu.addClass('show'); + setTimeout(function () { + dropdown[dropdown.is(':hover') ? 'addClass' : 'removeClass']('show'); + menu[dropdown.is(':hover') ? 'addClass' : 'removeClass']('show'); + }, 300); + + // Re-initialize Scrollspy with dynamic navbar height offset. + fixScrollspy(); + + if (window.location.hash) { + // When accessing homepage from another page and `#top` hash is set, show top of page (no hash). + if (window.location.hash == "#top") { + window.location.hash = "" + } else if (!$('.projects-container').length) { + // If URL contains a hash and there are no dynamically loaded images on the page, + // immediately scroll to target ID taking into account responsive offset. + // Otherwise, wait for `imagesLoaded()` to complete before scrolling to hash to prevent scrolling to wrong + // location. + scrollToAnchor(); + } + } + + // Call `fixScrollspy` when window is resized. + let resizeTimer; + $(window).resize(function () { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(fixScrollspy, 200); + }); +}); + })(jQuery); diff --git a/themes/academic/assets/js/vendor/fuse.min.js b/themes/academic/assets/js/vendor/fuse.min.js new file mode 100644 index 0000000..0c7ae7d --- /dev/null +++ b/themes/academic/assets/js/vendor/fuse.min.js @@ -0,0 +1,9 @@ +/*! + * Fuse.js v3.2.1 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Fuse",[],t):"object"==typeof exports?exports.Fuse=t():e.Fuse=t()}(this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};return t.m=e,t.c=r,t.i=function(e){return e},t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=8)}([function(e,t,r){"use strict";e.exports=function(e){return Array.isArray?Array.isArray(e):"[object Array]"===Object.prototype.toString.call(e)}},function(e,t,r){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var r=0;rr)return i(e,this.pattern,n);var o=this.options,s=o.location,c=o.distance,h=o.threshold,l=o.findAllMatches,u=o.minMatchCharLength;return a(e,this.pattern,this.patternAlphabet,{location:s,distance:c,threshold:h,findAllMatches:l,minMatchCharLength:u})}}]),e}();e.exports=c},function(e,t,r){"use strict";var n=r(0),o=function e(t,r,o){if(r){var i=r.indexOf("."),a=r,s=null;-1!==i&&(a=r.slice(0,i),s=r.slice(i+1));var c=t[a];if(null!==c&&void 0!==c)if(s||"string"!=typeof c&&"number"!=typeof c)if(n(c))for(var h=0,l=c.length;h0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,r=[],n=-1,o=-1,i=0,a=e.length;i=t&&r.push([n,o]),n=-1)}return e[i-1]&&i-n>=t&&r.push([n,i-1]),r}},function(e,t,r){"use strict";e.exports=function(e){for(var t={},r=e.length,n=0;n2&&void 0!==arguments[2]?arguments[2]:/ +/g,n=new RegExp(t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&").replace(r,"|")),o=e.match(n),i=!!o,a=[];if(i)for(var s=0,c=o.length;s=P;T-=1){var E=T-1,K=r[e.charAt(E)];if(K&&(S[E]=1),z[T]=(z[T+1]<<1|1)&K,0!==I&&(z[T]|=(L[T+1]|L[T])<<1|1|L[T+1]),z[T]&C&&(w=n(t,{errors:I,currentLocation:E,expectedLocation:g,distance:h}))<=m){if(m=w,(k=E)<=g)break;P=Math.max(1,2*g-k)}}if(n(t,{errors:I+1,currentLocation:g,expectedLocation:g,distance:h})>m)break;L=z}return{isMatch:k>=0,score:0===w?.001:w,matchedIndices:o(S,p)}}},function(e,t,r){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:"",t=[];if(this.options.tokenize)for(var r=e.split(this.options.tokenSeparator),n=0,o=r.length;n0&&void 0!==arguments[0]?arguments[0]:[],t=arguments[1],r=this.list,n={},o=[];if("string"==typeof r[0]){for(var i=0,a=r.length;i1)throw new Error("Key weight has to be > 0 and <= 1");d=d.name}else s[d]={weight:1};this._analyze({key:d,value:this.options.getFn(l,d),record:l,index:c},{resultMap:n,results:o,tokenSearchers:e,fullSearcher:t})}return{weights:s,results:o}}},{key:"_analyze",value:function(e,t){var r=e.key,n=e.arrayIndex,o=void 0===n?-1:n,i=e.value,a=e.record,c=e.index,h=t.tokenSearchers,l=void 0===h?[]:h,u=t.fullSearcher,f=void 0===u?[]:u,d=t.resultMap,v=void 0===d?{}:d,p=t.results,g=void 0===p?[]:p;if(void 0!==i&&null!==i){var y=!1,m=-1,k=0;if("string"==typeof i){this._log("\nKey: "+(""===r?"-":r));var x=f.search(i);if(this._log('Full text: "'+i+'", score: '+x.score),this.options.tokenize){for(var S=i.split(this.options.tokenSeparator),M=[],b=0;b-1&&(P=(P+m)/2),this._log("Score average:",P);var j=!this.options.tokenize||!this.options.matchAllTokens||k>=l.length;if(this._log("\nCheck Matches: "+j),(y||x.isMatch)&&j){var z=v[c];z?z.output.push({key:r,arrayIndex:o,value:i,score:P,matchedIndices:x.matchedIndices}):(v[c]={item:a,output:[{key:r,arrayIndex:o,value:i,score:P,matchedIndices:x.matchedIndices}]},g.push(v[c]))}}else if(s(i))for(var T=0,E=i.length;T-1&&(a.arrayIndex=i.arrayIndex),t.matches.push(a)}}}),this.options.includeScore&&r.push(function(e,t){t.score=e.score});for(var n=0,o=e.length;n1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:5e3;t(this,e),this.ctx=n,this.iframes=r,this.exclude=i,this.iframesTimeout=o}return n(e,[{key:"getContexts",value:function(){var e=[];return(void 0!==this.ctx&&this.ctx?NodeList.prototype.isPrototypeOf(this.ctx)?Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?this.ctx:"string"==typeof this.ctx?Array.prototype.slice.call(document.querySelectorAll(this.ctx)):[this.ctx]:[]).forEach(function(t){var n=e.filter(function(e){return e.contains(t)}).length>0;-1!==e.indexOf(t)||n||e.push(t)}),e}},{key:"getIframeContents",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){},r=void 0;try{var i=e.contentWindow;if(r=i.document,!i||!r)throw new Error("iframe inaccessible")}catch(e){n()}r&&t(r)}},{key:"isIframeBlank",value:function(e){var t=e.getAttribute("src").trim();return"about:blank"===e.contentWindow.location.href&&"about:blank"!==t&&t}},{key:"observeIframeLoad",value:function(e,t,n){var r=this,i=!1,o=null,a=function a(){if(!i){i=!0,clearTimeout(o);try{r.isIframeBlank(e)||(e.removeEventListener("load",a),r.getIframeContents(e,t,n))}catch(e){n()}}};e.addEventListener("load",a),o=setTimeout(a,this.iframesTimeout)}},{key:"onIframeReady",value:function(e,t,n){try{"complete"===e.contentWindow.document.readyState?this.isIframeBlank(e)?this.observeIframeLoad(e,t,n):this.getIframeContents(e,t,n):this.observeIframeLoad(e,t,n)}catch(e){n()}}},{key:"waitForIframes",value:function(e,t){var n=this,r=0;this.forEachIframe(e,function(){return!0},function(e){r++,n.waitForIframes(e.querySelector("html"),function(){--r||t()})},function(e){e||t()})}},{key:"forEachIframe",value:function(t,n,r){var i=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},a=t.querySelectorAll("iframe"),s=a.length,c=0;a=Array.prototype.slice.call(a);var u=function(){--s<=0&&o(c)};s||u(),a.forEach(function(t){e.matches(t,i.exclude)?u():i.onIframeReady(t,function(e){n(t)&&(c++,r(e)),u()},u)})}},{key:"createIterator",value:function(e,t,n){return document.createNodeIterator(e,t,n,!1)}},{key:"createInstanceOnIframe",value:function(t){return new e(t.querySelector("html"),this.iframes)}},{key:"compareNodeIframe",value:function(e,t,n){if(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_PRECEDING){if(null===t)return!0;if(t.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)return!0}return!1}},{key:"getIteratorNode",value:function(e){var t=e.previousNode();return{prevNode:t,node:null===t?e.nextNode():e.nextNode()&&e.nextNode()}}},{key:"checkIframeFilter",value:function(e,t,n,r){var i=!1,o=!1;return r.forEach(function(e,t){e.val===n&&(i=t,o=e.handled)}),this.compareNodeIframe(e,t,n)?(!1!==i||o?!1===i||o||(r[i].handled=!0):r.push({val:n,handled:!0}),!0):(!1===i&&r.push({val:n,handled:!1}),!1)}},{key:"handleOpenIframes",value:function(e,t,n,r){var i=this;e.forEach(function(e){e.handled||i.getIframeContents(e.val,function(e){i.createInstanceOnIframe(e).forEachNode(t,n,r)})})}},{key:"iterateThroughNodes",value:function(e,t,n,r,i){for(var o,a=this,s=this.createIterator(t,e,r),c=[],u=[],l=void 0,h=void 0;void 0,o=a.getIteratorNode(s),h=o.prevNode,l=o.node;)this.iframes&&this.forEachIframe(t,function(e){return a.checkIframeFilter(l,h,e,c)},function(t){a.createInstanceOnIframe(t).forEachNode(e,function(e){return u.push(e)},r)}),u.push(l);u.forEach(function(e){n(e)}),this.iframes&&this.handleOpenIframes(c,e,n,r),i()}},{key:"forEachNode",value:function(e,t,n){var r=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},o=this.getContexts(),a=o.length;a||i(),o.forEach(function(o){var s=function(){r.iterateThroughNodes(e,o,t,n,function(){--a<=0&&i()})};r.iframes?r.waitForIframes(o,s):s()})}}],[{key:"matches",value:function(e,t){var n="string"==typeof t?[t]:t,r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(r){var i=!1;return n.every(function(t){return!r.call(e,t)||(i=!0,!1)}),i}return!1}}]),e}(),o=function(){function o(e){t(this,o),this.ctx=e,this.ie=!1;var n=window.navigator.userAgent;(n.indexOf("MSIE")>-1||n.indexOf("Trident")>-1)&&(this.ie=!0)}return n(o,[{key:"log",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"debug",r=this.opt.log;this.opt.debug&&"object"===(void 0===r?"undefined":e(r))&&"function"==typeof r[n]&&r[n]("mark.js: "+t)}},{key:"escapeStr",value:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}},{key:"createRegExp",value:function(e){return"disabled"!==this.opt.wildcards&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),"disabled"!==this.opt.wildcards&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e)}},{key:"createSynonymsRegExp",value:function(e){var t=this.opt.synonyms,n=this.opt.caseSensitive?"":"i",r=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(var i in t)if(t.hasOwnProperty(i)){var o=t[i],a="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(i):this.escapeStr(i),s="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(o):this.escapeStr(o);""!==a&&""!==s&&(e=e.replace(new RegExp("("+this.escapeStr(a)+"|"+this.escapeStr(s)+")","gm"+n),r+"("+this.processSynomyms(a)+"|"+this.processSynomyms(s)+")"+r))}return e}},{key:"processSynomyms",value:function(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}},{key:"setupWildcardsRegExp",value:function(e){return(e=e.replace(/(?:\\)*\?/g,function(e){return"\\"===e.charAt(0)?"?":""})).replace(/(?:\\)*\*/g,function(e){return"\\"===e.charAt(0)?"*":""})}},{key:"createWildcardsRegExp",value:function(e){var t="withSpaces"===this.opt.wildcards;return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}},{key:"setupIgnoreJoinersRegExp",value:function(e){return e.replace(/[^(|)\\]/g,function(e,t,n){var r=n.charAt(t+1);return/[(|)\\]/.test(r)||""===r?e:e+"\0"})}},{key:"createJoinersRegExp",value:function(e){var t=[],n=this.opt.ignorePunctuation;return Array.isArray(n)&&n.length&&t.push(this.escapeStr(n.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join("["+t.join("")+"]*"):e}},{key:"createDiacriticsRegExp",value:function(e){var t=this.opt.caseSensitive?"":"i",n=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"],r=[];return e.split("").forEach(function(i){n.every(function(n){if(-1!==n.indexOf(i)){if(r.indexOf(n)>-1)return!1;e=e.replace(new RegExp("["+n+"]","gm"+t),"["+n+"]"),r.push(n)}return!0})}),e}},{key:"createMergedBlanksRegExp",value:function(e){return e.replace(/[\s]+/gim,"[\\s]+")}},{key:"createAccuracyRegExp",value:function(e){var t=this,n=this.opt.accuracy,r="string"==typeof n?n:n.value,i="";switch(("string"==typeof n?[]:n.limiters).forEach(function(e){i+="|"+t.escapeStr(e)}),r){case"partially":default:return"()("+e+")";case"complementary":return"()([^"+(i="\\s"+(i||this.escapeStr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿")))+"]*"+e+"[^"+i+"]*)";case"exactly":return"(^|\\s"+i+")("+e+")(?=$|\\s"+i+")"}}},{key:"getSeparatedKeywords",value:function(e){var t=this,n=[];return e.forEach(function(e){t.opt.separateWordSearch?e.split(" ").forEach(function(e){e.trim()&&-1===n.indexOf(e)&&n.push(e)}):e.trim()&&-1===n.indexOf(e)&&n.push(e)}),{keywords:n.sort(function(e,t){return t.length-e.length}),length:n.length}}},{key:"isNumeric",value:function(e){return Number(parseFloat(e))==e}},{key:"checkRanges",value:function(e){var t=this;if(!Array.isArray(e)||"[object Object]"!==Object.prototype.toString.call(e[0]))return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];var n=[],r=0;return e.sort(function(e,t){return e.start-t.start}).forEach(function(e){var i=t.callNoMatchOnInvalidRanges(e,r),o=i.start,a=i.end;i.valid&&(e.start=o,e.length=a-o,n.push(e),r=a)}),n}},{key:"callNoMatchOnInvalidRanges",value:function(e,t){var n=void 0,r=void 0,i=!1;return e&&void 0!==e.start?(r=(n=parseInt(e.start,10))+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?i=!0:(this.log("Ignoring invalid or overlapping range: "+JSON.stringify(e)),this.opt.noMatch(e))):(this.log("Ignoring invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:n,end:r,valid:i}}},{key:"checkWhitespaceRanges",value:function(e,t,n){var r=void 0,i=!0,o=n.length,a=t-o,s=parseInt(e.start,10)-a;return(r=(s=s>o?o:s)+parseInt(e.length,10))>o&&(r=o,this.log("End range automatically set to the max value of "+o)),s<0||r-s<0||s>o||r>o?(i=!1,this.log("Invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)):""===n.substring(s,r).replace(/\s+/g,"")&&(i=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:s,end:r,valid:i}}},{key:"getTextNodes",value:function(e){var t=this,n="",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(e){r.push({start:n.length,end:(n+=e.textContent).length,node:e})},function(e){return t.matchesExclude(e.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){e({value:n,nodes:r})})}},{key:"matchesExclude",value:function(e){return i.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}},{key:"wrapRangeInTextNode",value:function(e,t,n){var r=this.opt.element?this.opt.element:"mark",i=e.splitText(t),o=i.splitText(n-t),a=document.createElement(r);return a.setAttribute("data-markjs","true"),this.opt.className&&a.setAttribute("class",this.opt.className),a.textContent=i.textContent,i.parentNode.replaceChild(a,i),o}},{key:"wrapRangeInMappedTextNode",value:function(e,t,n,r,i){var o=this;e.nodes.every(function(a,s){var c=e.nodes[s+1];if(void 0===c||c.start>t){if(!r(a.node))return!1;var u=t-a.start,l=(n>a.end?a.end:n)-a.start,h=e.value.substr(0,a.start),f=e.value.substr(l+a.start);if(a.node=o.wrapRangeInTextNode(a.node,u,l),e.value=h+f,e.nodes.forEach(function(t,n){n>=s&&(e.nodes[n].start>0&&n!==s&&(e.nodes[n].start-=l),e.nodes[n].end-=l)}),n-=l,i(a.node.previousSibling,a.start),!(n>a.end))return!1;t=a.end}return!0})}},{key:"wrapMatches",value:function(e,t,n,r,i){var o=this,a=0===t?0:t+1;this.getTextNodes(function(t){t.nodes.forEach(function(t){t=t.node;for(var i=void 0;null!==(i=e.exec(t.textContent))&&""!==i[a];)if(n(i[a],t)){var s=i.index;if(0!==a)for(var c=1;c
{{ block "main" . }}{{ end }}
+ {{ partial "search" . }} {{ partial "site_js" . }}