Skip to content

Commit 6e09fee

Browse files
author
Nitai Sasson
committed
ferris buffer code cleanup
1 parent f95aed6 commit 6e09fee

File tree

2 files changed

+58
-54
lines changed

2 files changed

+58
-54
lines changed

ferris.css

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ body.ayu .not_desired_behavior {
1919
background: #501f21;
2020
}
2121

22+
:root {
23+
--ferris-large-width: 4.5em;
24+
--ferris-small-width: 2.3em;
25+
}
26+
2227
.ferris-container {
2328
position: absolute;
2429
z-index: 99;
@@ -33,11 +38,19 @@ body.ayu .not_desired_behavior {
3338
}
3439

3540
.ferris-large {
36-
width: 4.5em;
41+
width: var(--ferris-large-width);
3742
}
3843

3944
.ferris-small {
40-
width: 2.3em;
45+
width: var(--ferris-small-width);
46+
}
47+
48+
.ferris-buffer-large {
49+
margin-right: calc(var(--ferris-large-width) + 1.0em);
50+
}
51+
52+
.ferris-buffer-small {
53+
margin-right: calc(var(--ferris-small-width) + 0.5em);
4154
}
4255

4356
.ferris-explain {
@@ -65,7 +78,3 @@ pre:hover > .buttons button {
6578
visibility: visible;
6679
opacity: 1;
6780
}
68-
69-
.ferris-buffer {
70-
margin-right: 5em;
71-
}

ferris.js

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function attachFerrises(type) {
5454

5555
let ferris = createFerris(type, size)
5656
container.appendChild(ferris);
57-
addPadding(codeBlock, ferris);
57+
giveFerrisSpace(codeBlock, ferris, size);
5858
}
5959
}
6060

@@ -104,75 +104,70 @@ function createFerris(type, size) {
104104
}
105105

106106
/**
107-
*
107+
* Put each line ending in a span. For each of those spans,
108+
* if Ferris might hide it, give it a safety buffer.
108109
* @param {HTMLElement} codeBlock
109110
* @param {HTMLAnchorElement} ferris
111+
* @param {'small' | 'large'} size
110112
*/
111-
function addPadding(codeBlock, ferris) {
112-
// console.log("offsetHeight:", ferris.offsetHeight, "offsetWidth:", ferris.offsetWidth, "offsetLeft", ferris.offsetLeft, "offsetTop:", ferris.offsetTop, "offsetParent:", ferris.offsetParent);
113-
// console.log(ferris.getBoundingClientRect());
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+
}
114120

115-
const suspects = []; // array of spans which *might* be hidden by Ferris
121+
/** @type {HTMLSpanElement[]} */
122+
const lineEndings = []; // line endings which might be hidden by Ferris
116123

117124
const walker = document.createTreeWalker(codeBlock, NodeFilter.SHOW_TEXT);
118125
const re = /^(.*?)\n(.*)$/s
119126

120-
let linebreaksToFix = 10;
121-
while (linebreaksToFix > 0 && walker.nextNode()) {
127+
while (walker.nextNode()) {
122128
const current = walker.currentNode;
123129
const parent = current.parentNode;
124130

125-
// sanity checking + lint fixes
126-
if (!(current instanceof Text)) {
127-
console.log("!(current instanceof Text) shouldn't happen");
128-
continue;
129-
}
130-
if (!parent) {
131-
console.log("!parent shouldn't happen");
131+
// sanity checking + lint awareness
132+
if (!(current instanceof Text) || !parent) {
132133
continue;
133134
}
134135

135136
let re_results;
136-
// I know nodeValue is not null because current is Text
137-
current.splitText
138-
while (re_results = current.nodeValue.match(re)) {
139-
linebreaksToFix--;
140-
if (!linebreaksToFix) { break; }
137+
while (re_results = current.textContent.match(re)) {
138+
// text node contains newline
139+
const [_, beforeNewline, afterNewline] = re_results;
141140

142-
let [_, before, after] = re_results;
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);
143146

144-
let elemBefore = document.createElement("span");
145-
elemBefore.innerText = before;
146-
suspects.push(elemBefore);
147-
148-
parent.insertBefore(elemBefore, current);
147+
// newline now stands alone
149148
parent.insertBefore(document.createTextNode("\n"), current);
150-
current.nodeValue = after;
149+
150+
// rest of the text
151+
current.textContent = afterNewline;
152+
// current might still contain newlines, so we go again until it doesn't
151153
}
152154
}
153155

154156
codeBlock.normalize(); // not strictly necessary, but good practice to leave the DOM normalized
155157

156-
const actual_ferris = ferris.firstChild;
157-
158-
setTimeout(detectOverlap, 0, suspects, actual_ferris);
159-
}
160-
161-
/**
162-
*
163-
* @param {[HTMLSpanElement]} suspects
164-
* @param {HTMLAnchorElement} ferris
165-
*/
166-
function detectOverlap(suspects, ferris) {
167-
const f = ferris.getBoundingClientRect();
168-
suspects.forEach((s) => {
169-
const {bottom, top} = s.getBoundingClientRect();
170-
if ( // vertical overlap between ferris and span
171-
(bottom >= f.top && bottom <= f.bottom)
172-
|| (top >= f.top && top <= f.bottom)
173-
|| (f.top >= top && f.top <= bottom)
174-
) {
175-
s.classList.add("ferris-buffer");
176-
}
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+
});
177172
});
178173
}

0 commit comments

Comments
 (0)