mathjax/plantuml rendering only outside of code/scripts

This commit is contained in:
Klemek
2019-07-19 10:01:01 +02:00
parent 2e8ff1be92
commit 53e1fe7201
2 changed files with 91 additions and 13 deletions
+67 -13
View File
@@ -5,6 +5,55 @@ const showdown = require('showdown');
module.exports = (config) => { module.exports = (config) => {
const converter = new showdown.Converter(config['showdown']); const converter = new showdown.Converter(config['showdown']);
/**
* get parts outside of codes/scripts
* @param {string} data
* @returns {{index:number, end:number, text:string}[]} parts
*/
const getParts = (data) => {
let parts = [];
let match;
let i = 0;
while ((match = /```/m.exec(data.slice(i)))) {
parts.push({
index: i,
end: i + match.index,
text: data.slice(i, i + match.index),
});
i += match.index + match[0].length;
}
if (i < data.length)
parts.push({
index: i,
end: data.length,
text: data.slice(i, data.length),
});
parts = parts.filter((p, i) => i % 2 === 0); //filter out code parts
// detect scripts outside of code
parts.forEach((p, pi) => {
let i = 0;
const subParts = [];
while ((match = /(<script>((?:(?!<\/script>)[\s\S])*)<\/script>)/gm.exec(p.text.slice(i)))) {
subParts.push({
index: p.index + i,
end: i + match.index,
text: p.text.slice(i, i + match.index),
});
i += match.index + match[0].length;
}
if (i < p.text.length)
subParts.push({
index: p.index + i,
end: p.text.length,
text: p.text.slice(i, p.text.length),
});
parts.splice(pi, 1, ...subParts);
});
return parts;
};
const renderShowDown = (data, cb) => { const renderShowDown = (data, cb) => {
const html = converter.makeHtml(data); const html = converter.makeHtml(data);
cb(html); cb(html);
@@ -35,15 +84,19 @@ module.exports = (config) => {
const renderPlantUML = (data, cb) => { const renderPlantUML = (data, cb) => {
if (!config['modules']['plantuml']) if (!config['modules']['plantuml'])
return cb(data); return cb(data);
const parts = getParts(data);
const umlRegex = /@startuml\r?\n((?:(?!@enduml)[\s\S])*)\r?\n@enduml/m; const umlRegex = /@startuml\r?\n((?:(?!@enduml)[\s\S])*)\r?\n@enduml/m;
let match; let match;
while ((match = umlRegex.exec(data))) { parts.forEach(part => {
const code = match[1].trim(); while ((match = umlRegex.exec(part.text))) {
const s = unescape(encodeURIComponent(code)); // jshint ignore:line const code = match[1].trim();
const compressed = global['zip_deflate'](s); const s = unescape(encodeURIComponent(code)); // jshint ignore:line
const url = `http://www.plantuml.com/plantuml/${config['plantuml']['output_format']}/${encode64(compressed)}`;// jshint ignore:line const compressed = global['zip_deflate'](s);
data = data.slice(0, match.index) + `<img alt="generated PlantUML diagram" src="${url}">` + data.slice(match.index + match[0].length); const url = `http://www.plantuml.com/plantuml/${config['plantuml']['output_format']}/${encode64(compressed)}`;// jshint ignore:line
} part.text = part.text.slice(0, match.index) + `<img alt="generated PlantUML diagram" src="${url}">` + part.text.slice(match.index + match[0].length);
}
data = data.slice(0, part.index) + part.text + data.slice(part.end);
});
cb(data); cb(data);
}; };
@@ -64,7 +117,7 @@ module.exports = (config) => {
if (!config['modules']['mathjax']) if (!config['modules']['mathjax'])
return cb(data); return cb(data);
const spl = data.split('```'); const parts = getParts(data);
const doMJ = (match, format, i) => { const doMJ = (match, format, i) => {
const eq = match[1].trim(); const eq = match[1].trim();
@@ -76,8 +129,8 @@ module.exports = (config) => {
}; };
mjConf[output] = true; mjConf[output] = true;
mjAPI.typeset(mjConf, (res) => { mjAPI.typeset(mjConf, (res) => {
spl[i] = spl[i].slice(0, match.index) + res[output] + spl[i].slice(match.index + match[0].length); data = data.slice(0, parts[i].index + match.index) + res[output] + data.slice(parts[i].index + match.index + match[0].length);
renderMathJax(spl.join('```'), (data2) => { renderMathJax(data, (data2) => {
cb(data2); cb(data2);
}); });
}); });
@@ -86,11 +139,11 @@ module.exports = (config) => {
const eqRegex = /\$\$((?:(?!\$\$)[\s\S])*)\$\$/m; const eqRegex = /\$\$((?:(?!\$\$)[\s\S])*)\$\$/m;
const inlineEqRegex = /\$([^$\n]*)\$/; const inlineEqRegex = /\$([^$\n]*)\$/;
for (let i = 0; i < spl.length; i += 2) { for (let i = 0; i < parts.length; i += 2) {
let match; let match;
if ((match = eqRegex.exec(spl[i]))) { if ((match = eqRegex.exec(parts[i].text))) {
return doMJ(match, 'TeX', i); return doMJ(match, 'TeX', i);
} else if ((match = inlineEqRegex.exec(spl[i]))) { } else if ((match = inlineEqRegex.exec(parts[i].text))) {
return doMJ(match, 'inline-TeX', i); return doMJ(match, 'inline-TeX', i);
} }
} }
@@ -98,6 +151,7 @@ module.exports = (config) => {
}; };
return { return {
getParts: config['test'] ? getParts : undefined,
renderShowDown: config['test'] ? renderShowDown : undefined, renderShowDown: config['test'] ? renderShowDown : undefined,
renderPrism: config['test'] ? renderPrism : undefined, renderPrism: config['test'] ? renderPrism : undefined,
renderPlantUML: config['test'] ? renderPlantUML : undefined, renderPlantUML: config['test'] ? renderPlantUML : undefined,
+24
View File
@@ -42,6 +42,23 @@ afterAll(() => {
} }
}); });
describe('get parts', () => {
test('normal', () => {
const data = 'Hello\nthere\ngeneral\nkenobi';
const parts = renderer.getParts(data);
expect(parts.map(p => p.text)).toEqual([
'Hello\nthere\ngeneral\nkenobi'
]);
});
test('lot of stuff', () => {
const data = 'Hello\nthere\n```code```\ngeneral<script>script</script>\n<script>script2</script>\n```<script>script3</script>```kenobi';
const parts = renderer.getParts(data);
expect(parts.map(p => p.text)).toEqual([
'Hello\nthere\n', '\ngeneral', '\n', '\n', 'kenobi'
]);
});
});
describe('Test Showdown', () => { describe('Test Showdown', () => {
test('normal', (done) => { test('normal', (done) => {
renderer.renderShowDown('# Hello', (html) => { renderer.renderShowDown('# Hello', (html) => {
@@ -112,6 +129,13 @@ describe('Test PlantUML', () => {
}); });
}); });
test('plantuml ignored in code', (done) => {
renderer.renderPlantUML('code:\n```@startuml\nBob -> Alice : hello\n@enduml```', (data) => {
expect(data).toBe('code:\n```@startuml\nBob -> Alice : hello\n@enduml```');
done();
});
});
test('plantuml multiple uml', (done) => { test('plantuml multiple uml', (done) => {
renderer.renderPlantUML('@startuml\nBob -> Alice : hello\n@enduml\n@startuml\nBob -> Alice : hello\n@enduml', (data) => { renderer.renderPlantUML('@startuml\nBob -> Alice : hello\n@enduml\n@startuml\nBob -> Alice : hello\n@enduml', (data) => {
expect(data).toBe('<img alt="generated PlantUML diagram" src="http://www.plantuml.com/plantuml/svg/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000">\n<img alt="generated PlantUML diagram" src="http://www.plantuml.com/plantuml/svg/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000">'); expect(data).toBe('<img alt="generated PlantUML diagram" src="http://www.plantuml.com/plantuml/svg/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000">\n<img alt="generated PlantUML diagram" src="http://www.plantuml.com/plantuml/svg/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000">');