@@ -52,7 +52,9 @@ function attachFerrises(type) {
5252 continue ;
5353 }
5454
55- container . appendChild ( createFerris ( type , size ) ) ;
55+ let ferris = createFerris ( type , size )
56+ container . appendChild ( ferris ) ;
57+ giveFerrisSpace ( codeBlock , ferris , size ) ;
5658 }
5759}
5860
@@ -100,3 +102,72 @@ function createFerris(type, size) {
100102
101103 return a ;
102104}
105+
106+ /**
107+ * Put each line ending in a span. For each of those spans,
108+ * if Ferris might hide it, give it a safety buffer.
109+ * @param {HTMLElement } codeBlock
110+ * @param {HTMLAnchorElement } ferris
111+ * @param {'small' | 'large' } size
112+ */
113+ function giveFerrisSpace ( codeBlock , ferris , size ) {
114+ // sanity checking + lint awareness
115+ const ferrisImage = ferris . firstChild ;
116+ if ( ! ( ferrisImage instanceof HTMLImageElement ) ) {
117+ console . error ( "ferris should be <a> containing <img>" , ferris ) ;
118+ return ;
119+ }
120+
121+ /** @type {HTMLSpanElement[] } */
122+ const lineEndings = [ ] ; // line endings which might be hidden by Ferris
123+
124+ const walker = document . createTreeWalker ( codeBlock , NodeFilter . SHOW_TEXT ) ;
125+ const re = / ^ ( .* ?) \n ( .* ) $ / s
126+
127+ while ( walker . nextNode ( ) ) {
128+ const current = walker . currentNode ;
129+ const parent = current . parentNode ;
130+
131+ // sanity checking + lint awareness
132+ if ( ! ( current instanceof Text ) || ! parent ) {
133+ continue ;
134+ }
135+
136+ let re_results ;
137+ while ( re_results = current . textContent . match ( re ) ) {
138+ // text node contains newline
139+ const [ _ , beforeNewline , afterNewline ] = re_results ;
140+
141+ // line ending gets a span
142+ const lineEnd = document . createElement ( "span" ) ;
143+ lineEnd . textContent = beforeNewline ;
144+ lineEndings . push ( lineEnd ) ;
145+ parent . insertBefore ( lineEnd , current ) ;
146+
147+ // newline now stands alone
148+ parent . insertBefore ( document . createTextNode ( "\n" ) , current ) ;
149+
150+ // rest of the text
151+ current . textContent = afterNewline ;
152+ // current might still contain newlines, so we go again until it doesn't
153+ }
154+ }
155+
156+ codeBlock . normalize ( ) ; // not strictly necessary, but good practice to leave the DOM normalized
157+
158+ // setTimeout so getBoundingClientRect returns valid results
159+ setTimeout ( ( ) => {
160+ const f = ferrisImage . getBoundingClientRect ( ) ;
161+ lineEndings . forEach ( ( s ) => {
162+ const { bottom, top} = s . getBoundingClientRect ( ) ;
163+ if ( // vertical overlap between ferris and span
164+ ( bottom >= f . top && bottom <= f . bottom )
165+ || ( top >= f . top && top <= f . bottom )
166+ || ( f . top >= top && f . top <= bottom )
167+ ) {
168+ // buffer needed!
169+ s . classList . add ( "ferris-buffer-" + size ) ;
170+ }
171+ } ) ;
172+ } ) ;
173+ }
0 commit comments