| |
| function findMatchingClosingTag(src, openTag, closeTag) { |
| let depth = 1; |
| let index = openTag.length; |
| while (depth > 0 && index < src.length) { |
| if (src.startsWith(openTag, index)) { |
| depth++; |
| } else if (src.startsWith(closeTag, index)) { |
| depth--; |
| } |
| if (depth > 0) { |
| index++; |
| } |
| } |
| return depth === 0 ? index + closeTag.length : -1; |
| } |
|
|
| function detailsTokenizer(src) { |
| const detailsRegex = /^<details>\n/; |
| const summaryRegex = /^<summary>(.*?)<\/summary>\n/; |
|
|
| if (detailsRegex.test(src)) { |
| const endIndex = findMatchingClosingTag(src, '<details>', '</details>'); |
| if (endIndex === -1) return; |
|
|
| const fullMatch = src.slice(0, endIndex); |
| let content = fullMatch.slice(10, -10).trim(); |
|
|
| let summary = ''; |
| const summaryMatch = summaryRegex.exec(content); |
| if (summaryMatch) { |
| summary = summaryMatch[1].trim(); |
| content = content.slice(summaryMatch[0].length).trim(); |
| } |
|
|
| return { |
| type: 'details', |
| raw: fullMatch, |
| summary: summary, |
| text: content |
| }; |
| } |
| } |
|
|
| function detailsStart(src) { |
| return src.match(/^<details>/) ? 0 : -1; |
| } |
|
|
| function detailsRenderer(token) { |
| return `<details> |
| ${token.summary ? `<summary>${token.summary}</summary>` : ''} |
| ${token.text} |
| </details>`; |
| } |
|
|
| function detailsExtension() { |
| return { |
| name: 'details', |
| level: 'block', |
| start: detailsStart, |
| tokenizer: detailsTokenizer, |
| renderer: detailsRenderer |
| }; |
| } |
|
|
| export default function (options = {}) { |
| return { |
| extensions: [detailsExtension(options)] |
| }; |
| } |
|
|