156 lines
4.9 KiB
JavaScript
156 lines
4.9 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
const HSEP = "---";
|
||
|
const VSEP = "===";
|
||
|
|
||
|
|
||
|
/**
|
||
|
* MarkdownIt plugin to generate revealjs friendly markup.
|
||
|
*
|
||
|
* Use "---" to transition to a new horizontal slide.
|
||
|
* Use "===" to transition to a vertical child slide.
|
||
|
*
|
||
|
* @param {*} md
|
||
|
* @param {*} options
|
||
|
*/
|
||
|
export default function revealjs_plugin(md, options) {
|
||
|
function isVSep(token) {
|
||
|
return token.type === "inline" && token.content === VSEP;
|
||
|
}
|
||
|
|
||
|
function isHSep(token) {
|
||
|
return token.type === "hr" && token.markup === HSEP;
|
||
|
}
|
||
|
|
||
|
function isSep(token) {
|
||
|
return isHSep(token) || isVSep(token);
|
||
|
}
|
||
|
|
||
|
function renderOpening(tokens, idx, options, env, slf) {
|
||
|
var token = tokens[idx];
|
||
|
return `<${tokens[idx].tag}${slf.renderAttrs(tokens[idx])}>`;
|
||
|
};
|
||
|
|
||
|
function renderClosing(tokens, idx, options, env, slf) {
|
||
|
var token = tokens[idx];
|
||
|
return `</${tokens[idx].tag}>`;
|
||
|
};
|
||
|
|
||
|
md.renderer.rules.pres_open = renderOpening;
|
||
|
md.renderer.rules.pres_close = renderClosing;
|
||
|
md.renderer.rules.slide_open = renderOpening;
|
||
|
md.renderer.rules.slide_close = renderClosing;
|
||
|
|
||
|
function nextDivider(tokens, start) {
|
||
|
for (let i = start; i < tokens.length; i++) {
|
||
|
if (isSep(tokens[i])) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
function previousSlideOpen(tokens, before) {
|
||
|
for (let i = before - 1; i >= 0; i--) {
|
||
|
if (tokens[i].type === "slide_open") {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
var openSlides = 0;
|
||
|
|
||
|
function presentationOpen(state) {
|
||
|
var token = new state.Token("pres_open", "section", 1);
|
||
|
token.block = true;
|
||
|
token.attrs = [
|
||
|
["class", "reveal"]
|
||
|
];
|
||
|
return token;
|
||
|
}
|
||
|
|
||
|
function presentationClose(state) {
|
||
|
return new state.Token("pres_close", "section", -1);
|
||
|
}
|
||
|
|
||
|
function slidesOpen(state) {
|
||
|
var token = new state.Token("slides_open", "div", 1);
|
||
|
token.block = true;
|
||
|
token.attrs = [
|
||
|
["class", "slides"]
|
||
|
];
|
||
|
return token;
|
||
|
}
|
||
|
|
||
|
function slidesClose(state) {
|
||
|
return new state.Token("slides_close", "div", -1);
|
||
|
}
|
||
|
|
||
|
function slideOpen(state) {
|
||
|
openSlides++;
|
||
|
return new state.Token("slide_open", "section", 1);
|
||
|
}
|
||
|
|
||
|
function slideClose(state) {
|
||
|
openSlides--;
|
||
|
return new state.Token("slide_close", "section", -1);
|
||
|
}
|
||
|
|
||
|
md.core.ruler.push("revealjs", function(state) {
|
||
|
let divIdx = 0;
|
||
|
while (true) {
|
||
|
divIdx = nextDivider(state.tokens, divIdx);
|
||
|
if (divIdx === -1) {
|
||
|
break;
|
||
|
}
|
||
|
let divider = state.tokens[divIdx];
|
||
|
if (isSep(divider) && openSlides === 0) {
|
||
|
state.tokens.unshift(slideOpen(state));
|
||
|
divIdx++; // we added a token at the beginning, we need to update divIdx
|
||
|
}
|
||
|
let tags = [];
|
||
|
if (isHSep(divider)) {
|
||
|
while (openSlides > 0) {
|
||
|
tags.push(slideClose(state));
|
||
|
}
|
||
|
tags.push(slideOpen(state));
|
||
|
// because "---" is hijacked from plain markdown, it translates
|
||
|
// to one token which we remove
|
||
|
state.tokens.splice(divIdx, 1, ...tags);
|
||
|
} else if (isVSep(divider)) {
|
||
|
// if it is a vertical separator, we need to wrap the current slide
|
||
|
// in it's own section
|
||
|
if (openSlides === 1) {
|
||
|
let slideOpenIdx = previousSlideOpen(state.tokens, divIdx);
|
||
|
state.tokens.splice(slideOpenIdx, 1,
|
||
|
state.tokens[slideOpenIdx], // reuse it to avoid increasing openSlides
|
||
|
slideOpen(state)
|
||
|
);
|
||
|
divIdx++; // we added a token before the divider, we need to update divIdx
|
||
|
}
|
||
|
// if the current slide is already a vertical child, we need to close
|
||
|
// it; this will be indicated by a nesting level of two:
|
||
|
// <section horizontal-parent>
|
||
|
// <section current-vertical-child>
|
||
|
// <!-- this is where we are right now -->
|
||
|
if (openSlides === 2) {
|
||
|
tags.push(slideClose(state));
|
||
|
}
|
||
|
tags.push(slideOpen(state));
|
||
|
// because this is a custom token, it is first wrapped by the processor
|
||
|
// in a paragraph, so we need to replace para_open, sep, para_close
|
||
|
state.tokens.splice(divIdx - 1, 3, ...tags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (openSlides > 0) {
|
||
|
state.tokens.push(slideClose(state));
|
||
|
}
|
||
|
state.tokens.unshift(slidesOpen(state));
|
||
|
state.tokens.unshift(presentationOpen(state));
|
||
|
state.tokens.push(slidesClose(state));
|
||
|
state.tokens.push(presentationClose(state));
|
||
|
});
|
||
|
};
|