2025-04-12 16:21:02 +00:00
|
|
|
"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) {
|
2025-05-03 03:37:23 +00:00
|
|
|
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;
|
|
|
|
}
|
2025-04-12 16:21:02 +00:00
|
|
|
}
|
2025-05-03 03:37:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
function previousSlideOpen(tokens, before) {
|
|
|
|
for (let i = before - 1; i >= 0; i--) {
|
|
|
|
if (tokens[i].type === "slide_open") {
|
|
|
|
return i;
|
|
|
|
}
|
2025-04-12 16:21:02 +00:00
|
|
|
}
|
2025-05-03 03:37:23 +00:00
|
|
|
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));
|
2025-04-12 16:21:02 +00:00
|
|
|
}
|
2025-05-03 03:37:23 +00:00
|
|
|
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
|
2025-04-12 16:21:02 +00:00
|
|
|
}
|
2025-05-03 03:37:23 +00:00
|
|
|
// 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);
|
|
|
|
}
|
2025-04-12 16:21:02 +00:00
|
|
|
}
|
|
|
|
|
2025-05-03 03:37:23 +00:00
|
|
|
while (openSlides > 0) {
|
|
|
|
state.tokens.push(slideClose(state));
|
2025-04-12 16:21:02 +00:00
|
|
|
}
|
2025-05-03 03:37:23 +00:00
|
|
|
state.tokens.unshift(slidesOpen(state));
|
|
|
|
state.tokens.unshift(presentationOpen(state));
|
|
|
|
state.tokens.push(slidesClose(state));
|
|
|
|
state.tokens.push(presentationClose(state));
|
|
|
|
});
|
|
|
|
}
|