"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processMusicalNotation = processMusicalNotation; /** * Processes musical notation in HTML content * Wraps musical notation in appropriate HTML for rendering */ function processMusicalNotation(html) { // First, clean up any corrupted abc-notation divs with very long data-abc attributes // These were created by a buggy regex that matched the entire HTML document html = html.replace(/
...
html = html.replace(/]*>]*class="[^"]*language-abc[^"]*"[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (match, codeContent) => {
// Check if codeContent contains an abc-notation div with a very long data-abc attribute (>500 chars = corrupted)
const longDataAbcMatch = codeContent.match(/]*class="[^"]*abc-notation[^"]*"[^>]*data-abc="([^"]{500,})"/i);
if (longDataAbcMatch) {
// Extract just the ABC notation from the beginning of the corrupted data-abc value
let decoded = longDataAbcMatch[1]
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, "'");
// The ABC notation ends where the HTML document starts ( or )
// Extract everything from X: up to (but not including) </code> or </pre>
const abcMatch = decoded.match(/^(X:\s*\d+[\s\S]*?)(?=<\/code>|<\/pre>)/);
if (abcMatch) {
let cleanAbc = abcMatch[1].trim();
// Remove any trailing HTML entities
cleanAbc = cleanAbc.replace(/<.*$/, '').trim();
// Validate it's reasonable ABC notation
if (cleanAbc.length > 10 && cleanAbc.length < 2000 && cleanAbc.match(/^X:\s*\d+/m)) {
// Return clean code block - the processing step will wrap it in abc-notation div
return `${cleanAbc}
`;
}
}
// If extraction fails, just remove the corrupted div and return empty code block
// This prevents the corrupted data from being rendered
return `
`;
}
return match;
});
// Process ABC notation blocks - ONLY code blocks explicitly marked with language-abc class
// These come from: [source,abc], [source, abc], [abc] in AsciiDoc, or ```abc in Markdown
// We do NOT auto-detect ABC notation - it must be explicitly marked
html = html.replace(/]*>]*class="[^"]*language-abc[^"]*"[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (match, codeContent) => {
// Skip if already processed or corrupted
if (codeContent.includes('abc-notation') ||
codeContent.includes('class="abc-notation"') ||
codeContent.includes('') ||
codeContent.length > 5000) {
return match;
}
// Extract ABC content from the code block
let abcContent = codeContent
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, "'")
.replace(/'/g, "'")
.replace(///g, '/');
// Remove any HTML tags
abcContent = abcContent.replace(/<[^>]+>/g, '').trim();
// Only process if it looks like valid ABC notation (starts with X:)
// Since this is explicitly marked as ABC, we trust it's ABC notation
if (abcContent.match(/^X:\s*\d+/m) &&
abcContent.length < 3000 &&
!abcContent.includes('') &&
!abcContent.includes(' 200) {
break;
}
abcLines.push(line);
if (abcLines.join('\n').length > 2000) {
break;
}
}
const cleanAbc = abcLines.join('\n').trim();
if (cleanAbc.match(/^X:\s*\d+/m) && cleanAbc.length > 10 && cleanAbc.length < 2000) {
return `${match}`;
}
}
return match;
});
// Process LilyPond notation blocks
const lilypondPattern = /(\\relative[^}]+})/gs;
html = html.replace(lilypondPattern, (match) => {
const lilypondContent = match.trim();
return `${lilypondContent}`;
});
// Process inline chord notation: [C], [Am], [F#m7], etc.
const chordPattern = /\[([A-G][#b]?m?[0-9]?[^\[\]]*)\]/g;
html = html.replace(chordPattern, (match, chord) => {
return `[${chord}]`;
});
// Process MusicXML-like notation
const musicxmlPattern = /(]*>.*?<\/music>)/gs;
html = html.replace(musicxmlPattern, (match) => {
const musicxmlContent = match.trim();
return `${musicxmlContent}`;
});
return html;
}
/**
* Escapes a string for use in HTML attributes
*/
function escapeForAttr(text) {
return text
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(//g, '>')
.replace(/\n/g, ' ')
.replace(/\r/g, '');
}