diff --git a/Core.js b/Core.js index 9ec77cc..7f06c75 100644 --- a/Core.js +++ b/Core.js @@ -363,17 +363,11 @@ class SimonUp { return html.replace('', tocListHtml); } + /** * SimonUp Core Compiler (Core.js) - Version 1.2 * Eine moderne, fluss- und blockorientierte Auszeichnungssprache. */ - -// Globale Hilfsfunktionen außerhalb der Klasse, um Parsing-Verschachtelungen zu verhindern -function _suSlugify(text) { - if (!text) return ''; - return text.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-'); -} - class SimonUp { constructor() { this.plugins = []; @@ -381,18 +375,25 @@ class SimonUp { this.counters = [0, 0, 0, 0, 0, 0]; } + /** + * Registriert eine Erweiterung (Plugin) + */ use(plugin) { this.plugins.push(plugin); return this; } + /** + * Hauptfunktion: Wandelt SimonUp-Text (.up) in valides HTML um + */ parse(text) { this.headings = []; this.counters = [0, 0, 0, 0, 0, 0]; - var html = text; + + let html = text; if (this.plugins && this.plugins.length > 0) { - for (var i = 0; i < this.plugins.length; i++) { + for (let i = 0; i < this.plugins.length; i++) { if (this.plugins[i].before) { html = this.plugins[i].before(html); } @@ -403,9 +404,9 @@ class SimonUp { html = this._injectTOC(html); if (this.plugins && this.plugins.length > 0) { - for (var j = 0; j < this.plugins.length; j++) { - if (this.plugins[j].after) { - html = this.plugins[j].after(html); + for (let i = 0; i < this.plugins.length; i++) { + if (this.plugins[i].after) { + html = this.plugins[i].after(html); } } } @@ -413,21 +414,24 @@ class SimonUp { return html; } + /** + * INTERN: Verarbeitet strukturelle Blöcke und freie Strukturen zeilenweise + */ _parseBlocks(text) { - var lines = text.replace(/\r\n/g, '\n').split('\n'); - var result = []; - var inBlock = false; - var blockType = null; - var blockMeta = ""; - var blockBuffer = []; + const lines = text.replace(/\r\n/g, '\n').split('\n'); + let result = []; + let inBlock = false; + let blockType = null; + let blockMeta = ""; + let blockBuffer = []; - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; if (!inBlock && line.startsWith('&') && line.trim().length > 1) { inBlock = true; - var fullHeader = line.substring(1).trim(); - var spaceIndex = fullHeader.indexOf(' '); + const fullHeader = line.substring(1).trim(); + const spaceIndex = fullHeader.indexOf(' '); if (spaceIndex !== -1) { blockType = fullHeader.substring(0, spaceIndex).toLowerCase(); @@ -440,7 +444,7 @@ class SimonUp { continue; } - if (inBlock && line.trim() === '&') { + if (inBlock && line.trim().replace(/\u00A0/g, ' ') === '&') { result.push(this._renderBlock(blockType, blockMeta, blockBuffer)); inBlock = false; blockType = null; @@ -451,17 +455,17 @@ class SimonUp { if (inBlock) { blockBuffer.push(line); } else { - var trimmed = line.trim(); + let trimmed = line.trim(); if (trimmed === '___') { result.push('
'); } else if (trimmed.startsWith('- ')) { - var insideUl = this._parseInline(line.replace(/^\s*-\s+/, '')); - result.push('' + insideUl + ''); + let insideUl = this._parseInline(line.replace(/^\s*-\s+/, '')); + result.push(`${insideUl}`); } else if (/^\d+\.\s+/.test(trimmed)) { - var cleanLine = line.replace(/^\s*\d+\.\s+/, ''); - var insideOl = this._parseInline(cleanLine); - result.push('' + insideOl + ''); + let cleanLine = line.replace(/^\s*\d+\.\s+/, ''); + let insideOl = this._parseInline(cleanLine); + result.push(`${insideOl}`); } else { result.push(this._parseInline(line)); } @@ -471,69 +475,75 @@ class SimonUp { return this._processParagraphsAndLists(result); } + /** + * INTERN: Erzeugt das HTML für einen geschlossenen Block + */ _renderBlock(type, meta, lines) { - var content = lines.join('\n'); - var self = this; + const content = lines.join('\n'); + let self = this; switch (type) { case 'toc': - var tocWrapper = ''; + let tocWrapper = ''; if (meta || content.trim()) { tocWrapper = '
\n'; if (meta) { - tocWrapper += '

' + meta + '

\n'; + tocWrapper += `

${meta}

\n`; } if (content.trim()) { - var intro = lines.map(function(l) { return self._parseInline(l); }).join('
'); - tocWrapper += '

' + intro + '

\n'; + const intro = lines.map(function(l) { return self._parseInline(l); }).join('
'); + tocWrapper += `

${intro}

\n`; } tocWrapper += '\n
'; } return tocWrapper; case 'c': - var langClass = meta ? ' class="language-' + meta + '"' : ''; - var escapedCode = content.replace(/&/g, '&').replace(//g, '>'); - return '
' + escapedCode + '
'; + const langClass = meta ? ` class="language-${meta}"` : ''; + const escapedCode = content.replace(/&/g, '&').replace(//g, '>'); + return `
${escapedCode}
`; case 'q': - var qContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); - return '
' + qContent + '
'; + const qContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); + return `
${qContent}
`; case 'b': - var bContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); - return '
' + bContent + '
'; + const bContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); + return `
${bContent}
`; case 'f': - var fSummary = meta || 'Details'; - var fContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); - return '
\n' + fSummary + '\n

' + fContent + '

\n
'; + const fSummary = meta || 'Details'; + const fContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); + return `
\n${fSummary}\n

${fContent}

\n
`; case 'i': - var iColor = meta || '#ff6347'; - var iContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); - return '
\n' + iContent + '\n
'; + const iColor = meta || '#ff6347'; + const iContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); + return `
\n${iContent}\n
`; case 't': return this._renderTable(meta, lines); default: - var defContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); - return '
' + defContent + '
'; + const defContent = lines.map(function(l) { return self._parseInline(l); }).join('
'); + return `
${defContent}
`; } } + /** + * INTERN: Hilfsfunktion zum Rendern von Multi-line-Tabellen und Galerien + */ _renderTable(meta, lines) { - var tableHtml = '\n'; - var currentCells = []; - var cellBuffer = []; - var self = this; + let tableHtml = '
\n'; + let currentCells = []; + let cellBuffer = []; + let self = this; - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - var parts = line.split('|'); - for (var j = 0; j < parts.length; j++) { - var part = parts[j]; + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; + let parts = line.split('|'); + for (let j = 0; j < parts.length; j++) { + let part = parts[j]; if (j === parts.length - 1) { if (part.trim() !== '') { @@ -551,11 +561,11 @@ class SimonUp { } if (line.endsWith('|') && currentCells.length > 0) { - var rowContent = currentCells.map(function(c) { - var parsedCell = self._parseInline(c.trim()).replace(/\n/g, '
'); - return ''; + let rowContent = currentCells.map(function(c) { + let parsedCell = self._parseInline(c.trim()).replace(/\n/g, '
'); + return ``; }).join('\n'); - tableHtml += '\n' + rowContent + '\n\n'; + tableHtml += `\n${rowContent}\n\n`; currentCells = []; } } @@ -563,58 +573,61 @@ class SimonUp { return tableHtml; } + /** + * INTERN: Verarbeitet Fluss-Formatierungen (Inline) mittels V1.2-RegEx + */ _parseInline(text) { if (!text) return ''; - var html = text; + let html = text; - var escapes = []; + const escapes = []; html = html.replace(/\\(.)/g, function(match, char) { escapes.push(char); return ''; }); - html = html.replace(/\/\/(\s|$)/g, '$1'); + html = html.replace(/\/\/(( \s|$))/g, '$1'); - var self = this; - var processHeading = function(pattern, callback) { - var match; + let self = this; + const processHeading = function(pattern, callback) { + let match; while ((match = pattern.exec(html)) !== null) { - var fullMatch = match[0]; - var level = parseInt(match[1]); - var titleAndRest = match[2]; + const fullMatch = match[0]; + const level = parseInt(match[1]); + const titleAndRest = match[2]; - var title = titleAndRest; - var rest = ""; - var closeIdx = titleAndRest.indexOf('/#'); + let title = titleAndRest; + let rest = ""; + const closeIdx = titleAndRest.indexOf('/#'); if (closeIdx !== -1) { title = titleAndRest.substring(0, closeIdx); rest = titleAndRest.substring(closeIdx + 2); } - var result = callback(level, title.trim(), rest); + const result = callback(level, title.trim(), rest); html = html.substring(0, match.index) + result + html.substring(match.index + fullMatch.length); } }; processHeading(/^([1-6])x#\s*(.*)/g, function(level, title, rest) { - var numStr = self._getAutoNumber(level); - var fullTitle = numStr + ' ' + title; - var id = _suSlugify(fullTitle); + const numStr = self._getAutoNumber(level); + const fullTitle = `${numStr} ${title}`; + const id = self._slugify(fullTitle); self.headings.push({ level: level, text: fullTitle, id: id }); - return '' + fullTitle + '' + rest; + return `${fullTitle}${rest}`; }); processHeading(/^([1-6])-#\s*(.*)/g, function(level, title, rest) { - return '' + title + '' + rest; + return `${title}${rest}`; }); processHeading(/^([1-6])#\s*(.*)/g, function(level, title, rest) { - var id = _suSlugify(title); + const id = self._slugify(title); self.headings.push({ level: level, text: title, id: id }); - return '' + title + '' + rest; + return `${title}${rest}`; }); - var inlineRules = [ + const inlineRules = [ { tag: 'strong', symbol: '\\*\\*' }, { tag: 'span style="font-weight: 500;"', symbol: '\\*' }, { tag: 'em', symbol: '%' }, @@ -626,31 +639,31 @@ class SimonUp { { tag: 'code', symbol: '`' } ]; - for (var k = 0; k < inlineRules.length; k++) { - var rule = inlineRules[k]; - var tagSimple = rule.tag.split(' ')[0]; + for (let k = 0; k < inlineRules.length; k++) { + let rule = inlineRules[k]; + let tagSimple = rule.tag.split(' ')[0]; - var regex = new RegExp(rule.symbol + '(.+?)\\x2F' + rule.symbol, 'g'); - html = html.replace(regex, '<' + rule.tag + '>$1'); + let regex = new RegExp(rule.symbol + '(.+?)\\\\' + rule.symbol, 'g'); + html = html.replace(regex, `<${rule.tag}>$1`); - var resetRegex = new RegExp(rule.symbol + '(.+?)(?=)', 'g'); - html = html.replace(resetRegex, '<' + rule.tag + '>$1'); + let resetRegex = new RegExp(rule.symbol + '(.+?)(?=)', 'g'); + html = html.replace(resetRegex, `<${rule.tag}>$1`); } html = html.replace(//g, ''); - var alignMap = { 'l': 'left', 'r': 'right', 'c': 'center', 'j': 'justify' }; + const alignMap = { 'l': 'left', 'r': 'right', 'c': 'center', 'j': 'justify' }; html = html.replace(/>([lrcj])\s+(.+?)\s*\/>/g, function(m, dir, content) { - return '
' + content + '
'; + return `
${content}
`; }); html = html.replace(/!\[([^\]]+)\s+w:(\d+)\]\(([^)]+)\)/g, '$1'); html = html.replace(/!\[([^\]]+)\]\(([^)]+)\)/g, '$1'); html = html.replace(/\[([^\]]+)\]\{([^}]+)\}/g, '$1'); - html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, ''); + html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); - html = html.replace(/\^x\[([^\]]+)\]\^/g, '[*]'); - html = html.replace(/\^(\d+)\[([^\]]+)\]\^/g, '[$1]'); + html = html.replace(/\^x\[([^\]]+)\]\^x/g, '[*]'); + html = html.replace(/\^(\d+)\[([^\]]+)\]\^x/g, '[$1]'); html = html.replace(//g, function(match, idx) { return escapes[idx]; @@ -659,16 +672,31 @@ class SimonUp { return html; } + /** + * INTERN: Errechnet die nächste fortlaufende Kapitelnummer + */ _getAutoNumber(level) { - var idx = level - 1; + let idx = level - 1; this.counters[idx]++; - for (var i = idx + 1; i < 6; i++) { + for (let i = idx + 1; i < 6; i++) { this.counters[i] = 0; } - var activeParts = this.counters.slice(0, level); + let activeParts = this.counters.slice(0, level); return activeParts.join('.') + '.'; } + /** + * INTERN: Macht aus Text eine URL-konforme ID + */ + _slugify(text) { + return text.toLowerCase() + .replace(/[^a-z0-9\s-]/g, '') + .replace(/\s+/g, '-'); + } + + /** + * INTERN: Erzeugt die fertige HTML-Liste des TOC und injiziert sie in den Block + */ _injectTOC(html) { if (!html.includes('')) { return html; @@ -677,24 +705,24 @@ class SimonUp { return html.replace('', ''); } - var tocListHtml = '
\n\n
'; @@ -702,45 +730,48 @@ class SimonUp { return html.replace('', tocListHtml); } + /** + * INTERN: Fasst freie Listen-Elemente zusammen und generiert Absätze (p) + */ _processParagraphsAndLists(elements) { - var processed = []; - var paragraphBuffer = []; - var listBuffer = []; - var currentListType = null; + let processed = []; + let paragraphBuffer = []; + let listBuffer = []; + let currentListType = null; - var flushList = function() { + const flushList = function() { if (listBuffer.length > 0) { - var items = listBuffer.map(function(item) { return '
  • ' + item + '
  • '; }).join('\n'); - processed.push('<' + currentListType + '>\n' + items + '\n'); + let items = listBuffer.map(function(item) { return `
  • ${item}
  • `; }).join('\n'); + processed.push(`<${currentListType}>\n${items}\n`); listBuffer = []; currentListType = null; } }; - var flushParagraph = function() { + const flushParagraph = function() { if (paragraphBuffer.length > 0) { - var pContent = paragraphBuffer.join('
    '); - processed.push('

    ' + pContent + '

    '); + let pContent = paragraphBuffer.join('
    '); + processed.push(`

    ${pContent}

    `); paragraphBuffer = []; } }; - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - var isUlItem = element.startsWith(''); - var isOlItem = element.startsWith(''); + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + let isUlItem = element.startsWith(''); + let isOlItem = element.startsWith(''); if (isUlItem || isOlItem) { flushParagraph(); - var type = isUlItem ? 'ul' : 'ol'; - var content = isUlItem ? element.replace(/<\/?ul-item>/g, '') : element.replace(/<\/?ol-item>/g, ''); + let type = isUlItem ? 'ul' : 'ol'; + let content = isUlItem ? element.replace(/<\/?ul-item>/g, '') : element.replace(/<\/?ol-item>/g, ''); if (currentListType && currentListType !== type) { flushList(); } currentListType = type; listBuffer.push(content); - } else if (element.startsWith('<') && !/^(|||||||||||)/i.test(element)) { + } else if (element.startsWith('<') && !/^(|||||||||||
    ' + parsedCell + '${parsedCell}