diff --git a/docs/event_system.md b/docs/event_system.md index 7b5fea3..d5002aa 100644 --- a/docs/event_system.md +++ b/docs/event_system.md @@ -27,7 +27,7 @@ Note: This object holds the text captured by the subparser. The properties of this object are different for each subparser. -Properties whose name starts with `_` are readonly. +**Properties whose name starts with `_` are readonly**. Example: @@ -214,3 +214,15 @@ These events are always guaranteed to be called, regardless of options or circum ### blockquote #### + + +### metadata + + + +## Common pitfalls + +### Infinite loop + +If you pass the input as output in a `onCapture` event, you might trigger an infinite loop. + diff --git a/src/subParsers/makehtml/metadata.js b/src/subParsers/makehtml/metadata.js index c665287..8b15176 100644 --- a/src/subParsers/makehtml/metadata.js +++ b/src/subParsers/makehtml/metadata.js @@ -8,11 +8,50 @@ showdown.subParser('makehtml.metadata', function (text, options, globals) { return text; } - text = globals.converter._dispatch('makehtml.metadata.before', text, options, globals).getText(); + let startEvent = new showdown.Event('makehtml.metadata.onStart', text); + startEvent + .setOutput(text) + ._setGlobals(globals) + ._setOptions(options); + startEvent = globals.converter.dispatch(startEvent); + text = startEvent.output; + + /** + * @param {RegExp} pattern + * @param {string} wholeMatch + * @param {string} format + * @param {string} content + * @returns {string} + */ + function parseMetadataContents (pattern, wholeMatch, format, content) { + let otp; + let captureStartEvent = new showdown.Event('makehtml.metadata.onCapture', content); + captureStartEvent + .setOutput(null) + ._setGlobals(globals) + ._setOptions(options) + .setRegexp(pattern) + .setMatches({ + _wholeMatch: wholeMatch, + format: format, + content: content + }) + .setAttributes({}); + captureStartEvent = globals.converter.dispatch(captureStartEvent); + + format = captureStartEvent.matches.format; + content = captureStartEvent.matches.content; + + // if something was passed as output, it will be used as output + if (captureStartEvent.output && captureStartEvent.output !== '') { + otp = captureStartEvent.output; + } else { + otp = '¨M'; + } - function parseMetadataContents (content) { // raw is raw so it's not changed in any way globals.metadata.raw = content; + globals.metadata.format = format; // escape chars forbidden in html attributes // double quotes @@ -20,35 +59,47 @@ showdown.subParser('makehtml.metadata', function (text, options, globals) { // ampersand first .replace(/&/g, '&') // double quotes - .replace(/"/g, '"'); - + .replace(/"/g, '"') // Restore dollar signs and tremas - content = content .replace(/¨D/g, '$$') - .replace(/¨T/g, '¨'); + .replace(/¨T/g, '¨') + // replace multiple spaces + .replace(/\n {4}/g, ' '); - content = content.replace(/\n {4}/g, ' '); content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) { globals.metadata.parsed[key] = value; return ''; }); + let beforeHashEvent = new showdown.Event('makehtml.metadata.onHash', otp); + beforeHashEvent + .setOutput(otp) + ._setGlobals(globals) + ._setOptions(options); + beforeHashEvent = globals.converter.dispatch(beforeHashEvent); + otp = beforeHashEvent.output; + if (!otp) { + otp = '¨M'; + } + return otp; } - text = text.replace(/^\s*«««+\s*(\S*?)\n([\s\S]+?)\n»»»+\s*\n/, function (wholematch, format, content) { - parseMetadataContents(content); - return '¨M'; + // 1. Metadata with «««»»» delimiters + const rgx1 = /^\s*«««+\s*(\S*?)\n([\s\S]+?)\n»»»+\s*\n/; + text = text.replace(rgx1, function (wholeMatch, format, content) { + return parseMetadataContents(rgx1, wholeMatch, format, content); }); - text = text.replace(/^\s*---+\s*(\S*?)\n([\s\S]+?)\n---+\s*\n/, function (wholematch, format, content) { - if (format) { - globals.metadata.format = format; - } - parseMetadataContents(content); - return '¨M'; + // 2. Metadata with YAML delimiters + const rgx2 = /^\s*---+\s*(\S*?)\n([\s\S]+?)\n---+\s*\n/; + text = text.replace(rgx2, function (wholeMatch, format, content) { + return parseMetadataContents(rgx1, wholeMatch, format, content); }); - text = text.replace(/¨M/g, ''); - - text = globals.converter._dispatch('makehtml.metadata.after', text, options, globals).getText(); - return text; + let afterEvent = new showdown.Event('makehtml.metadata.onEnd', text); + afterEvent + .setOutput(text) + ._setGlobals(globals) + ._setOptions(options); + afterEvent = globals.converter.dispatch(afterEvent); + return afterEvent.output; });