add support for automatically scrolling code highlights into view
This commit is contained in:
parent
5a5a5c9a6c
commit
bff9bfb101
|
@ -39,6 +39,7 @@ body {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: all .2s ease;
|
transition: all .2s ease;
|
||||||
|
will-change: opacity;
|
||||||
|
|
||||||
&.visible {
|
&.visible {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -1599,6 +1600,10 @@ $overlayHeaderPadding: 5px;
|
||||||
* CODE HIGHLGIHTING
|
* CODE HIGHLGIHTING
|
||||||
*********************************************/
|
*********************************************/
|
||||||
|
|
||||||
|
.reveal .hljs {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.reveal .hljs table {
|
.reveal .hljs table {
|
||||||
margin: initial;
|
margin: initial;
|
||||||
}
|
}
|
||||||
|
|
15
demo.html
15
demo.html
|
@ -102,7 +102,7 @@
|
||||||
|
|
||||||
<section data-auto-animate>
|
<section data-auto-animate>
|
||||||
<h2 data-id="code-title">With animations</h2>
|
<h2 data-id="code-title">With animations</h2>
|
||||||
<pre data-id="code-animation"><code class="hljs" data-trim data-line-numbers="|4|4,8-11">
|
<pre data-id="code-animation"><code class="hljs" data-trim data-line-numbers="|4,8-11|17|22-24">
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
function Example() {
|
function Example() {
|
||||||
|
@ -117,6 +117,19 @@
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SecondExample() {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>You clicked {count} times</p>
|
||||||
|
<button onClick={() => setCount(count + 1)}>
|
||||||
|
Click me
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,14 @@ export default class AutoAnimate {
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
this.Reveal.dispatchEvent( 'autoanimate', { fromSlide: fromSlide, toSlide: toSlide, sheet: this.autoAnimateStyleSheet } );
|
this.Reveal.dispatchEvent({
|
||||||
|
type: 'autoanimate',
|
||||||
|
data: {
|
||||||
|
fromSlide,
|
||||||
|
toSlide,
|
||||||
|
sheet: this.autoAnimateStyleSheet
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ export default class Fragments {
|
||||||
|
|
||||||
// Visible fragments
|
// Visible fragments
|
||||||
if( i <= index ) {
|
if( i <= index ) {
|
||||||
if( !el.classList.contains( 'visible' ) ) changedFragments.shown.push( el );
|
let wasVisible = el.classList.contains( 'visible' )
|
||||||
el.classList.add( 'visible' );
|
el.classList.add( 'visible' );
|
||||||
el.classList.remove( 'current-fragment' );
|
el.classList.remove( 'current-fragment' );
|
||||||
|
|
||||||
|
@ -191,12 +191,30 @@ export default class Fragments {
|
||||||
el.classList.add( 'current-fragment' );
|
el.classList.add( 'current-fragment' );
|
||||||
this.Reveal.slideContent.startEmbeddedContent( el );
|
this.Reveal.slideContent.startEmbeddedContent( el );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( !wasVisible ) {
|
||||||
|
changedFragments.shown.push( el )
|
||||||
|
this.Reveal.dispatchEvent({
|
||||||
|
target: el,
|
||||||
|
type: 'visible',
|
||||||
|
bubbles: false
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Hidden fragments
|
// Hidden fragments
|
||||||
else {
|
else {
|
||||||
if( el.classList.contains( 'visible' ) ) changedFragments.hidden.push( el );
|
let wasVisible = el.classList.contains( 'visible' )
|
||||||
el.classList.remove( 'visible' );
|
el.classList.remove( 'visible' );
|
||||||
el.classList.remove( 'current-fragment' );
|
el.classList.remove( 'current-fragment' );
|
||||||
|
|
||||||
|
if( wasVisible ) {
|
||||||
|
changedFragments.hidden.push( el );
|
||||||
|
this.Reveal.dispatchEvent({
|
||||||
|
target: el,
|
||||||
|
type: 'hidden',
|
||||||
|
bubbles: false
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
@ -253,11 +271,23 @@ export default class Fragments {
|
||||||
let changedFragments = this.update( index, fragments );
|
let changedFragments = this.update( index, fragments );
|
||||||
|
|
||||||
if( changedFragments.hidden.length ) {
|
if( changedFragments.hidden.length ) {
|
||||||
this.Reveal.dispatchEvent( 'fragmenthidden', { fragment: changedFragments.hidden[0], fragments: changedFragments.hidden } );
|
this.Reveal.dispatchEvent({
|
||||||
|
type: 'fragmenthidden',
|
||||||
|
data: {
|
||||||
|
fragment: changedFragments.hidden[0],
|
||||||
|
fragments: changedFragments.hidden
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if( changedFragments.shown.length ) {
|
if( changedFragments.shown.length ) {
|
||||||
this.Reveal.dispatchEvent( 'fragmentshown', { fragment: changedFragments.shown[0], fragments: changedFragments.shown } );
|
this.Reveal.dispatchEvent({
|
||||||
|
type: 'fragmentshown',
|
||||||
|
data: {
|
||||||
|
fragment: changedFragments.shown[0],
|
||||||
|
fragments: changedFragments.shown
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Reveal.updateControls();
|
this.Reveal.updateControls();
|
||||||
|
|
|
@ -65,10 +65,13 @@ export default class Overview {
|
||||||
const indices = this.Reveal.getIndices();
|
const indices = this.Reveal.getIndices();
|
||||||
|
|
||||||
// Notify observers of the overview showing
|
// Notify observers of the overview showing
|
||||||
this.Reveal.dispatchEvent( 'overviewshown', {
|
this.Reveal.dispatchEvent({
|
||||||
|
type: 'overviewshown',
|
||||||
|
data: {
|
||||||
'indexh': indices.h,
|
'indexh': indices.h,
|
||||||
'indexv': indices.v,
|
'indexv': indices.v,
|
||||||
'currentSlide': this.Reveal.getCurrentSlide()
|
'currentSlide': this.Reveal.getCurrentSlide()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -175,10 +178,13 @@ export default class Overview {
|
||||||
this.Reveal.cueAutoSlide();
|
this.Reveal.cueAutoSlide();
|
||||||
|
|
||||||
// Notify observers of the overview hiding
|
// Notify observers of the overview hiding
|
||||||
this.Reveal.dispatchEvent( 'overviewhidden', {
|
this.Reveal.dispatchEvent({
|
||||||
|
type: 'overviewhidden',
|
||||||
|
data: {
|
||||||
'indexh': indices.h,
|
'indexh': indices.h,
|
||||||
'indexv': indices.v,
|
'indexv': indices.v,
|
||||||
'currentSlide': this.Reveal.getCurrentSlide()
|
'currentSlide': this.Reveal.getCurrentSlide()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
79
js/reveal.js
79
js/reveal.js
|
@ -194,10 +194,13 @@ export default function( revealElement, options ) {
|
||||||
|
|
||||||
dom.wrapper.classList.add( 'ready' );
|
dom.wrapper.classList.add( 'ready' );
|
||||||
|
|
||||||
dispatchEvent( 'ready', {
|
dispatchEvent({
|
||||||
'indexh': indexh,
|
type: 'ready',
|
||||||
'indexv': indexv,
|
data: {
|
||||||
'currentSlide': currentSlide
|
indexh,
|
||||||
|
indexv,
|
||||||
|
currentSlide
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, 1 );
|
}, 1 );
|
||||||
|
|
||||||
|
@ -511,7 +514,7 @@ export default function( revealElement, options ) {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// Notify subscribers that the PDF layout is good to go
|
// Notify subscribers that the PDF layout is good to go
|
||||||
dispatchEvent( 'pdf-ready' );
|
dispatchEvent({ type: 'pdf-ready' });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,16 +1061,18 @@ export default function( revealElement, options ) {
|
||||||
* Dispatches an event of the specified type from the
|
* Dispatches an event of the specified type from the
|
||||||
* reveal DOM element.
|
* reveal DOM element.
|
||||||
*/
|
*/
|
||||||
function dispatchEvent( type, args ) {
|
function dispatchEvent({ target=dom.wrapper, type, data, bubbles=true }) {
|
||||||
|
|
||||||
let event = document.createEvent( 'HTMLEvents', 1, 2 );
|
let event = document.createEvent( 'HTMLEvents', 1, 2 );
|
||||||
event.initEvent( type, true, true );
|
event.initEvent( type, bubbles, true );
|
||||||
extend( event, args );
|
extend( event, data );
|
||||||
dom.wrapper.dispatchEvent( event );
|
target.dispatchEvent( event );
|
||||||
|
|
||||||
|
if( target === dom.wrapper ) {
|
||||||
// If we're in an iframe, post each reveal.js event to the
|
// If we're in an iframe, post each reveal.js event to the
|
||||||
// parent window. Used by the notes plugin
|
// parent window. Used by the notes plugin
|
||||||
dispatchPostMessage( type );
|
dispatchPostMessage( type );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1347,10 +1352,13 @@ export default function( revealElement, options ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if( oldScale !== scale ) {
|
if( oldScale !== scale ) {
|
||||||
dispatchEvent( 'resize', {
|
dispatchEvent({
|
||||||
'oldScale': oldScale,
|
type: 'resize',
|
||||||
'scale': scale,
|
data: {
|
||||||
'size': size
|
oldScale,
|
||||||
|
scale,
|
||||||
|
size
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1577,7 +1585,7 @@ export default function( revealElement, options ) {
|
||||||
dom.wrapper.classList.add( 'paused' );
|
dom.wrapper.classList.add( 'paused' );
|
||||||
|
|
||||||
if( wasPaused === false ) {
|
if( wasPaused === false ) {
|
||||||
dispatchEvent( 'paused' );
|
dispatchEvent({ type: 'paused' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1594,7 +1602,7 @@ export default function( revealElement, options ) {
|
||||||
cueAutoSlide();
|
cueAutoSlide();
|
||||||
|
|
||||||
if( wasPaused ) {
|
if( wasPaused ) {
|
||||||
dispatchEvent( 'resumed' );
|
dispatchEvent({ type: 'resumed' });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1763,7 +1771,7 @@ export default function( revealElement, options ) {
|
||||||
document.documentElement.classList.add( state[i] );
|
document.documentElement.classList.add( state[i] );
|
||||||
|
|
||||||
// Dispatch custom event matching the state's name
|
// Dispatch custom event matching the state's name
|
||||||
dispatchEvent( state[i] );
|
dispatchEvent({ type: state[i] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the remains of the previous state
|
// Clean up the remains of the previous state
|
||||||
|
@ -1772,12 +1780,15 @@ export default function( revealElement, options ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if( slideChanged ) {
|
if( slideChanged ) {
|
||||||
dispatchEvent( 'slidechanged', {
|
dispatchEvent({
|
||||||
'indexh': indexh,
|
type: 'slidechanged',
|
||||||
'indexv': indexv,
|
data: {
|
||||||
'previousSlide': previousSlide,
|
indexh,
|
||||||
'currentSlide': currentSlide,
|
indexv,
|
||||||
'origin': o
|
previousSlide,
|
||||||
|
currentSlide,
|
||||||
|
origin: o
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2035,14 +2046,26 @@ export default function( revealElement, options ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let slide = slides[index];
|
||||||
|
let wasPresent = slide.classList.contains( 'present' );
|
||||||
|
|
||||||
// Mark the current slide as present
|
// Mark the current slide as present
|
||||||
slides[index].classList.add( 'present' );
|
slide.classList.add( 'present' );
|
||||||
slides[index].removeAttribute( 'hidden' );
|
slide.removeAttribute( 'hidden' );
|
||||||
slides[index].removeAttribute( 'aria-hidden' );
|
slide.removeAttribute( 'aria-hidden' );
|
||||||
|
|
||||||
|
if( !wasPresent ) {
|
||||||
|
// Dispatch an event indicating the slide is now visible
|
||||||
|
dispatchEvent({
|
||||||
|
target: slide,
|
||||||
|
type: 'visible',
|
||||||
|
bubbles: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// If this slide has a state associated with it, add it
|
// If this slide has a state associated with it, add it
|
||||||
// onto the current state of the deck
|
// onto the current state of the deck
|
||||||
let slideState = slides[index].getAttribute( 'data-state' );
|
let slideState = slide.getAttribute( 'data-state' );
|
||||||
if( slideState ) {
|
if( slideState ) {
|
||||||
state = state.concat( slideState.split( ' ' ) );
|
state = state.concat( slideState.split( ' ' ) );
|
||||||
}
|
}
|
||||||
|
@ -2947,7 +2970,7 @@ export default function( revealElement, options ) {
|
||||||
|
|
||||||
if( autoSlide && !autoSlidePaused ) {
|
if( autoSlide && !autoSlidePaused ) {
|
||||||
autoSlidePaused = true;
|
autoSlidePaused = true;
|
||||||
dispatchEvent( 'autoslidepaused' );
|
dispatchEvent({ type: 'autoslidepaused' });
|
||||||
clearTimeout( autoSlideTimeout );
|
clearTimeout( autoSlideTimeout );
|
||||||
|
|
||||||
if( autoSlidePlayer ) {
|
if( autoSlidePlayer ) {
|
||||||
|
@ -2961,7 +2984,7 @@ export default function( revealElement, options ) {
|
||||||
|
|
||||||
if( autoSlide && autoSlidePaused ) {
|
if( autoSlide && autoSlidePaused ) {
|
||||||
autoSlidePaused = false;
|
autoSlidePaused = false;
|
||||||
dispatchEvent( 'autoslideresumed' );
|
dispatchEvent({ type: 'autoslideresumed' });
|
||||||
cueAutoSlide();
|
cueAutoSlide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,15 @@
|
||||||
if( config.highlightOnLoad ) {
|
if( config.highlightOnLoad ) {
|
||||||
RevealHighlight.highlightBlock( block );
|
RevealHighlight.highlightBlock( block );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
// If we're printing to PDF, scroll the code highlights of
|
||||||
|
// all blocks in the deck into view at once
|
||||||
|
Reveal.addEventListener( 'pdf-ready', function() {
|
||||||
|
[].slice.call( document.querySelectorAll( '.reveal pre code[data-line-numbers].current-fragment' ) ).forEach( function( block ) {
|
||||||
|
RevealHighlight.scrollHighlightedLineIntoView( block, {}, true );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -122,6 +131,8 @@
|
||||||
if( block.hasAttribute( 'data-line-numbers' ) ) {
|
if( block.hasAttribute( 'data-line-numbers' ) ) {
|
||||||
hljs.lineNumbersBlock( block, { singleLine: true } );
|
hljs.lineNumbersBlock( block, { singleLine: true } );
|
||||||
|
|
||||||
|
var scrollState = { currentBlock: block };
|
||||||
|
|
||||||
// If there is at least one highlight step, generate
|
// If there is at least one highlight step, generate
|
||||||
// fragments
|
// fragments
|
||||||
var highlightSteps = RevealHighlight.deserializeHighlightSteps( block.getAttribute( 'data-line-numbers' ) );
|
var highlightSteps = RevealHighlight.deserializeHighlightSteps( block.getAttribute( 'data-line-numbers' ) );
|
||||||
|
@ -130,6 +141,7 @@
|
||||||
// If the original code block has a fragment-index,
|
// If the original code block has a fragment-index,
|
||||||
// each clone should follow in an incremental sequence
|
// each clone should follow in an incremental sequence
|
||||||
var fragmentIndex = parseInt( block.getAttribute( 'data-fragment-index' ), 10 );
|
var fragmentIndex = parseInt( block.getAttribute( 'data-fragment-index' ), 10 );
|
||||||
|
|
||||||
if( typeof fragmentIndex !== 'number' || isNaN( fragmentIndex ) ) {
|
if( typeof fragmentIndex !== 'number' || isNaN( fragmentIndex ) ) {
|
||||||
fragmentIndex = null;
|
fragmentIndex = null;
|
||||||
}
|
}
|
||||||
|
@ -151,6 +163,10 @@
|
||||||
fragmentBlock.removeAttribute( 'data-fragment-index' );
|
fragmentBlock.removeAttribute( 'data-fragment-index' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scroll highlights into view as we step through them
|
||||||
|
fragmentBlock.addEventListener( 'visible', RevealHighlight.scrollHighlightedLineIntoView.bind( RevealHighlight, fragmentBlock, scrollState ) );
|
||||||
|
fragmentBlock.addEventListener( 'hidden', RevealHighlight.scrollHighlightedLineIntoView.bind( RevealHighlight, fragmentBlock.previousSibling, scrollState ) );
|
||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
block.removeAttribute( 'data-fragment-index' )
|
block.removeAttribute( 'data-fragment-index' )
|
||||||
|
@ -158,12 +174,116 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scroll the first highlight into view when the slide
|
||||||
|
// becomes visible. Note supported in IE11 since it lacks
|
||||||
|
// support for Element.closest.
|
||||||
|
var slide = typeof block.closest === 'function' ? block.closest( 'section:not(.stack)' ) : null;
|
||||||
|
if( slide ) {
|
||||||
|
var scrollFirstHighlightIntoView = function() {
|
||||||
|
RevealHighlight.scrollHighlightedLineIntoView( block, scrollState, true );
|
||||||
|
slide.removeEventListener( 'visible', scrollFirstHighlightIntoView );
|
||||||
|
}
|
||||||
|
slide.addEventListener( 'visible', scrollFirstHighlightIntoView );
|
||||||
|
}
|
||||||
|
|
||||||
RevealHighlight.highlightLines( block );
|
RevealHighlight.highlightLines( block );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates scrolling to the first highlighted line
|
||||||
|
* in the given code block.
|
||||||
|
*/
|
||||||
|
scrollHighlightedLineIntoView: function( block, scrollState, skipAnimation ) {
|
||||||
|
|
||||||
|
cancelAnimationFrame( scrollState.animationFrameID );
|
||||||
|
|
||||||
|
// Match the scroll position of the currently visible
|
||||||
|
// code block
|
||||||
|
if( scrollState.currentBlock ) {
|
||||||
|
block.scrollTop = scrollState.currentBlock.scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember the current code block so that we can match
|
||||||
|
// its scroll position when showing/hiding fragments
|
||||||
|
scrollState.currentBlock = block;
|
||||||
|
|
||||||
|
var highlightBounds = this.getHighlightedLineBounds( block )
|
||||||
|
var viewportHeight = block.offsetHeight;
|
||||||
|
|
||||||
|
// Subtract padding from the viewport height
|
||||||
|
var blockStyles = getComputedStyle( block );
|
||||||
|
viewportHeight -= parseInt( blockStyles.paddingTop ) + parseInt( blockStyles.paddingBottom );
|
||||||
|
|
||||||
|
// Scroll position which centers all highlights
|
||||||
|
var startTop = block.scrollTop;
|
||||||
|
var targetTop = highlightBounds.top + ( Math.min( highlightBounds.bottom - highlightBounds.top, viewportHeight ) - viewportHeight ) / 2;
|
||||||
|
|
||||||
|
// Account for offsets in position applied to the
|
||||||
|
// <table> that holds our lines of code
|
||||||
|
var lineTable = block.querySelector( '.hljs-ln' );
|
||||||
|
if( lineTable ) targetTop += lineTable.offsetTop - parseInt( blockStyles.paddingTop );
|
||||||
|
|
||||||
|
// Make sure the scroll target is within bounds
|
||||||
|
targetTop = Math.max( Math.min( targetTop, block.scrollHeight - viewportHeight ), 0 );
|
||||||
|
|
||||||
|
if( skipAnimation === true || startTop === targetTop ) {
|
||||||
|
block.scrollTop = targetTop;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Don't attempt to scroll if there is no overflow
|
||||||
|
if( block.scrollHeight <= viewportHeight ) return;
|
||||||
|
|
||||||
|
var time = 0;
|
||||||
|
var animate = function() {
|
||||||
|
time = Math.min( time + 0.02, 1 );
|
||||||
|
|
||||||
|
// Update our eased scroll position
|
||||||
|
block.scrollTop = startTop + ( targetTop - startTop ) * RevealHighlight.easeInOutQuart( time );
|
||||||
|
|
||||||
|
// Keep animating unless we've reached the end
|
||||||
|
if( time < 1 ) {
|
||||||
|
scrollState.animationFrameID = requestAnimationFrame( animate );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
animate();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The easing function used when scrolling.
|
||||||
|
*/
|
||||||
|
easeInOutQuart: function( t ) {
|
||||||
|
|
||||||
|
// easeInOutQuart
|
||||||
|
return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
getHighlightedLineBounds: function( block ) {
|
||||||
|
|
||||||
|
var highlightedLines = block.querySelectorAll( '.highlight-line' );
|
||||||
|
if( highlightedLines.length === 0 ) {
|
||||||
|
return { top: 0, bottom: 0 };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var firstHighlight = highlightedLines[0];
|
||||||
|
var lastHighlight = highlightedLines[ highlightedLines.length -1 ];
|
||||||
|
|
||||||
|
return {
|
||||||
|
top: firstHighlight.offsetTop,
|
||||||
|
bottom: lastHighlight.offsetTop + lastHighlight.offsetHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visually emphasize specific lines within a code block.
|
* Visually emphasize specific lines within a code block.
|
||||||
* This only works on blocks with line numbering turned on.
|
* This only works on blocks with line numbering turned on.
|
||||||
|
|
Loading…
Reference in New Issue