Skip to content

Commit 09e68b1

Browse files
committed
Header anchor generation fix
1 parent 6388186 commit 09e68b1

File tree

1 file changed

+67
-5
lines changed

1 file changed

+67
-5
lines changed

app/lib/processMarkdown.jsx

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import markedToc from 'marked-toc';
2-
import marked from 'marked';
2+
import {default as marked, Renderer} from 'marked';
33
import highlightJs from 'highlight.js';
44

55
marked.setOptions({
@@ -8,12 +8,74 @@ marked.setOptions({
88
}
99
});
1010

11-
function processMarkdown(markdown, callback) {
12-
var toc = '\n<!--start_toc-->\n\n' + markedToc(markdown, {maxDepth: 1}) + '\n\n<!--end_toc-->\n';
13-
var html = marked(markdown.replace('<!-- toc -->', toc), callback);
11+
export default function processMarkdown(markdown, callback) {
12+
var toc = '\n<!--start_toc-->\n\n' +
13+
markedToc(markdown, {maxDepth: 1, slugify: generateHeaderAnchor}) +
14+
'\n\n<!--end_toc-->\n';
15+
var html = marked(markdown.replace('<!-- toc -->', toc), {renderer: new RendererReplacement()}, callback);
1416
html = html.replace('<!--start_toc-->', '<div class="toc"><div class="toc-header">Table of contents:</div>');
1517
html = html.replace('<!--end_toc-->', '</div>');
1618
return html;
1719
}
1820

19-
module.exports = processMarkdown;
21+
class RendererReplacement extends Renderer {
22+
heading(text, level, raw) {
23+
let idPrefix = this.options.headerPrefix;
24+
let id = generateHeaderAnchor(raw);
25+
return `<h${level} id="${idPrefix + id}">${text}</h${level}>\n`;
26+
}
27+
}
28+
29+
/**
30+
* @see https://github.com/vmg/redcarpet/blob/master/ext/redcarpet/html.c
31+
* @param {String} text
32+
* @returns {String}
33+
*/
34+
function generateHeaderAnchor(text) {
35+
var heading = '';
36+
/* Dasherize the string removing extra white spaces
37+
and stripped chars */
38+
for (let i = 0; i < text.length; ++i) {
39+
var addDash = false;
40+
while ((i + 1) < text.length && isIllegalChar(text[i]) && isIllegalChar(text[i + 1])) {
41+
if (shouldReplaceWithDash(text[i]) || shouldReplaceWithDash(text[i + 1])) {
42+
addDash = true;
43+
}
44+
i++;
45+
}
46+
47+
if (addDash || shouldReplaceWithDash(text[i])) {
48+
if (i + 1 < text.length) {
49+
heading += '-';
50+
}
51+
} else {
52+
if (!shouldRemoveChar(text[i])) {
53+
heading += text[i].toLowerCase();
54+
}
55+
}
56+
}
57+
if (text.indexOf('Why') !== -1) console.log(text, '->', heading);
58+
return heading;
59+
}
60+
61+
const toDashChars = toCharIndex(' -&+$,:;=?@"#{}|^~[]`\\*()%.!\'');
62+
const removeChars = toCharIndex('/');
63+
64+
function isIllegalChar(char) {
65+
return shouldReplaceWithDash(char) || shouldRemoveChar(char);
66+
}
67+
68+
function shouldReplaceWithDash(char) {
69+
return Boolean(toDashChars[char]);
70+
}
71+
72+
function shouldRemoveChar(char) {
73+
return Boolean(removeChars[char]);
74+
}
75+
76+
function toCharIndex(input) {
77+
return input.split('').reduce((obj, char) => {
78+
obj[char] = true;
79+
return obj;
80+
}, {});
81+
}

0 commit comments

Comments
 (0)