From 90db4823dc84f47de9f9a04b024904dff58c50eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Weber?= Date: Wed, 21 Jun 2023 22:06:16 +0200 Subject: [PATCH] highlight: simplify js implementation #169 --- exampleSite/content/tests/code/_index.en.md | 4 +- static/css/ie.css | 3 + static/css/theme.css | 13 +- static/css/variant.css | 2 + static/js/theme.js | 125 +++++--------------- 5 files changed, 50 insertions(+), 97 deletions(-) diff --git a/exampleSite/content/tests/code/_index.en.md b/exampleSite/content/tests/code/_index.en.md index ce194f9e5e..6bef0c0229 100644 --- a/exampleSite/content/tests/code/_index.en.md +++ b/exampleSite/content/tests/code/_index.en.md @@ -75,7 +75,7 @@ Some preformatted stuff in HTML elements #### %% %% {{% tab title="json" %}} -{{% highlight html "linenos=table,hl_lines=1 2,anchorlinenos=true" %}} +{{% highlight html "hl_lines=1 2,anchorlinenos=true" %}} { "Hello": "World" "Hello": "World" "Hello": "World" } @@ -85,7 +85,7 @@ Some preformatted stuff in HTML elements #### %% <> {{% tab title="json" %}} -{{< highlight html "linenos=table,hl_lines=1 2,anchorlinenos=true" >}} +{{< highlight html "hl_lines=1 2,anchorlinenos=true" >}} { "Hello": "World" "Hello": "World" "Hello": "World" } diff --git a/static/css/ie.css b/static/css/ie.css index 2b00aec339..28e69e3075 100644 --- a/static/css/ie.css +++ b/static/css/ie.css @@ -126,6 +126,7 @@ border-bottom-right-radius: 2px; border-bottom-left-radius: 0; } + div.highlight > div table + .copy-to-clipboard-button, pre > .copy-to-clipboard-button { right: 4px; } @@ -522,11 +523,13 @@ color: rgba( 255, 250, 233, 1 ); /* var(--CODE-INLINE-BG-color) */ } + div.highlight > div table + .copy-to-clipboard-button, pre .copy-to-clipboard-button { border-color: rgba( 216, 216, 216, 1 ); /* var(--CODE-BLOCK-BORDER-color) */ color: rgba( 72, 106, 201, 1 ); /* var(--MAIN-LINK-color) */ } + div.highlight > div table + .copy-to-clipboard-button:hover, pre .copy-to-clipboard-button:hover { background-color: rgba( 72, 106, 201, 1 ); /* var(--MAIN-LINK-color) */ border-color: rgba( 72, 106, 201, 1 ); /* var(--MAIN-LINK-color) */ diff --git a/static/css/theme.css b/static/css/theme.css index ad9ce5d348..76d2e4966e 100644 --- a/static/css/theme.css +++ b/static/css/theme.css @@ -670,6 +670,7 @@ pre code { div.highlight > div{ border-style: solid; border-width: 1px; + position: relative; } /* remove default style for usual markdown tables */ div.highlight > div table{ @@ -679,7 +680,6 @@ div.highlight > div table{ } div.highlight > div td{ border-width: 0; - !padding: 0; } #body div.highlight > div a { line-height: inherit; @@ -695,6 +695,14 @@ div.highlight > div td:first-child:not(:last-child){ div.highlight > div td:not(:first-child):last-child{ width: 100%; } +/* add scrollbars if highlight shortcode was used in table lineno mode */ +div.highlight > div table{ + display: block; + overflow: auto; +} +div.highlight > div td:not(:first-child):last-child pre code{ + white-space: pre; +} /* remove border from row cells if highlight shortcode was used in table lineno mode */ div.highlight > div td > pre { border-radius: 0; @@ -1077,6 +1085,7 @@ html[dir="rtl"] #body #breadcrumbs { border-width: 1px; cursor: pointer; font-size: .934375rem; + line-height: 1.15; } span > .copy-to-clipboard-button { @@ -1091,11 +1100,13 @@ span > .copy-to-clipboard-button { font-weight: 500; } +div.highlight > div table + .copy-to-clipboard-button > i, .copy-to-clipboard-code + .copy-to-clipboard-button > i { padding-left: 5px; padding-right: 5px; } +div.highlight > div table + .copy-to-clipboard-button, pre > .copy-to-clipboard-button { background-color: rgba( 160, 160, 160, .2 ); border-radius: 2px; diff --git a/static/css/variant.css b/static/css/variant.css index d3e279dd11..1721d2ae77 100644 --- a/static/css/variant.css +++ b/static/css/variant.css @@ -272,11 +272,13 @@ table { color: var(--INTERNAL-CODE-INLINE-BG-color); } +div.highlight > div table + .copy-to-clipboard-button, pre .copy-to-clipboard-button { border-color: var(--INTERNAL-CODE-BLOCK-BORDER-color); color: var(--INTERNAL-MAIN-LINK-color); } +div.highlight > div table + .copy-to-clipboard-button:hover, pre .copy-to-clipboard-button:hover { background-color: var(--INTERNAL-MAIN-LINK-color); border-color: var(--INTERNAL-MAIN-LINK-color); diff --git a/static/js/theme.js b/static/js/theme.js index c4272b8b3c..835536a983 100644 --- a/static/js/theme.js +++ b/static/js/theme.js @@ -484,13 +484,16 @@ function initAnchorClipboard(){ function initCodeClipboard(){ function getCodeText( node ){ - var text = node.textContent; + // if highlight shortcode is used in inline lineno mode, remove lineno nodes before generating text, otherwise it doesn't hurt + var code = node.cloneNode( true ); + Array.from( code.querySelectorAll( '*:scope > span > span:first-child:not(:last-child)' ) ).forEach( function( lineno ){ + lineno.remove(); + }); + var text = code.textContent; // remove a trailing line break, this may most likely // come from the browser / Hugo transformation text = text.replace( /\n$/, '' ); - // removes leading $ signs from text in an assumption - // that this has to be the unix prompt marker - weird - return text.replace( /^\$\s/gm, '' ); + return text; } function fallbackMessage( action ){ @@ -508,114 +511,41 @@ function initCodeClipboard(){ return actionMsg; } - if( !window.disableHighlightWrapFix ){ - // if the highlight shortcode was used with table lineno mode, the generated DOM - // is a table, containg exactly one row with two cells, the first cell for all linenos - // the second with the code; - // this does not look nice if the code gets wrapped around, so we reformat the table - // by creating a row for each line, containing the two cells but with only the content of - // one line - var codeTables = Array.from( document.querySelectorAll( 'code' ) ).reduce( function(a, code){ - // collect all tbody's without duplicates that need our treatment - if( code.parentNode.tagName.toLowerCase() == 'pre' && - code.parentNode.parentNode.tagName.toLowerCase() == 'td' && - code.parentNode.parentNode.parentNode.tagName.toLowerCase() == 'tr' && - code.parentNode.parentNode.parentNode.parentNode.tagName.toLowerCase() == 'tbody' && - code.parentNode.parentNode.parentNode.querySelector( 'td:first-child > pre > code' ) == code && - ( !a.length || a[a.length-1] != code.parentNode.parentNode.parentNode.parentNode ) ){ - var table = code.parentNode.parentNode.parentNode.parentNode; - a.push( table ); - } - return a; - }, [] ); - for( var i = 0; i < codeTables.length; i++ ){ - // now treat the table (tbody); - // first we collect some data, setting up our row template and collect the text - // representation of the code for later usage with copy-to-clipboard - var table = codeTables[i]; - var text = getCodeText( table.querySelector( 'td:last-child code' ) ); - var tr = table.querySelector( 'tr' ).cloneNode(); - tr.appendChild( table.querySelector( 'td:first-child' ).cloneNode() ) - .appendChild( table.querySelector( 'td:first-child pre' ).cloneNode() ) - .appendChild( table.querySelector( 'td:first-child code' ).cloneNode() ) - .classList.add( 'nocode' ); - tr.appendChild( table.querySelector( 'td:last-child' ).cloneNode() ) - .appendChild( table.querySelector( 'td:last-child pre' ).cloneNode() ) - .appendChild( table.querySelector( 'td:last-child code' ).cloneNode() ) - .classList.add( 'nocode' ); - - // select lineno and code cell of first line that contains all the content - var linenums = table.querySelectorAll( 'td:first-child code > span' ); - var codes = table.querySelectorAll( 'td:last-child code > span' ); - for( var j = 0; j < linenums.length; j++ ){ - // now create a new table row by cloning our template - // and transfering the original content - var clonedTr = tr.cloneNode(true); - var code1 = clonedTr.querySelector( 'td:first-child code' ); - var code2 = clonedTr.querySelector( 'td:last-child code' ); - code1.appendChild( linenums[j] ); - code2.appendChild( codes[j] ); - table.appendChild( clonedTr ); - } - // in the end we have an empty first row, that needs to be deleted - table.querySelector( 'tr:first-child' ).remove(); - // we delete the reformat marker of the first code cell to allow the - // copy-to-clipboard functionality - table.querySelector( 'tr:first-child td:last-child code' ).classList.remove( 'nocode' ); - // put the text representation into a data attribute - table.querySelector( 'tr:first-child td:last-child code' ).dataset[ 'code' ] = text; - // finally mark our tbody to apply special CSS styling - table.parentNode.parentNode.parentNode.classList.add( 'wrapfix' ); - } - } - - var codeElements = document.querySelectorAll( 'code:not(.nocode)' ); + var codeElements = document.querySelectorAll( 'code' ); for( var i = 0; i < codeElements.length; i++ ){ var code = codeElements[i]; - var text = code.textContent; + var text = getCodeText( code ); var inPre = code.parentNode.tagName.toLowerCase() == 'pre'; + var inTable = inPre && + code.parentNode.parentNode.tagName.toLowerCase() == 'td'; // avoid copy-to-clipboard for highlight shortcode in table lineno mode - var isFirstLineCell = inPre && - code.parentNode.parentNode.tagName.toLowerCase() == 'td' && + var isFirstLineCell = inTable && code.parentNode.parentNode.parentNode.querySelector( 'td:first-child > pre > code' ) == code; if( !isFirstLineCell && ( inPre || text.length > 5 ) ){ var clip = new ClipboardJS( '.copy-to-clipboard-button', { text: function( trigger ){ - if( !( trigger.previousElementSibling && trigger.previousElementSibling.matches( 'code' ) ) ){ + if( !trigger.previousElementSibling ){ return ''; } - // if we already have a text representation, return it - var code = trigger.previousElementSibling; - if( code.dataset.code ){ - return code.dataset.code; - } - // if highlight shortcode used in inline lineno mode, remove lineno nodes before generating text - code = code.cloneNode( true ); - Array.from( code.querySelectorAll( '*:scope > span > span:first-child:not(:last-child)' ) ).forEach( function( lineno ){ - lineno.remove(); - }); - // generate and save generated text for repeated usage - var text = getCodeText( code ); - trigger.previousElementSibling.dataset[ 'code' ] = text; - return text; + return trigger.previousElementSibling.dataset.code || ''; } }); clip.on( 'success', function( e ){ e.clearSelection(); - var inPre = e.trigger.parentNode.tagName.toLowerCase() == 'pre'; + var doBeside = e.trigger.parentNode.tagName.toLowerCase() == 'pre' || (e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'table' ); e.trigger.setAttribute( 'aria-label', window.T_Copied_to_clipboard ); - e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (inPre ? 'w' : 's'+(isRtl?'e':'w')) ); + e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's'+(isRtl?'e':'w')) ); }); clip.on( 'error', function( e ){ - var inPre = e.trigger.parentNode.tagName.toLowerCase() == 'pre'; + var doBeside = e.trigger.parentNode.tagName.toLowerCase() == 'pre' || (e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'table' ); e.trigger.setAttribute( 'aria-label', fallbackMessage(e.action) ); - e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (inPre ? 'w' : 's'+(isRtl?'e':'w')) ); + e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's'+(isRtl?'e':'w')) ); var f = function(){ e.trigger.setAttribute( 'aria-label', window.T_Copied_to_clipboard ); - e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (inPre ? 'w' : 's'+(isRtl?'e':'w')) ); + e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's'+(isRtl?'e':'w')) ); document.removeEventListener( 'copy', f ); }; document.addEventListener( 'copy', f ); @@ -627,12 +557,11 @@ function initCodeClipboard(){ code.parentNode.classList.add( 'pre-code' ); } else{ - var clone = code.cloneNode( true ); + var parent = code.parentNode; var span = document.createElement( 'span' ); span.classList.add( 'copy-to-clipboard' ); - span.appendChild( clone ); - code.parentNode.replaceChild( span, code ); - code = clone; + span.appendChild( code ); + parent.appendChild( span ); } var button = document.createElement( 'span' ); button.classList.add( 'copy-to-clipboard-button' ); @@ -642,7 +571,15 @@ function initCodeClipboard(){ this.removeAttribute( 'aria-label' ); this.classList.remove( 'tooltipped', 'tooltipped-w', 'tooltipped-se', 'tooltipped-sw' ); }); - code.parentNode.insertBefore( button, code.nextSibling ); + if( inTable ){ + var table = code.parentNode.parentNode.parentNode.parentNode.parentNode; + table.dataset[ 'code' ] = text; + table.parentNode.insertBefore( button, table.nextSibling ); + } + else{ + code.dataset[ 'code' ] = text; + code.parentNode.insertBefore( button, code.nextSibling ); + } } } }