108 lines
3.8 KiB
JavaScript
108 lines
3.8 KiB
JavaScript
export default function figcaptionPlugin(md) {
|
|
// Rule to identify images followed by italicized text for figcaption
|
|
function figcaptionRule(state) {
|
|
const tokens = state.tokens;
|
|
let figcaptionStartIndex = -1;
|
|
|
|
for (let i = 0; i < tokens.length; i++) {
|
|
// Check for paragraph containing only an image
|
|
if (
|
|
tokens[i].type === "paragraph_open" &&
|
|
i + 2 < tokens.length &&
|
|
tokens[i + 1].type === "inline" &&
|
|
tokens[i + 1].children &&
|
|
tokens[i + 1].children.length === 1 &&
|
|
tokens[i + 1].children[0].type === "image" &&
|
|
tokens[i + 2].type === "paragraph_close"
|
|
) {
|
|
// Check if the next token is a paragraph starting with emphasis
|
|
if (
|
|
i + 5 < tokens.length &&
|
|
tokens[i + 3].type === "paragraph_open" &&
|
|
tokens[i + 4].type === "inline" &&
|
|
tokens[i + 4].children &&
|
|
tokens[i + 4].children.length > 0 &&
|
|
tokens[i + 4].children[0].type === "em_open" &&
|
|
tokens[i + 5].type === "paragraph_close"
|
|
) {
|
|
figcaptionStartIndex = i + 3; // Start index of the caption paragraph
|
|
|
|
// --- Replace tokens ---
|
|
|
|
// 1. Change paragraph_open to figure_open
|
|
const figureOpen = new state.Token("figure_open", "figure", 1);
|
|
tokens[i] = figureOpen; // Replace paragraph_open
|
|
|
|
// 2. Keep the inline token with the image as is (tokens[i+1])
|
|
|
|
// 3. Change paragraph_close to figcaption_open
|
|
const figcaptionOpen = new state.Token(
|
|
"figcaption_open",
|
|
"figcaption",
|
|
1,
|
|
);
|
|
tokens[i + 2] = figcaptionOpen; // Replace paragraph_close
|
|
|
|
// 4. Remove the caption's paragraph_open
|
|
tokens.splice(figcaptionStartIndex, 1); // Remove paragraph_open at i+3
|
|
|
|
// 5. Modify the caption's inline content: remove outer <em> tags
|
|
const captionInlineToken = tokens[figcaptionStartIndex]; // Now at index i+3 after splice
|
|
if (
|
|
captionInlineToken.children[0].type === "em_open" &&
|
|
captionInlineToken.children[captionInlineToken.children.length - 1]
|
|
.type === "em_close"
|
|
) {
|
|
captionInlineToken.children.shift(); // Remove em_open
|
|
captionInlineToken.children.pop(); // Remove em_close
|
|
}
|
|
// Adjust level for caption content
|
|
captionInlineToken.level += 1;
|
|
captionInlineToken.children.forEach((child) => {
|
|
child.level += 1;
|
|
});
|
|
|
|
// 6. Change the caption's paragraph_close to figcaption_close
|
|
const figcaptionClose = new state.Token(
|
|
"figcaption_close",
|
|
"figcaption",
|
|
-1,
|
|
);
|
|
tokens[figcaptionStartIndex + 1] = figcaptionClose; // Replace paragraph_close (now at i+4)
|
|
|
|
// 7. Add figure_close after figcaption_close
|
|
const figureClose = new state.Token("figure_close", "figure", -1);
|
|
tokens.splice(figcaptionStartIndex + 2, 0, figureClose); // Insert figure_close (at i+5)
|
|
|
|
// Adjust token index to skip the newly inserted/modified tokens
|
|
i += 6; // Move past the figure structure
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
md.core.ruler.after("inline", "figcaption", figcaptionRule);
|
|
|
|
// Add renderer rules if they don't exist
|
|
md.renderer.rules.figure_open =
|
|
md.renderer.rules.figure_open ||
|
|
function () {
|
|
return "<figure>\n";
|
|
};
|
|
md.renderer.rules.figure_close =
|
|
md.renderer.rules.figure_close ||
|
|
function () {
|
|
return "</figure>\n";
|
|
};
|
|
md.renderer.rules.figcaption_open =
|
|
md.renderer.rules.figcaption_open ||
|
|
function () {
|
|
return "<figcaption>";
|
|
};
|
|
md.renderer.rules.figcaption_close =
|
|
md.renderer.rules.figcaption_close ||
|
|
function () {
|
|
return "</figcaption>\n";
|
|
};
|
|
}
|