diff --git a/.github/workflows/build-test-site.yaml b/.github/workflows/build-test-site.yaml index df5fcc82..494e120f 100644 --- a/.github/workflows/build-test-site.yaml +++ b/.github/workflows/build-test-site.yaml @@ -32,7 +32,7 @@ jobs: run: rm -rf node_modules && yarn install --frozen-lockfile - name: Check for linting errors - run: yarn run lint + run: yarn run lint:js # TODO: also lint styles again once new rule errors resolved # format: # runs-on: ubuntu-latest diff --git a/wowchemy/assets/css/libs/chroma/dracula.css b/wowchemy/assets/css/libs/chroma/dracula.css new file mode 100644 index 00000000..c19577dc --- /dev/null +++ b/wowchemy/assets/css/libs/chroma/dracula.css @@ -0,0 +1,85 @@ +/* Background */ .bg { color: #f8f8f2; background-color: #282a36 } +/* PreWrapper */ .chroma { color: #f8f8f2; background-color: #282a36; } +/* Other */ .chroma .x { } +/* Error */ .chroma .err { } +/* CodeLine */ .chroma .cl { } +/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } +/* LineHighlight */ .chroma .hl { background-color: #ffc } +/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* Line */ .chroma .line { display: flex; } +/* Keyword */ .chroma .k { color: #ff79c6 } +/* KeywordConstant */ .chroma .kc { color: #ff79c6 } +/* KeywordDeclaration */ .chroma .kd { color: #8be9fd; font-style: italic } +/* KeywordNamespace */ .chroma .kn { color: #ff79c6 } +/* KeywordPseudo */ .chroma .kp { color: #ff79c6 } +/* KeywordReserved */ .chroma .kr { color: #ff79c6 } +/* KeywordType */ .chroma .kt { color: #8be9fd } +/* Name */ .chroma .n { } +/* NameAttribute */ .chroma .na { color: #50fa7b } +/* NameBuiltin */ .chroma .nb { color: #8be9fd; font-style: italic } +/* NameBuiltinPseudo */ .chroma .bp { } +/* NameClass */ .chroma .nc { color: #50fa7b } +/* NameConstant */ .chroma .no { } +/* NameDecorator */ .chroma .nd { } +/* NameEntity */ .chroma .ni { } +/* NameException */ .chroma .ne { } +/* NameFunction */ .chroma .nf { color: #50fa7b } +/* NameFunctionMagic */ .chroma .fm { } +/* NameLabel */ .chroma .nl { color: #8be9fd; font-style: italic } +/* NameNamespace */ .chroma .nn { } +/* NameOther */ .chroma .nx { } +/* NameProperty */ .chroma .py { } +/* NameTag */ .chroma .nt { color: #ff79c6 } +/* NameVariable */ .chroma .nv { color: #8be9fd; font-style: italic } +/* NameVariableClass */ .chroma .vc { color: #8be9fd; font-style: italic } +/* NameVariableGlobal */ .chroma .vg { color: #8be9fd; font-style: italic } +/* NameVariableInstance */ .chroma .vi { color: #8be9fd; font-style: italic } +/* NameVariableMagic */ .chroma .vm { } +/* Literal */ .chroma .l { } +/* LiteralDate */ .chroma .ld { } +/* LiteralString */ .chroma .s { color: #f1fa8c } +/* LiteralStringAffix */ .chroma .sa { color: #f1fa8c } +/* LiteralStringBacktick */ .chroma .sb { color: #f1fa8c } +/* LiteralStringChar */ .chroma .sc { color: #f1fa8c } +/* LiteralStringDelimiter */ .chroma .dl { color: #f1fa8c } +/* LiteralStringDoc */ .chroma .sd { color: #f1fa8c } +/* LiteralStringDouble */ .chroma .s2 { color: #f1fa8c } +/* LiteralStringEscape */ .chroma .se { color: #f1fa8c } +/* LiteralStringHeredoc */ .chroma .sh { color: #f1fa8c } +/* LiteralStringInterpol */ .chroma .si { color: #f1fa8c } +/* LiteralStringOther */ .chroma .sx { color: #f1fa8c } +/* LiteralStringRegex */ .chroma .sr { color: #f1fa8c } +/* LiteralStringSingle */ .chroma .s1 { color: #f1fa8c } +/* LiteralStringSymbol */ .chroma .ss { color: #f1fa8c } +/* LiteralNumber */ .chroma .m { color: #bd93f9 } +/* LiteralNumberBin */ .chroma .mb { color: #bd93f9 } +/* LiteralNumberFloat */ .chroma .mf { color: #bd93f9 } +/* LiteralNumberHex */ .chroma .mh { color: #bd93f9 } +/* LiteralNumberInteger */ .chroma .mi { color: #bd93f9 } +/* LiteralNumberIntegerLong */ .chroma .il { color: #bd93f9 } +/* LiteralNumberOct */ .chroma .mo { color: #bd93f9 } +/* Operator */ .chroma .o { color: #ff79c6 } +/* OperatorWord */ .chroma .ow { color: #ff79c6 } +/* Punctuation */ .chroma .p { } +/* Comment */ .chroma .c { color: #6272a4 } +/* CommentHashbang */ .chroma .ch { color: #6272a4 } +/* CommentMultiline */ .chroma .cm { color: #6272a4 } +/* CommentSingle */ .chroma .c1 { color: #6272a4 } +/* CommentSpecial */ .chroma .cs { color: #6272a4 } +/* CommentPreproc */ .chroma .cp { color: #ff79c6 } +/* CommentPreprocFile */ .chroma .cpf { color: #ff79c6 } +/* Generic */ .chroma .g { } +/* GenericDeleted */ .chroma .gd { color: #f55 } +/* GenericEmph */ .chroma .ge { text-decoration: underline } +/* GenericError */ .chroma .gr { } +/* GenericHeading */ .chroma .gh { font-weight: bold } +/* GenericInserted */ .chroma .gi { color: #50fa7b; font-weight: bold } +/* GenericOutput */ .chroma .go { color: #44475a } +/* GenericPrompt */ .chroma .gp { } +/* GenericStrong */ .chroma .gs { } +/* GenericSubheading */ .chroma .gu { font-weight: bold } +/* GenericTraceback */ .chroma .gt { } +/* GenericUnderline */ .chroma .gl { text-decoration: underline } +/* TextWhitespace */ .chroma .w { } diff --git a/wowchemy/assets/css/libs/chroma/github-dark.css b/wowchemy/assets/css/libs/chroma/github-dark.css new file mode 100644 index 00000000..77716777 --- /dev/null +++ b/wowchemy/assets/css/libs/chroma/github-dark.css @@ -0,0 +1,271 @@ +.chroma { + background-color: #0d1117; + color: #fff; +} +.chroma .nn { + color: #fff; +} +.chroma .err { + color: #f85149; +} +.chroma .p { + color: #c9d1d9; +} +.chroma .lntd { + border: 0; + margin: 0; + padding: 0; + vertical-align: top; +} +.chroma .lntable { + width: auto; + border: 0; + margin: 0; + display: block; + padding: 0; + overflow: auto; + border-spacing: 0; +} +.chroma .hl { + width: 100%; + display: block; + background-color: rgba(110,118,129,0.1); +} +.chroma .lnt { + color: #8b949e; + padding: 0 0.4em 0 0.4em; + font-size: 0.875rem; + margin-right: 0.4em; +} +.chroma .ln { + color: #8b949e; + padding: 0 0.4em 0 0.4em; + margin-right: 0.4em; +} +.chroma .k { + color: #ff7b72; +} +.chroma .kc { + color: #ff7b72; + font-weight: bold; +} +.chroma .kd { + color: #ff7b72; + font-weight: bold; +} +.chroma .kn { + color: #ff7b72; + font-weight: bold; +} +.chroma .kp { + color: #ff7b72; + font-weight: bold; +} +.chroma .kr { + color: #ff7b72; + font-weight: bold; +} +.chroma .kt { + color: #ff7b72; + font-weight: bold; +} +.chroma .na { + color: #c9d1d9; +} +.chroma .nb { + color: #c9d1d9; +} +.chroma .bp { + color: #c9d1d9; +} +.chroma .nc { + color: #7ee787; + font-weight: bold; +} +.chroma .no { + color: #79c0ff; + font-weight: bold; +} +.chroma .nd { + color: #bc8cff; + font-weight: bold; +} +.chroma .ni { + color: #ffa657; +} +.chroma .ne { + color: #ffa657; + font-weight: bold; +} +.chroma .nf { + color: #d2a8ff; + font-weight: bold; +} +.chroma .nl { + color: #79c0ff; + font-weight: bold; +} +.chroma .nm { + color: #ff7b72; +} +.chroma .nx { + color: #79c0ff; +} +.chroma .py { + color: #79c0ff; +} +.chroma .nt { + color: #7ee787; +} +.chroma .nv { + color: #79c0ff; +} +.chroma .vc { + color: #79c0ff; +} +.chroma .vg { + color: #79c0ff; +} +.chroma .vi { + color: #79c0ff; +} +.chroma .l { + color: #79c0ff; +} +.chroma .ld { + color: #79c0ff; +} +.chroma .s { + color: #79c0ff; +} +.chroma .sa { + color: #79c0ff; +} +.chroma .sb { + color: #79c0ff; +} +.chroma .sc { + color: #79c0ff; +} +.chroma .dl { + color: #79c0ff; +} +.chroma .sd { + color: #79c0ff; +} +.chroma .s2 { + color: #79c0ff; +} +.chroma .se { + color: #79c0ff; +} +.chroma .sh { + color: #79c0ff; +} +.chroma .si { + color: #79c0ff; +} +.chroma .sx { + color: #79c0ff; +} +.chroma .sr { + color: #79c0ff; +} +.chroma .s1 { + color: #79c0ff; +} +.chroma .ss { + color: #79c0ff; +} +.chroma .m { + color: #79c0ff; +} +.chroma .mb { + color: #79c0ff; +} +.chroma .mf { + color: #79c0ff; +} +.chroma .mh { + color: #79c0ff; +} +.chroma .mi { + color: #79c0ff; +} +.chroma .il { + color: #79c0ff; +} +.chroma .mo { + color: #79c0ff; +} +.chroma .o { + color: #a5d6ff; + font-weight: bold; +} +.chroma .ow { + color: #a5d6ff; + font-weight: bold; +} +.chroma .c { + color: #8b949e; + font-style: italic; +} +.chroma .ch { + color: #8b949e; + font-style: italic; +} +.chroma .cm { + color: #8b949e; + font-style: italic; +} +.chroma .c1 { + color: #8b949e; + font-style: italic; +} +.chroma .cs { + color: #8b949e; + font-style: italic; +} +.chroma .cpf { + color: #8b949e; + font-style: italic; +} +.chroma .gd { + color: #ffa198; + background-color: #490202; +} +.chroma .ge { + color: #c9d1d9; + font-style: italic; +} +.chroma .gr { + color: #ffa198; +} +.chroma .gh { + color: #79c0ff; +} +.chroma .gi { + color: #7ee787; + background-color: #56d364; +} +.chroma .go { + color: #8b949e; +} +.chroma .gp { + color: #8b949e; +} +.chroma .gs { + font-weight: bold; +} +.chroma .gu { + color: #79c0ff; +} +.chroma .gt { + color: #ffa198; +} +.chroma .gl { + text-decoration: underline; +} +.chroma .w { + color: #484f58; +} diff --git a/wowchemy/assets/css/libs/chroma/github-light.css b/wowchemy/assets/css/libs/chroma/github-light.css new file mode 100644 index 00000000..07ea90ab --- /dev/null +++ b/wowchemy/assets/css/libs/chroma/github-light.css @@ -0,0 +1,85 @@ +/* Background */ .bg { background-color: #fff } +/* PreWrapper */ .chroma { background-color: #fff; } +/* Other */ .chroma .x { } +/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 } +/* CodeLine */ .chroma .cl { } +/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } +/* LineHighlight */ .chroma .hl { background-color: #ffc } +/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em;color: #7f7f7f } +/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em;color: #7f7f7f } +/* Line */ .chroma .line { display: flex; } +/* Keyword */ .chroma .k { color: #000; font-weight: bold } +/* KeywordConstant */ .chroma .kc { color: #000; font-weight: bold } +/* KeywordDeclaration */ .chroma .kd { color: #000; font-weight: bold } +/* KeywordNamespace */ .chroma .kn { color: #000; font-weight: bold } +/* KeywordPseudo */ .chroma .kp { color: #000; font-weight: bold } +/* KeywordReserved */ .chroma .kr { color: #000; font-weight: bold } +/* KeywordType */ .chroma .kt { color: #458; font-weight: bold } +/* Name */ .chroma .n { } +/* NameAttribute */ .chroma .na { color: #008080 } +/* NameBuiltin */ .chroma .nb { color: #0086b3 } +/* NameBuiltinPseudo */ .chroma .bp { color: #999 } +/* NameClass */ .chroma .nc { color: #458; font-weight: bold } +/* NameConstant */ .chroma .no { color: #008080 } +/* NameDecorator */ .chroma .nd { color: #3c5d5d; font-weight: bold } +/* NameEntity */ .chroma .ni { color: #800080 } +/* NameException */ .chroma .ne { color: #900; font-weight: bold } +/* NameFunction */ .chroma .nf { color: #900; font-weight: bold } +/* NameFunctionMagic */ .chroma .fm { } +/* NameLabel */ .chroma .nl { color: #900; font-weight: bold } +/* NameNamespace */ .chroma .nn { color: #555 } +/* NameOther */ .chroma .nx { } +/* NameProperty */ .chroma .py { } +/* NameTag */ .chroma .nt { color: #000080 } +/* NameVariable */ .chroma .nv { color: #008080 } +/* NameVariableClass */ .chroma .vc { color: #008080 } +/* NameVariableGlobal */ .chroma .vg { color: #008080 } +/* NameVariableInstance */ .chroma .vi { color: #008080 } +/* NameVariableMagic */ .chroma .vm { } +/* Literal */ .chroma .l { } +/* LiteralDate */ .chroma .ld { } +/* LiteralString */ .chroma .s { color: #d14 } +/* LiteralStringAffix */ .chroma .sa { color: #d14 } +/* LiteralStringBacktick */ .chroma .sb { color: #d14 } +/* LiteralStringChar */ .chroma .sc { color: #d14 } +/* LiteralStringDelimiter */ .chroma .dl { color: #d14 } +/* LiteralStringDoc */ .chroma .sd { color: #d14 } +/* LiteralStringDouble */ .chroma .s2 { color: #d14 } +/* LiteralStringEscape */ .chroma .se { color: #d14 } +/* LiteralStringHeredoc */ .chroma .sh { color: #d14 } +/* LiteralStringInterpol */ .chroma .si { color: #d14 } +/* LiteralStringOther */ .chroma .sx { color: #d14 } +/* LiteralStringRegex */ .chroma .sr { color: #009926 } +/* LiteralStringSingle */ .chroma .s1 { color: #d14 } +/* LiteralStringSymbol */ .chroma .ss { color: #990073 } +/* LiteralNumber */ .chroma .m { color: #099 } +/* LiteralNumberBin */ .chroma .mb { color: #099 } +/* LiteralNumberFloat */ .chroma .mf { color: #099 } +/* LiteralNumberHex */ .chroma .mh { color: #099 } +/* LiteralNumberInteger */ .chroma .mi { color: #099 } +/* LiteralNumberIntegerLong */ .chroma .il { color: #099 } +/* LiteralNumberOct */ .chroma .mo { color: #099 } +/* Operator */ .chroma .o { color: #000; font-weight: bold } +/* OperatorWord */ .chroma .ow { color: #000; font-weight: bold } +/* Punctuation */ .chroma .p { } +/* Comment */ .chroma .c { color: #998; font-style: italic } +/* CommentHashbang */ .chroma .ch { color: #998; font-style: italic } +/* CommentMultiline */ .chroma .cm { color: #998; font-style: italic } +/* CommentSingle */ .chroma .c1 { color: #998; font-style: italic } +/* CommentSpecial */ .chroma .cs { color: #999; font-weight: bold; font-style: italic } +/* CommentPreproc */ .chroma .cp { color: #999; font-weight: bold; font-style: italic } +/* CommentPreprocFile */ .chroma .cpf { color: #999; font-weight: bold; font-style: italic } +/* Generic */ .chroma .g { } +/* GenericDeleted */ .chroma .gd { color: #000; background-color: #fdd } +/* GenericEmph */ .chroma .ge { color: #000; font-style: italic } +/* GenericError */ .chroma .gr { color: #a00 } +/* GenericHeading */ .chroma .gh { color: #999 } +/* GenericInserted */ .chroma .gi { color: #000; background-color: #dfd } +/* GenericOutput */ .chroma .go { color: #888 } +/* GenericPrompt */ .chroma .gp { color: #555 } +/* GenericStrong */ .chroma .gs { font-weight: bold } +/* GenericSubheading */ .chroma .gu { color: #aaa } +/* GenericTraceback */ .chroma .gt { color: #a00 } +/* GenericUnderline */ .chroma .gl { text-decoration: underline } +/* TextWhitespace */ .chroma .w { color: #bbb } diff --git a/wowchemy/assets/js/wowchemy-github.js b/wowchemy/assets/js/wowchemy-github.js new file mode 100644 index 00000000..5cd5df29 --- /dev/null +++ b/wowchemy/assets/js/wowchemy-github.js @@ -0,0 +1,21 @@ +import {hugoEnvironment} from '@params'; + +/* --------------------------------------------------------------------------- + * GitHub API. + * --------------------------------------------------------------------------- */ + +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); + }); + } +} + +export {printLatestRelease}; diff --git a/wowchemy/assets/js/wowchemy-navigation.js b/wowchemy/assets/js/wowchemy-navigation.js new file mode 100644 index 00000000..f31f55dd --- /dev/null +++ b/wowchemy/assets/js/wowchemy-navigation.js @@ -0,0 +1,124 @@ +/* --------------------------------------------------------------------------- + * Responsive scrolling for URL hashes. + * --------------------------------------------------------------------------- */ + +// Dynamically get responsive navigation bar height for offsetting Scrollspy. +function getNavBarHeight() { + let navbar = document.getElementById('navbar-main'); + let navbarHeight = navbar ? navbar.getBoundingClientRect().height : 0; + console.debug('Navbar height: ' + navbarHeight); + return navbarHeight; +} + +/** + * Responsive hash scrolling. + * Check for a URL hash as an anchor. + * If page anchor matches hash, scroll to it responsively considering dynamic height elements. + * If `target` argument omitted (e.g. after event), assume it's the window's hash. + * Default to 0ms animation duration as don't want animation for fixing scrollspy Book page ToC highlighting. + */ +function scrollToAnchor(target, duration = 0) { + // 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; + + // 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, '\\:');` + + 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'); + }, + ); + } 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'); + } +} + +/* --------------------------------------------------------------------------- + * Add smooth scrolling to all links inside the main navbar. + * --------------------------------------------------------------------------- */ + +$('#navbar-main li.nav-item a.nav-link, .js-scroll').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'); + } +}); + +// 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); +}); + +// Call `fixScrollspy` when window is resized. +let resizeTimer; +$(window).resize(function () { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(fixScrollspy, 200); +}); + +// Check for hash change event and fix responsive offset for hash links (e.g. Markdown footnotes). +window.addEventListener('hashchange', scrollToAnchor); + +export {fixScrollspy, scrollToAnchor}; diff --git a/wowchemy/assets/js/wowchemy-slides.js b/wowchemy/assets/js/wowchemy-slides.js index 0ac5be2d..a6c74422 100644 --- a/wowchemy/assets/js/wowchemy-slides.js +++ b/wowchemy/assets/js/wowchemy-slides.js @@ -1,11 +1,11 @@ /* - global RevealMarkdown, RevealHighlight, RevealSearch, RevealNotes, RevealMath, RevealZoom, Reveal, mermaid, RevealMenu + global RevealMarkdown, RevealSearch, RevealNotes, RevealMath, RevealZoom, Reveal, mermaid, RevealMenu */ import * as params from '@params'; // Enable core slide features. -var enabledPlugins = [RevealMarkdown, RevealHighlight, RevealSearch, RevealNotes, RevealMath.MathJax3, RevealZoom]; +var enabledPlugins = [RevealMarkdown, RevealSearch, RevealNotes, RevealMath.MathJax3, RevealZoom]; const isObject = function (o) { return o === Object(o) && !isArray(o) && typeof o !== 'function'; diff --git a/wowchemy/assets/js/wowchemy.js b/wowchemy/assets/js/wowchemy.js index 6ffa1c09..38b1f7f5 100644 --- a/wowchemy/assets/js/wowchemy.js +++ b/wowchemy/assets/js/wowchemy.js @@ -6,8 +6,10 @@ **************************************************/ import mediumZoom from './_vendor/medium-zoom.esm'; -import {hugoEnvironment, codeHighlighting, searchEnabled} from '@params'; +import {hugoEnvironment, searchEnabled, i18n} from '@params'; import {scrollParentToChild} from './wowchemy-utils'; +import {fixScrollspy, scrollToAnchor} from './wowchemy-navigation'; +import {printLatestRelease} from './wowchemy-github'; import { changeThemeModeClick, initThemeVariation, @@ -17,63 +19,6 @@ import { console.debug(`Environment: ${hugoEnvironment}`); -/* --------------------------------------------------------------------------- - * Responsive scrolling for URL hashes. - * --------------------------------------------------------------------------- */ - -// Dynamically get responsive navigation bar height for offsetting Scrollspy. -function getNavBarHeight() { - let navbar = document.getElementById('navbar-main'); - let navbarHeight = navbar ? navbar.getBoundingClientRect().height : 0; - console.debug('Navbar height: ' + navbarHeight); - return navbarHeight; -} - -/** - * Responsive hash scrolling. - * Check for a URL hash as an anchor. - * If page anchor matches hash, scroll to it responsively considering dynamic height elements. - * If `target` argument omitted (e.g. after event), assume it's the window's hash. - * Default to 0ms animation duration as don't want animation for fixing scrollspy Book page ToC highlighting. - */ -function scrollToAnchor(target, duration = 0) { - // 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; - - // 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, '\\:');` - - 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'); - }, - ); - } 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 = @@ -82,71 +27,6 @@ function removeQueryParamsFromUrl() { } } -// 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, .js-scroll').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'); - } -}); - -/* --------------------------------------------------------------------------- - * GitHub API. - * --------------------------------------------------------------------------- */ - -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); - }); - } -} - /* --------------------------------------------------------------------------- * Toggle search dialog. * --------------------------------------------------------------------------- */ @@ -218,25 +98,25 @@ function getSiblings(elem) { * On document ready. * --------------------------------------------------------------------------- */ -$(document).ready(function () { +document.addEventListener('DOMContentLoaded', function () { fixHugoOutput(); // Render theme variation, including any HLJS and Mermaid themes. let {isDarkTheme, themeMode} = initThemeVariation(); renderThemeVariation(isDarkTheme, themeMode, true); - // Initialise code highlighting if enabled for this page. - // Note: this block should be processed after the Mermaid code-->div conversion. - if (codeHighlighting) { - hljs.initHighlighting(); - } - // Scroll Book page's active menu sidebar link into view. let child = document.querySelector('.docs-links .active'); let parent = document.querySelector('.docs-links'); if (child && parent) { scrollParentToChild(parent, child); } + + // Print latest version of GitHub projects. + let githubReleaseSelector = '.js-github-release'; + if ($(githubReleaseSelector).length > 0) { + printLatestRelease(githubReleaseSelector, $(githubReleaseSelector).data('repo')); + } }); /* --------------------------------------------------------------------------- @@ -353,12 +233,6 @@ $(window).on('load', function () { } } - // Print latest version of GitHub projects. - let githubReleaseSelector = '.js-github-release'; - if ($(githubReleaseSelector).length > 0) { - printLatestRelease(githubReleaseSelector, $(githubReleaseSelector).data('repo')); - } - // Parse Wowchemy keyboard shortcuts. document.addEventListener('keyup', (event) => { if (event.code === 'Escape') { @@ -426,21 +300,37 @@ darkModeMediaQuery.addEventListener('change', (event) => { onMediaQueryListEvent(event); }); -// 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); -}); +// Code block copy button +document.querySelectorAll('pre > code').forEach((codeblock) => { + const container = codeblock.parentNode.parentNode; + const copyBtn = document.createElement('button'); + let classesToAdd = ['btn', 'btn-primary', 'btn-copy-code']; + copyBtn.classList.add(...classesToAdd); + copyBtn.innerHTML = i18n['copy']; -// Call `fixScrollspy` when window is resized. -let resizeTimer; -$(window).resize(function () { - clearTimeout(resizeTimer); - resizeTimer = setTimeout(fixScrollspy, 200); + function copiedNotification() { + copyBtn.innerHTML = i18n['copied']; + setTimeout(() => { + copyBtn.innerHTML = i18n['copy']; + }, 2000); + } + + copyBtn.addEventListener('click', () => { + if ('clipboard' in navigator) { + navigator.clipboard.writeText(codeblock.textContent); + copiedNotification(); + return; + } + }); + + if (container.classList.contains('highlight')) { + // Parent when Hugo line numbers disabled + container.appendChild(copyBtn); + } else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == 'TABLE') { + // Parent when Hugo line numbers enabled + codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copyBtn); + } else { + // Parent when Hugo `highlight` class not applied to code block + codeblock.parentNode.appendChild(copyBtn); + } }); diff --git a/wowchemy/assets/scss/wowchemy/_base.scss b/wowchemy/assets/scss/wowchemy/_base.scss index 3864cea5..d2aad12a 100644 --- a/wowchemy/assets/scss/wowchemy/_base.scss +++ b/wowchemy/assets/scss/wowchemy/_base.scss @@ -211,14 +211,6 @@ code { font-family: $sta-font-mono, monospace; } -//pre, -//code:not(.hljs) { -// color: #c7254e; -// -// /* Match bg of default highlight theme. */ -// background-color: rgb(248, 248, 248); -//} - pre { margin: 0 0 1rem; @@ -234,6 +226,25 @@ pre code { overflow-x: auto; } +div.highlight, +pre { + // Required to position Copy button + position: relative; +} + +.btn-copy-code { + display: none; + position: absolute; + top: 4px; + right: 4px; + user-select: none; +} + +div.highlight:hover .btn-copy-code, +pre:hover .btn-copy-code { + display: block; +} + hr { border: 0; height: 1px; diff --git a/wowchemy/assets/scss/wowchemy/_dark.scss b/wowchemy/assets/scss/wowchemy/_dark.scss index 856d1d1b..64ffe539 100644 --- a/wowchemy/assets/scss/wowchemy/_dark.scss +++ b/wowchemy/assets/scss/wowchemy/_dark.scss @@ -45,14 +45,8 @@ body.dark, color: #fff; } -.dark pre, -.dark code:not(.hljs) { - color: rgb(139, 233, 253); - background-color: rgb(68, 71, 90); -} - .dark pre { - /* Match `pre` bg color above. */ + /* Match `pre` bg color. */ border-color: rgb(68, 71, 90); } diff --git a/wowchemy/config.yaml b/wowchemy/config.yaml index f363d508..1f47853b 100644 --- a/wowchemy/config.yaml +++ b/wowchemy/config.yaml @@ -9,9 +9,10 @@ markup: title: true highlight: codeFences: true - noHl: true + noHl: false lineNumbersInTable: false noClasses: false + guessSyntax: true tableOfContents: startLevel: 2 endLevel: 3 diff --git a/wowchemy/data/assets.toml b/wowchemy/data/assets.toml index 39843a9f..285a0b62 100644 --- a/wowchemy/data/assets.toml +++ b/wowchemy/data/assets.toml @@ -7,14 +7,6 @@ # JavaScript -[js.highlight] - version = "10.2.1" - sri = "sha512-Ypjm0o7jOxAd4hpdoppSEN0TQOC19UtPAqD+4s5AlXmUvbmmS/YMxYqAqarQYyxTnB6/rqip9qcxlNB/3U9Wdg==" - url = "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@%s/build/highlight.min.js" -[js.highlight_lang] - version = "10.2.1" - sri = "" # No SRI as highlight language styles is determined at run time. - url = "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@%s/build/languages/%s.min.js" [js.mathJax] version = "3" sri = "" # No SRI as dynamically generated. @@ -86,10 +78,6 @@ version = "7.4.5" sri = "sha256-TehzF/2QvNKhGQrrNpoOb2Ck4iGZ1J/DI4pkd2oUsBc=" url = "https://cdn.jsdelivr.net/npm/instantsearch.css@%s/themes/satellite-min.css" -[css.highlight] - version = "10.2.1" - sri = "" # No SRI as highlight style is determined at run time. - url = "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@%s/build/styles/%s.min.css" [css.cookieconsent] version = "3.1.1" sri = "sha512-LQ97camar/lOliT/MqjcQs5kWgy6Qz/cCRzzRzUCfv0fotsCTC9ZHXaPQmJV8Xu/PVALfJZ7BDezl5lW3/qBxg==" diff --git a/wowchemy/i18n/en.yaml b/wowchemy/i18n/en.yaml index cab3cb70..d2d36cc8 100644 --- a/wowchemy/i18n/en.yaml +++ b/wowchemy/i18n/en.yaml @@ -90,6 +90,9 @@ - id: btn_copy translation: Copy +- id: btn_copied + translation: Copied + - id: btn_download translation: Download @@ -138,7 +141,7 @@ - id: contact_message translation: Message - + - id: contact_attachment translation: Attach file diff --git a/wowchemy/layouts/partials/site_head.html b/wowchemy/layouts/partials/site_head.html index 741628f7..fe55d68d 100644 --- a/wowchemy/layouts/partials/site_head.html +++ b/wowchemy/layouts/partials/site_head.html @@ -155,21 +155,12 @@ {{ else if isset site.Params.features.syntax_highlighter "enable" }} {{ $scr.Set "highlight_enabled" site.Params.features.syntax_highlighter.enable }} {{ end }} - {{ if ($scr.Get "highlight_enabled") }} - {{ $v := $css.highlight.version }} - {{ with site.Params.features.syntax_highlighter.theme }} - {{ printf "" (printf $css.highlight.url $css.highlight.version .) | safeHTML }} - {{ printf "" (printf $css.highlight.url $css.highlight.version .) | safeHTML }} - {{ else }} - {{ if eq ($scr.Get "light") true }} - {{ printf "" (printf $css.highlight.url $css.highlight.version "github") | safeHTML }} - {{ printf "" (printf $css.highlight.url $css.highlight.version "dracula") | safeHTML }} - {{ else }} - {{ printf "" (printf $css.highlight.url $css.highlight.version "github") | safeHTML }} - {{ printf "" (printf $css.highlight.url $css.highlight.version "dracula") | safeHTML }} - {{ end }} - {{ end }} - {{ end }} + {{ $hl_theme_light := site.Params.features.syntax_highlighter.light_theme | default "github-light" }} + {{ $hl_theme_dark := site.Params.features.syntax_highlighter.dark_theme | default "dracula" }} + {{ $hl_theme_light_css := resources.Get (printf "css/libs/chroma/%s.css" $hl_theme_light) | minify }} + {{ $hl_theme_dark_css := resources.Get (printf "css/libs/chroma/%s.css" $hl_theme_dark) | minify }} + + {{/* Maps CSS. */}} {{ $map_provider := lower site.Params.features.map.provider }} diff --git a/wowchemy/layouts/partials/site_js.html b/wowchemy/layouts/partials/site_js.html index e365171d..af077d34 100644 --- a/wowchemy/layouts/partials/site_js.html +++ b/wowchemy/layouts/partials/site_js.html @@ -32,15 +32,6 @@ {{ printf "" (printf $js.mermaid.url $js.mermaid.version) $js.mermaid.sri | safeHTML }} {{ end }} - {{ if $.Scratch.Get "highlight_enabled" }} - {{ $v := $js.highlight.version }} - {{ printf "" (printf $js.highlight.url $v) $js.highlight.sri | safeHTML }} - {{ $v := $js.highlight_lang.version }} - {{ range site.Params.features.syntax_highlighter.extra_languages }} - {{ printf "" (printf $js.highlight_lang.url $v .) | safeHTML }} - {{ end }} - {{ end }} - {{ end }} {{/* Maps JS. */}} @@ -149,7 +140,8 @@ {{ $js_license := $js_license | printf "%s/*! Copyright 2016-present George Cushen (https://georgecushen.com/) */\n" }} {{ $js_license := $js_license | printf "%s/*! License: https://github.com/wowchemy/wowchemy-hugo-themes/blob/main/LICENSE.md */\n" }} {{ $js_bundle_head := $js_license | resources.FromString "js/bundle-head.js" }} - {{ $js_params := dict "hugoEnvironment" hugo.Environment "codeHighlighting" ($scr.Get "highlight_enabled" | default false) "searchEnabled" (in (slice "wowchemy" "algolia") $search_provider) }} + {{ $i18n := dict "copy" (i18n "btn_copy") "copied" (i18n "btn_copied" | default "Copied") }} + {{ $js_params := dict "hugoEnvironment" hugo.Environment "searchEnabled" (in (slice "wowchemy" "algolia") $search_provider) "i18n" $i18n }} {{ $js_academic := resources.Get "js/wowchemy.js" | js.Build (dict "targetPath" (printf "%s/js/wow-core.js" .Lang ) "params" $js_params) }} {{ $js_bundle := slice $js_academic }} {{ if eq $search_provider "wowchemy" }} diff --git a/wowchemy/layouts/slides/baseof.html b/wowchemy/layouts/slides/baseof.html index 544e45e0..1ffae172 100644 --- a/wowchemy/layouts/slides/baseof.html +++ b/wowchemy/layouts/slides/baseof.html @@ -26,8 +26,9 @@ {{- $theme := $.Param "slides.theme" | default "black" -}} - {{- $highlight_style := $.Param "slides.highlight_style" | default "dracula" -}} - {{ printf "" (printf $css.highlight.url $css.highlight.version $highlight_style) | safeHTML }} + {{ $hl_theme := $.Param "slides.highlight_style" | default "dracula" }} + {{ $hl_theme_css := resources.Get (printf "css/libs/chroma/%s.css" $hl_theme) | minify }} + {{ $css_custom := resources.Get "css/reveal_custom.css" }} {{ $style := slice $css_custom | resources.Concat "css/reveal_custom.css" | resources.Minify }} @@ -39,7 +40,6 @@ -