| Christophe Dervieux | 8afae13 | 2021-12-06 15:16:42 +0100 | [diff] [blame] | 1 | import hljs from 'highlight.js'; | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 2 |  | 
| Christophe Dervieux | 8afae13 | 2021-12-06 15:16:42 +0100 | [diff] [blame] | 3 | /* highlightjs-line-numbers.js 2.8.0 | (C) 2018 Yauheni Pakala | MIT License | github.com/wcoder/highlightjs-line-numbers.js */ | 
 | 4 | !function(r,o){"use strict";var e,i="hljs-ln",l="hljs-ln-line",h="hljs-ln-code",s="hljs-ln-numbers",c="hljs-ln-n",m="data-line-number",a=/\r\n|\r|\n/g;function u(e){for(var n=e.toString(),t=e.anchorNode;"TD"!==t.nodeName;)t=t.parentNode;for(var r=e.focusNode;"TD"!==r.nodeName;)r=r.parentNode;var o=parseInt(t.dataset.lineNumber),a=parseInt(r.dataset.lineNumber);if(o==a)return n;var i,l=t.textContent,s=r.textContent;for(a<o&&(i=o,o=a,a=i,i=l,l=s,s=i);0!==n.indexOf(l);)l=l.slice(1);for(;-1===n.lastIndexOf(s);)s=s.slice(0,-1);for(var c=l,u=function(e){for(var n=e;"TABLE"!==n.nodeName;)n=n.parentNode;return n}(t),d=o+1;d<a;++d){var f=p('.{0}[{1}="{2}"]',[h,m,d]);c+="\n"+u.querySelector(f).textContent}return c+="\n"+s}function n(e){try{var n=o.querySelectorAll("code.hljs,code.nohighlight");for(var t in n)n.hasOwnProperty(t)&&(n[t].classList.contains("nohljsln")||d(n[t],e))}catch(e){r.console.error("LineNumbers error: ",e)}}function d(e,n){if("object"==typeof e)e.innerHTML=f(e,n)}function f(e,n){var t,r,o=(t=e,{singleLine:function(e){return!!e.singleLine&&e.singleLine}(r=(r=n)||{}),startFrom:function(e,n){var t=1;isFinite(n.startFrom)&&(t=n.startFrom);var r=function(e,n){return e.hasAttribute(n)?e.getAttribute(n):null}(e,"data-ln-start-from");return null!==r&&(t=function(e,n){if(!e)return n;var t=Number(e);return isFinite(t)?t:n}(r,1)),t}(t,r)});return function e(n){var t=n.childNodes;for(var r in t){var o;t.hasOwnProperty(r)&&(o=t[r],0<(o.textContent.trim().match(a)||[]).length&&(0<o.childNodes.length?e(o):v(o.parentNode)))}}(e),function(e,n){var t=g(e);""===t[t.length-1].trim()&&t.pop();if(1<t.length||n.singleLine){for(var r="",o=0,a=t.length;o<a;o++)r+=p('<tr><td class="{0} {1}" {3}="{5}"><div class="{2}" {3}="{5}"></div></td><td class="{0} {4}" {3}="{5}">{6}</td></tr>',[l,s,c,m,h,o+n.startFrom,0<t[o].length?t[o]:" "]);return p('<table class="{0}">{1}</table>',[i,r])}return e}(e.innerHTML,o)}function v(e){var n=e.className;if(/hljs-/.test(n)){for(var t=g(e.innerHTML),r=0,o="";r<t.length;r++){o+=p('<span class="{0}">{1}</span>\n',[n,0<t[r].length?t[r]:" "])}e.innerHTML=o.trim()}}function g(e){return 0===e.length?[]:e.split(a)}function p(e,t){return e.replace(/\{(\d+)\}/g,function(e,n){return void 0!==t[n]?t[n]:e})}hljs?(hljs.initLineNumbersOnLoad=function(e){"interactive"===o.readyState||"complete"===o.readyState?n(e):r.addEventListener("DOMContentLoaded",function(){n(e)})},hljs.lineNumbersBlock=d,hljs.lineNumbersValue=function(e,n){if("string"!=typeof e)return;var t=document.createElement("code");return t.innerHTML=e,f(t,n)},(e=o.createElement("style")).type="text/css",e.innerHTML=p(".{0}{border-collapse:collapse}.{0} td{padding:0}.{1}:before{content:attr({2})}",[i,c,m]),o.getElementsByTagName("head")[0].appendChild(e)):r.console.error("highlight.js not detected!"),document.addEventListener("copy",function(e){var n,t=window.getSelection();!function(e){for(var n=e;n;){if(n.className&&-1!==n.className.indexOf("hljs-ln-code"))return 1;n=n.parentNode}}(t.anchorNode)||(n=-1!==window.navigator.userAgent.indexOf("Edge")?u(t):t.toString(),e.clipboardData.setData("text/plain",n),e.preventDefault())})}(window,document); | 
 | 5 |  | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 6 |  | 
 | 7 | /*! | 
 | 8 |  * reveal.js plugin that adds syntax highlight support. | 
 | 9 |  */ | 
 | 10 |  | 
 | 11 | const Plugin = { | 
 | 12 |  | 
 | 13 | 	id: 'highlight', | 
 | 14 |  | 
 | 15 | 	HIGHLIGHT_STEP_DELIMITER: '|', | 
 | 16 | 	HIGHLIGHT_LINE_DELIMITER: ',', | 
 | 17 | 	HIGHLIGHT_LINE_RANGE_DELIMITER: '-', | 
 | 18 |  | 
| Christophe Dervieux | 8afae13 | 2021-12-06 15:16:42 +0100 | [diff] [blame] | 19 | 	hljs, | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 20 |  | 
 | 21 | 	/** | 
| Marc Kupietz | 09b7575 | 2023-10-07 09:32:19 +0200 | [diff] [blame^] | 22 | 	 * Highlights code blocks within the given deck. | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 23 | 	 * | 
 | 24 | 	 * Note that this can be called multiple times if | 
 | 25 | 	 * there are multiple presentations on one page. | 
 | 26 | 	 * | 
 | 27 | 	 * @param {Reveal} reveal the reveal.js instance | 
 | 28 | 	 */ | 
 | 29 | 	init: function( reveal ) { | 
 | 30 |  | 
 | 31 | 		// Read the plugin config options and provide fallbacks | 
 | 32 | 		let config = reveal.getConfig().highlight || {}; | 
| Christophe Dervieux | 8afae13 | 2021-12-06 15:16:42 +0100 | [diff] [blame] | 33 |  | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 34 | 		config.highlightOnLoad = typeof config.highlightOnLoad === 'boolean' ? config.highlightOnLoad : true; | 
 | 35 | 		config.escapeHTML = typeof config.escapeHTML === 'boolean' ? config.escapeHTML : true; | 
 | 36 |  | 
 | 37 | 		Array.from( reveal.getRevealElement().querySelectorAll( 'pre code' ) ).forEach( block => { | 
 | 38 |  | 
| Christophe Dervieux | 8afae13 | 2021-12-06 15:16:42 +0100 | [diff] [blame] | 39 | 			block.parentNode.classList.add('code-wrapper'); | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 40 |  | 
 | 41 | 			// Code can optionally be wrapped in script template to avoid | 
 | 42 | 			// HTML being parsed by the browser (i.e. when you need to | 
 | 43 | 			// include <, > or & in your code). | 
 | 44 | 			let substitute = block.querySelector( 'script[type="text/template"]' ); | 
 | 45 | 			if( substitute ) { | 
 | 46 | 				// textContent handles the HTML entity escapes for us | 
 | 47 | 				block.textContent = substitute.innerHTML; | 
 | 48 | 			} | 
 | 49 |  | 
 | 50 | 			// Trim whitespace if the "data-trim" attribute is present | 
 | 51 | 			if( block.hasAttribute( 'data-trim' ) && typeof block.innerHTML.trim === 'function' ) { | 
 | 52 | 				block.innerHTML = betterTrim( block ); | 
 | 53 | 			} | 
 | 54 |  | 
 | 55 | 			// Escape HTML tags unless the "data-noescape" attrbute is present | 
 | 56 | 			if( config.escapeHTML && !block.hasAttribute( 'data-noescape' )) { | 
 | 57 | 				block.innerHTML = block.innerHTML.replace( /</g,"<").replace(/>/g, '>' ); | 
 | 58 | 			} | 
 | 59 |  | 
 | 60 | 			// Re-highlight when focus is lost (for contenteditable code) | 
 | 61 | 			block.addEventListener( 'focusout', function( event ) { | 
 | 62 | 				hljs.highlightElement( event.currentTarget ); | 
 | 63 | 			}, false ); | 
 | 64 |  | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 65 | 		} ); | 
 | 66 |  | 
| Christophe Dervieux | 8afae13 | 2021-12-06 15:16:42 +0100 | [diff] [blame] | 67 | 		// Triggers a callback function before we trigger highlighting | 
 | 68 | 		if( typeof config.beforeHighlight === 'function' ) { | 
 | 69 | 			config.beforeHighlight( hljs ); | 
 | 70 | 		} | 
 | 71 |  | 
 | 72 | 		// Run initial highlighting for all code | 
 | 73 | 		if( config.highlightOnLoad ) { | 
 | 74 | 			Array.from( reveal.getRevealElement().querySelectorAll( 'pre code' ) ).forEach( block => { | 
 | 75 | 				Plugin.highlightBlock( block ); | 
 | 76 | 			} ); | 
 | 77 | 		} | 
 | 78 |  | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 79 | 		// If we're printing to PDF, scroll the code highlights of | 
 | 80 | 		// all blocks in the deck into view at once | 
 | 81 | 		reveal.on( 'pdf-ready', function() { | 
 | 82 | 			[].slice.call( reveal.getRevealElement().querySelectorAll( 'pre code[data-line-numbers].current-fragment' ) ).forEach( function( block ) { | 
 | 83 | 				Plugin.scrollHighlightedLineIntoView( block, {}, true ); | 
 | 84 | 			} ); | 
 | 85 | 		} ); | 
 | 86 |  | 
 | 87 | 	}, | 
 | 88 |  | 
 | 89 | 	/** | 
 | 90 | 	 * Highlights a code block. If the <code> node has the | 
 | 91 | 	 * 'data-line-numbers' attribute we also generate slide | 
 | 92 | 	 * numbers. | 
 | 93 | 	 * | 
 | 94 | 	 * If the block contains multiple line highlight steps, | 
 | 95 | 	 * we clone the block and create a fragment for each step. | 
 | 96 | 	 */ | 
 | 97 | 	highlightBlock: function( block ) { | 
 | 98 |  | 
 | 99 | 		hljs.highlightElement( block ); | 
 | 100 |  | 
 | 101 | 		// Don't generate line numbers for empty code blocks | 
 | 102 | 		if( block.innerHTML.trim().length === 0 ) return; | 
 | 103 |  | 
 | 104 | 		if( block.hasAttribute( 'data-line-numbers' ) ) { | 
 | 105 | 			hljs.lineNumbersBlock( block, { singleLine: true } ); | 
 | 106 |  | 
 | 107 | 			var scrollState = { currentBlock: block }; | 
 | 108 |  | 
| Marc Kupietz | 09b7575 | 2023-10-07 09:32:19 +0200 | [diff] [blame^] | 109 | 			// If there is more than one highlight step, generate | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 110 | 			// fragments | 
 | 111 | 			var highlightSteps = Plugin.deserializeHighlightSteps( block.getAttribute( 'data-line-numbers' ) ); | 
 | 112 | 			if( highlightSteps.length > 1 ) { | 
 | 113 |  | 
 | 114 | 				// If the original code block has a fragment-index, | 
 | 115 | 				// each clone should follow in an incremental sequence | 
 | 116 | 				var fragmentIndex = parseInt( block.getAttribute( 'data-fragment-index' ), 10 ); | 
 | 117 |  | 
 | 118 | 				if( typeof fragmentIndex !== 'number' || isNaN( fragmentIndex ) ) { | 
 | 119 | 					fragmentIndex = null; | 
 | 120 | 				} | 
 | 121 |  | 
 | 122 | 				// Generate fragments for all steps except the original block | 
 | 123 | 				highlightSteps.slice(1).forEach( function( highlight ) { | 
 | 124 |  | 
 | 125 | 					var fragmentBlock = block.cloneNode( true ); | 
 | 126 | 					fragmentBlock.setAttribute( 'data-line-numbers', Plugin.serializeHighlightSteps( [ highlight ] ) ); | 
 | 127 | 					fragmentBlock.classList.add( 'fragment' ); | 
 | 128 | 					block.parentNode.appendChild( fragmentBlock ); | 
 | 129 | 					Plugin.highlightLines( fragmentBlock ); | 
 | 130 |  | 
 | 131 | 					if( typeof fragmentIndex === 'number' ) { | 
 | 132 | 						fragmentBlock.setAttribute( 'data-fragment-index', fragmentIndex ); | 
 | 133 | 						fragmentIndex += 1; | 
 | 134 | 					} | 
 | 135 | 					else { | 
 | 136 | 						fragmentBlock.removeAttribute( 'data-fragment-index' ); | 
 | 137 | 					} | 
 | 138 |  | 
 | 139 | 					// Scroll highlights into view as we step through them | 
 | 140 | 					fragmentBlock.addEventListener( 'visible', Plugin.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock, scrollState ) ); | 
 | 141 | 					fragmentBlock.addEventListener( 'hidden', Plugin.scrollHighlightedLineIntoView.bind( Plugin, fragmentBlock.previousSibling, scrollState ) ); | 
 | 142 |  | 
 | 143 | 				} ); | 
 | 144 |  | 
| Marc Kupietz | 09b7575 | 2023-10-07 09:32:19 +0200 | [diff] [blame^] | 145 | 				block.removeAttribute( 'data-fragment-index' ); | 
| Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame] | 146 | 				block.setAttribute( 'data-line-numbers', Plugin.serializeHighlightSteps( [ highlightSteps[0] ] ) ); | 
 | 147 |  | 
 | 148 | 			} | 
 | 149 |  | 
 | 150 | 			// Scroll the first highlight into view when the slide | 
 | 151 | 			// becomes visible. Note supported in IE11 since it lacks | 
 | 152 | 			// support for Element.closest. | 
 | 153 | 			var slide = typeof block.closest === 'function' ? block.closest( 'section:not(.stack)' ) : null; | 
 | 154 | 			if( slide ) { | 
 | 155 | 				var scrollFirstHighlightIntoView = function() { | 
 | 156 | 					Plugin.scrollHighlightedLineIntoView( block, scrollState, true ); | 
 | 157 | 					slide.removeEventListener( 'visible', scrollFirstHighlightIntoView ); | 
 | 158 | 				} | 
 | 159 | 				slide.addEventListener( 'visible', scrollFirstHighlightIntoView ); | 
 | 160 | 			} | 
 | 161 |  | 
 | 162 | 			Plugin.highlightLines( block ); | 
 | 163 |  | 
 | 164 | 		} | 
 | 165 |  | 
 | 166 | 	}, | 
 | 167 |  | 
 | 168 | 	/** | 
 | 169 | 	 * Animates scrolling to the first highlighted line | 
 | 170 | 	 * in the given code block. | 
 | 171 | 	 */ | 
 | 172 | 	scrollHighlightedLineIntoView: function( block, scrollState, skipAnimation ) { | 
 | 173 |  | 
 | 174 | 		cancelAnimationFrame( scrollState.animationFrameID ); | 
 | 175 |  | 
 | 176 | 		// Match the scroll position of the currently visible | 
 | 177 | 		// code block | 
 | 178 | 		if( scrollState.currentBlock ) { | 
 | 179 | 			block.scrollTop = scrollState.currentBlock.scrollTop; | 
 | 180 | 		} | 
 | 181 |  | 
 | 182 | 		// Remember the current code block so that we can match | 
 | 183 | 		// its scroll position when showing/hiding fragments | 
 | 184 | 		scrollState.currentBlock = block; | 
 | 185 |  | 
 | 186 | 		var highlightBounds = this.getHighlightedLineBounds( block ) | 
 | 187 | 		var viewportHeight = block.offsetHeight; | 
 | 188 |  | 
 | 189 | 		// Subtract padding from the viewport height | 
 | 190 | 		var blockStyles = getComputedStyle( block ); | 
 | 191 | 		viewportHeight -= parseInt( blockStyles.paddingTop ) + parseInt( blockStyles.paddingBottom ); | 
 | 192 |  | 
 | 193 | 		// Scroll position which centers all highlights | 
 | 194 | 		var startTop = block.scrollTop; | 
 | 195 | 		var targetTop = highlightBounds.top + ( Math.min( highlightBounds.bottom - highlightBounds.top, viewportHeight ) - viewportHeight ) / 2; | 
 | 196 |  | 
 | 197 | 		// Account for offsets in position applied to the | 
 | 198 | 		// <table> that holds our lines of code | 
 | 199 | 		var lineTable = block.querySelector( '.hljs-ln' ); | 
 | 200 | 		if( lineTable ) targetTop += lineTable.offsetTop - parseInt( blockStyles.paddingTop ); | 
 | 201 |  | 
 | 202 | 		// Make sure the scroll target is within bounds | 
 | 203 | 		targetTop = Math.max( Math.min( targetTop, block.scrollHeight - viewportHeight ), 0 ); | 
 | 204 |  | 
 | 205 | 		if( skipAnimation === true || startTop === targetTop ) { | 
 | 206 | 			block.scrollTop = targetTop; | 
 | 207 | 		} | 
 | 208 | 		else { | 
 | 209 |  | 
 | 210 | 			// Don't attempt to scroll if there is no overflow | 
 | 211 | 			if( block.scrollHeight <= viewportHeight ) return; | 
 | 212 |  | 
 | 213 | 			var time = 0; | 
 | 214 | 			var animate = function() { | 
 | 215 | 				time = Math.min( time + 0.02, 1 ); | 
 | 216 |  | 
 | 217 | 				// Update our eased scroll position | 
 | 218 | 				block.scrollTop = startTop + ( targetTop - startTop ) * Plugin.easeInOutQuart( time ); | 
 | 219 |  | 
 | 220 | 				// Keep animating unless we've reached the end | 
 | 221 | 				if( time < 1 ) { | 
 | 222 | 					scrollState.animationFrameID = requestAnimationFrame( animate ); | 
 | 223 | 				} | 
 | 224 | 			}; | 
 | 225 |  | 
 | 226 | 			animate(); | 
 | 227 |  | 
 | 228 | 		} | 
 | 229 |  | 
 | 230 | 	}, | 
 | 231 |  | 
 | 232 | 	/** | 
 | 233 | 	 * The easing function used when scrolling. | 
 | 234 | 	 */ | 
 | 235 | 	easeInOutQuart: function( t ) { | 
 | 236 |  | 
 | 237 | 		// easeInOutQuart | 
 | 238 | 		return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t; | 
 | 239 |  | 
 | 240 | 	}, | 
 | 241 |  | 
 | 242 | 	getHighlightedLineBounds: function( block ) { | 
 | 243 |  | 
 | 244 | 		var highlightedLines = block.querySelectorAll( '.highlight-line' ); | 
 | 245 | 		if( highlightedLines.length === 0 ) { | 
 | 246 | 			return { top: 0, bottom: 0 }; | 
 | 247 | 		} | 
 | 248 | 		else { | 
 | 249 | 			var firstHighlight = highlightedLines[0]; | 
 | 250 | 			var lastHighlight = highlightedLines[ highlightedLines.length -1 ]; | 
 | 251 |  | 
 | 252 | 			return { | 
 | 253 | 				top: firstHighlight.offsetTop, | 
 | 254 | 				bottom: lastHighlight.offsetTop + lastHighlight.offsetHeight | 
 | 255 | 			} | 
 | 256 | 		} | 
 | 257 |  | 
 | 258 | 	}, | 
 | 259 |  | 
 | 260 | 	/** | 
 | 261 | 	 * Visually emphasize specific lines within a code block. | 
 | 262 | 	 * This only works on blocks with line numbering turned on. | 
 | 263 | 	 * | 
 | 264 | 	 * @param {HTMLElement} block a <code> block | 
 | 265 | 	 * @param {String} [linesToHighlight] The lines that should be | 
 | 266 | 	 * highlighted in this format: | 
 | 267 | 	 * "1" 		= highlights line 1 | 
 | 268 | 	 * "2,5"	= highlights lines 2 & 5 | 
 | 269 | 	 * "2,5-7"	= highlights lines 2, 5, 6 & 7 | 
 | 270 | 	 */ | 
 | 271 | 	highlightLines: function( block, linesToHighlight ) { | 
 | 272 |  | 
 | 273 | 		var highlightSteps = Plugin.deserializeHighlightSteps( linesToHighlight || block.getAttribute( 'data-line-numbers' ) ); | 
 | 274 |  | 
 | 275 | 		if( highlightSteps.length ) { | 
 | 276 |  | 
 | 277 | 			highlightSteps[0].forEach( function( highlight ) { | 
 | 278 |  | 
 | 279 | 				var elementsToHighlight = []; | 
 | 280 |  | 
 | 281 | 				// Highlight a range | 
 | 282 | 				if( typeof highlight.end === 'number' ) { | 
 | 283 | 					elementsToHighlight = [].slice.call( block.querySelectorAll( 'table tr:nth-child(n+'+highlight.start+'):nth-child(-n+'+highlight.end+')' ) ); | 
 | 284 | 				} | 
 | 285 | 				// Highlight a single line | 
 | 286 | 				else if( typeof highlight.start === 'number' ) { | 
 | 287 | 					elementsToHighlight = [].slice.call( block.querySelectorAll( 'table tr:nth-child('+highlight.start+')' ) ); | 
 | 288 | 				} | 
 | 289 |  | 
 | 290 | 				if( elementsToHighlight.length ) { | 
 | 291 | 					elementsToHighlight.forEach( function( lineElement ) { | 
 | 292 | 						lineElement.classList.add( 'highlight-line' ); | 
 | 293 | 					} ); | 
 | 294 |  | 
 | 295 | 					block.classList.add( 'has-highlights' ); | 
 | 296 | 				} | 
 | 297 |  | 
 | 298 | 			} ); | 
 | 299 |  | 
 | 300 | 		} | 
 | 301 |  | 
 | 302 | 	}, | 
 | 303 |  | 
 | 304 | 	/** | 
 | 305 | 	 * Parses and formats a user-defined string of line | 
 | 306 | 	 * numbers to highlight. | 
 | 307 | 	 * | 
 | 308 | 	 * @example | 
 | 309 | 	 * Plugin.deserializeHighlightSteps( '1,2|3,5-10' ) | 
 | 310 | 	 * // [ | 
 | 311 | 	 * //   [ { start: 1 }, { start: 2 } ], | 
 | 312 | 	 * //   [ { start: 3 }, { start: 5, end: 10 } ] | 
 | 313 | 	 * // ] | 
 | 314 | 	 */ | 
 | 315 | 	deserializeHighlightSteps: function( highlightSteps ) { | 
 | 316 |  | 
 | 317 | 		// Remove whitespace | 
 | 318 | 		highlightSteps = highlightSteps.replace( /\s/g, '' ); | 
 | 319 |  | 
 | 320 | 		// Divide up our line number groups | 
 | 321 | 		highlightSteps = highlightSteps.split( Plugin.HIGHLIGHT_STEP_DELIMITER ); | 
 | 322 |  | 
 | 323 | 		return highlightSteps.map( function( highlights ) { | 
 | 324 |  | 
 | 325 | 			return highlights.split( Plugin.HIGHLIGHT_LINE_DELIMITER ).map( function( highlight ) { | 
 | 326 |  | 
 | 327 | 				// Parse valid line numbers | 
 | 328 | 				if( /^[\d-]+$/.test( highlight ) ) { | 
 | 329 |  | 
 | 330 | 					highlight = highlight.split( Plugin.HIGHLIGHT_LINE_RANGE_DELIMITER ); | 
 | 331 |  | 
 | 332 | 					var lineStart = parseInt( highlight[0], 10 ), | 
 | 333 | 						lineEnd = parseInt( highlight[1], 10 ); | 
 | 334 |  | 
 | 335 | 					if( isNaN( lineEnd ) ) { | 
 | 336 | 						return { | 
 | 337 | 							start: lineStart | 
 | 338 | 						}; | 
 | 339 | 					} | 
 | 340 | 					else { | 
 | 341 | 						return { | 
 | 342 | 							start: lineStart, | 
 | 343 | 							end: lineEnd | 
 | 344 | 						}; | 
 | 345 | 					} | 
 | 346 |  | 
 | 347 | 				} | 
 | 348 | 				// If no line numbers are provided, no code will be highlighted | 
 | 349 | 				else { | 
 | 350 |  | 
 | 351 | 					return {}; | 
 | 352 |  | 
 | 353 | 				} | 
 | 354 |  | 
 | 355 | 			} ); | 
 | 356 |  | 
 | 357 | 		} ); | 
 | 358 |  | 
 | 359 | 	}, | 
 | 360 |  | 
 | 361 | 	/** | 
 | 362 | 	 * Serializes parsed line number data into a string so | 
 | 363 | 	 * that we can store it in the DOM. | 
 | 364 | 	 */ | 
 | 365 | 	serializeHighlightSteps: function( highlightSteps ) { | 
 | 366 |  | 
 | 367 | 		return highlightSteps.map( function( highlights ) { | 
 | 368 |  | 
 | 369 | 			return highlights.map( function( highlight ) { | 
 | 370 |  | 
 | 371 | 				// Line range | 
 | 372 | 				if( typeof highlight.end === 'number' ) { | 
 | 373 | 					return highlight.start + Plugin.HIGHLIGHT_LINE_RANGE_DELIMITER + highlight.end; | 
 | 374 | 				} | 
 | 375 | 				// Single line | 
 | 376 | 				else if( typeof highlight.start === 'number' ) { | 
 | 377 | 					return highlight.start; | 
 | 378 | 				} | 
 | 379 | 				// All lines | 
 | 380 | 				else { | 
 | 381 | 					return ''; | 
 | 382 | 				} | 
 | 383 |  | 
 | 384 | 			} ).join( Plugin.HIGHLIGHT_LINE_DELIMITER ); | 
 | 385 |  | 
 | 386 | 		} ).join( Plugin.HIGHLIGHT_STEP_DELIMITER ); | 
 | 387 |  | 
 | 388 | 	} | 
 | 389 |  | 
 | 390 | } | 
 | 391 |  | 
 | 392 | // Function to perform a better "data-trim" on code snippets | 
 | 393 | // Will slice an indentation amount on each line of the snippet (amount based on the line having the lowest indentation length) | 
 | 394 | function betterTrim(snippetEl) { | 
 | 395 | 	// Helper functions | 
 | 396 | 	function trimLeft(val) { | 
 | 397 | 		// Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill | 
 | 398 | 		return val.replace(/^[\s\uFEFF\xA0]+/g, ''); | 
 | 399 | 	} | 
 | 400 | 	function trimLineBreaks(input) { | 
 | 401 | 		var lines = input.split('\n'); | 
 | 402 |  | 
 | 403 | 		// Trim line-breaks from the beginning | 
 | 404 | 		for (var i = 0; i < lines.length; i++) { | 
 | 405 | 			if (lines[i].trim() === '') { | 
 | 406 | 				lines.splice(i--, 1); | 
 | 407 | 			} else break; | 
 | 408 | 		} | 
 | 409 |  | 
 | 410 | 		// Trim line-breaks from the end | 
 | 411 | 		for (var i = lines.length-1; i >= 0; i--) { | 
 | 412 | 			if (lines[i].trim() === '') { | 
 | 413 | 				lines.splice(i, 1); | 
 | 414 | 			} else break; | 
 | 415 | 		} | 
 | 416 |  | 
 | 417 | 		return lines.join('\n'); | 
 | 418 | 	} | 
 | 419 |  | 
 | 420 | 	// Main function for betterTrim() | 
 | 421 | 	return (function(snippetEl) { | 
 | 422 | 		var content = trimLineBreaks(snippetEl.innerHTML); | 
 | 423 | 		var lines = content.split('\n'); | 
 | 424 | 		// Calculate the minimum amount to remove on each line start of the snippet (can be 0) | 
 | 425 | 		var pad = lines.reduce(function(acc, line) { | 
 | 426 | 			if (line.length > 0 && trimLeft(line).length > 0 && acc > line.length - trimLeft(line).length) { | 
 | 427 | 				return line.length - trimLeft(line).length; | 
 | 428 | 			} | 
 | 429 | 			return acc; | 
 | 430 | 		}, Number.POSITIVE_INFINITY); | 
 | 431 | 		// Slice each line with this amount | 
 | 432 | 		return lines.map(function(line, index) { | 
 | 433 | 			return line.slice(pad); | 
 | 434 | 		}) | 
 | 435 | 		.join('\n'); | 
 | 436 | 	})(snippetEl); | 
 | 437 | } | 
 | 438 |  | 
 | 439 | export default () => Plugin; |