Skip to content

Instantly share code, notes, and snippets.

@jmakeig
Last active March 15, 2019 06:47
Show Gist options
  • Save jmakeig/1a937ad817e6efbcc8f32c392d5f659e to your computer and use it in GitHub Desktop.
Save jmakeig/1a937ad817e6efbcc8f32c392d5f659e to your computer and use it in GitHub Desktop.
Single pass annotation rendering

There are libraries that can add highlights to existing markup. Because HTML does not allow for overlapping elements, for example V<a>W<b>X</a>Y</b>Z, these libraries split inline elements across boundaries, nesting part of overlapping portion under a sibling element. The previous example would be split as V<a>W</a><b><a>X</a>Y</b>Z. Note the <b><a>X</a>… designates the leftover portion of a that overlaps with b.

The objective here is to be able to render the content and the higlights in one pass. Rather than modifying the rendered content in situ in with highlights in a second pass, render the content and the highlights in a single pass. This is how you’d do it in React, for example.

body {
font-family: Helvetica, sans-serif;
line-height: 1.45;
}
.line {
font-family: "SF Mono", "Inconsolata", "Consolas", monospace;
margin: 1em 0;
}
mark {
background-color: rgba(241, 244, 48, 0.5);
outline: solid 0.5px #ccc;
}
mark[data-annotation="1"] {
background-color: rgb(66, 134, 244, 0.75);
}
mark[data-annotation="2"] {
background-color: rgba(121, 224, 69, 0.75);
}
mark[data-annotation="3"] {
background-color: rgba(224, 143, 69, 0.75);
}
<div id="dynamic">
</div>
<div id="static">
<div class="line">Lorem ipsum dolor amet twee shaman salvia, helvetica coloring book direct trade copper mug food truck kitsch DIY. Edison bulb <mark data-annotation="1">snackwave blue bottle meggings hoodie, beard listicle hexagon wolf jean shorts migas 8-bit dreamcatcher. La croix snackwave woke <mark data-annotation="2">marfa, messenger bag af kitsch next level authentic you probably haven't heard</mark></mark>
<mark data-annotation="2"> of them pinterest <mark data-annotation="3">everyday carry. Cronut yuccie offal, cray plaid meditation tote bag master cleanse 90's microdosing hoodie bespoke. </mark></mark><mark data-annotation="3">Artisan actually keytar blue bottle quinoa taxidermy beard. Raw denim polaroid thundercats biodiesel enamel pin messenger bag mixtape pok pok woke ugh drinking vinegar.</mark></div>
<div class="line"><mark data-annotation="3">Seitan waistcoat kickstarter everyday carry PBR&amp;B church-key. Helvetica poke trust fund air plant, pug authentic fanny</mark> pack glossier keffiyeh hell of chillwave. Readymade street art synth, try-hard actually meh squid
blue bottle cray affogato shaman beard sartorial green juice sustainable. Neutra kitsch vegan, knausgaard master cleanse thundercats blog sustainable coloring book aesthetic iPhone tumeric cronut drinking vinegar. Ennui live-edge 3 wolf moon migas
retro small batch.</div>
<div class="line">Retro yr deep v af, kinfolk hoodie knausgaard normcore taxidermy sustainable brunch mustache polaroid four dollar toast raw denim. Listicle af ennui pinterest messenger bag jianbing roof party heirloom vice stumptown. Bushwick scenester fingerstache
narwhal, vice banjo viral drinking vinegar craft beer flannel woke kitsch franzen synth next level. Disrupt snackwave knausgaard VHS artisan taiyaki, umami trust fund kinfolk lomo. Stumptown la croix tattooed kogi. Kitsch street art master cleanse
williamsburg hammock, pabst hell of migas meditation brooklyn hoodie chambray everyday carry.</div>
</div>
/* TODO: Differentiate between Node and Text */
function Tree(data, parent /* Tree */) {
this.data = data;
this.parent = parent;
this.children = [];
}
Tree.prototype.addChild = function addChild(child) {
const last = this.children[this.children.length - 1];
// Collapse string nodes
if (last && 'string' === typeof child && 'string' === typeof last.data) {
last.data += child;
return last;
}
const node = new Tree(child, this);
this.children.push(node);
return node;
};
function DOMBuilder() {
this.root = new Tree();
this.current = this.root;
}
DOMBuilder.prototype.startMark = function startMark(attributes) {
this.current = this.current.addChild({ attributes });
return this;
};
DOMBuilder.prototype.endMark = function endMark() {
this.current = this.current.parent; // pop!
return this;
};
DOMBuilder.prototype.addText = function addText(text) {
this.current.addChild(text);
return this;
};
DOMBuilder.prototype.toNode = function toNode() {
// console.dir(this.current);
return this.current;
};
const markdown = `Lorem ipsum dolor amet twee shaman salvia, helvetica coloring book direct trade copper mug food truck kitsch DIY. Edison bulb snackwave blue bottle meggings hoodie, beard listicle hexagon wolf jean shorts migas 8-bit dreamcatcher. La croix snackwave woke marfa, messenger bag af kitsch next level authentic you probably haven't heard of them pinterest everyday carry. Cronut yuccie offal, cray plaid meditation tote bag master cleanse 90's microdosing hoodie bespoke. Artisan actually keytar blue bottle quinoa taxidermy beard. Raw denim polaroid thundercats biodiesel enamel pin messenger bag mixtape pok pok woke ugh drinking vinegar.
Seitan waistcoat kickstarter everyday carry PBR&B church-key. Helvetica poke trust fund air plant, pug authentic fanny pack glossier keffiyeh hell of chillwave. Readymade street art synth, try-hard actually meh squid blue bottle cray affogato shaman beard sartorial green juice sustainable. Neutra kitsch vegan, knausgaard master cleanse thundercats blog sustainable coloring book aesthetic iPhone tumeric cronut drinking vinegar. Ennui live-edge 3 wolf moon migas retro small batch.
Retro yr deep v af, kinfolk hoodie knausgaard normcore taxidermy sustainable brunch mustache polaroid four dollar toast raw denim. Listicle af ennui pinterest messenger bag jianbing roof party heirloom vice stumptown. Bushwick scenester fingerstache narwhal, vice banjo viral drinking vinegar craft beer flannel woke kitsch franzen synth next level. Disrupt snackwave knausgaard VHS artisan taiyaki, umami trust fund kinfolk lomo. Stumptown la croix tattooed kogi. Kitsch street art master cleanse williamsburg hammock, pabst hell of migas meditation brooklyn hoodie chambray everyday carry.`;
const annotations = [
{
id: '1',
range: {
start: {
line: 1,
column: 127
},
end: {
line: 1,
column: 127 + 129
}
}
},
{
id: '2',
range: {
start: {
line: 1,
column: 256
},
end: {
line: 1,
column: 468
}
}
},
{
id: '3',
range: {
start: {
line: 1,
column: 353
},
end: {
line: 2,
column: 118
}
}
}
];
function documentOrder(a, b) {
if (a.range.start.line > b.range.start.line) return true;
if (a.range.start.line === b.range.start.line) {
return a.range.start.column > b.range.start.column;
}
return false;
}
function inScope(annotations, line) {
//console.dir(annotations);
return annotations
.filter(a => a.range.start.line <= line && line <= a.range.end.line)
.sort(documentOrder);
}
function Line(line, annotations) {
const builder = new DOMBuilder();
for (let column = 0; column < line.length; column++) {
builder.addText(line[column]);
}
return builder.toNode();
}
console.dir(
markdown
.split(/\n/)
.map((line, index) => Line(line, inScope(annotations, index + 1)))
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment