refactor codeblock to new event system

This commit is contained in:
Estevão Soares dos Santos 2022-03-31 05:35:50 +01:00
parent 997adada9d
commit 954e065193
5 changed files with 101 additions and 40 deletions

View File

@ -74,11 +74,14 @@ Usually you would want to use this event if you wish to modify the subparser beh
of the subparser.
**IMPORTANT NOTE**: Extensions listening to onCapture event should try to AVOID changing the output property.
Instead, they should modify the values of the matches and attributes arrays. This is because
the **output property takes precedence over the matches array**, which means 2 very important things:
Instead, they should modify the values of the matches and attributes objects. This is because
the **output property takes precedence over the matches objects** and **prevents showdown to call other subparsers**
inside the captured fragment.This means 3 very important things:
1. If something is passed as the output property, any changes to the matches array will be ignored.
2. Point 1 includes other extensions that listen to the same event.
1. If something is passed as the output property, any changes to the matches and attributes objects will be ignored.
2. Changes made by other extensions to the matches or attributes objects will also be ignored
3. Showdown will not call other subparsers, such as encode code or span gamut in the text fragment, which may lead to
weird results.
```javascript
// showdown extension 1 that is listening to makehtml.blockquote.onCapture

View File

@ -61,6 +61,22 @@ showdown.helper.isUndefined = function (value) {
return typeof value === 'undefined';
};
/**
* Check if value is an object (excluding arrays)
* @param {*} value
* @returns {boolean}
*/
showdown.helper.isObject = function (value) {
'use strict';
return (
typeof value === 'object' &&
!Array.isArray(value) &&
value !== null
);
}
/**
* ForEach helper function
* Iterates over Arrays and Objects (own properties only)
@ -551,6 +567,24 @@ showdown.helper.cloneObject = function (obj, deep) {
}
};
/**
* Populate attributes in output text
* @param {string} text
* @param {{}} attributes
* @returns {string}
*/
showdown.helper._populateAttributes = function (attributes) {
let text = '';
if (attributes && showdown.helper.isObject(attributes)) {
for (let attr in attributes) {
if (attributes.hasOwnProperty(attr)) {
text += ' ' + attr + '=' + attributes[attr];
}
}
}
return text;
};
showdown.helper.Event = class {
/**

View File

@ -75,13 +75,7 @@ showdown.subParser('makehtml.blockQuotes', function (text, options, globals) {
return pre;
});
attributes = captureStartEvent.attributes;
otp = '<blockquote';
for (let attr in attributes) {
if (attributes.hasOwnProperty(attr)) {
otp += ' ' + attr + '=' + attributes[attr];
}
}
otp += '>\n' + bq + '\n</blockquote>';
otp = '<blockquote' + showdown.helper._populateAttributes(attributes) + '>\n' + bq + '\n</blockquote>';
}
let beforeHashEvent = new showdown.helper.Event('makehtml.blockQuotes.onHash', otp);

View File

@ -65,24 +65,8 @@ showdown.subParser('makehtml.codeBlocks', function (text, options, globals) {
otp = '<pre><code>';
if (!showdown.helper.isUndefined(attributes)) {
otp = '<pre';
if (!showdown.helper.isUndefined(attributes.pre)) {
for (let preAttr in attributes.pre) {
if (attributes.hasOwnProperty(preAttr)) {
otp += ' ' + preAttr + '=' + attributes[preAttr];
}
}
}
otp += '><code';
if (!showdown.helper.isUndefined(attributes.code)) {
for (let codeAttr in attributes.code) {
if (attributes.hasOwnProperty(codeAttr)) {
otp += ' ' + codeAttr + '=' + attributes[codeAttr];
}
}
}
otp += '>';
otp = '<pre' + showdown.helper._populateAttributes(attributes.pre) + '>';
otp += '<code' + showdown.helper._populateAttributes(attributes.code) + '>';
}
if (options.omitExtraWLInCodeBlocks) {
end = '';

View File

@ -26,23 +26,69 @@
showdown.subParser('makehtml.codeSpans', function (text, options, globals) {
'use strict';
text = globals.converter._dispatch('makehtml.codeSpans.before', text, options, globals).getText();
let startEvent = new showdown.helper.Event('makehtml.codeSpans.onStart', text);
startEvent
.setOutput(text)
._setGlobals(globals)
._setOptions(options);
if (typeof (text) === 'undefined') {
startEvent = globals.converter.dispatch(startEvent);
text = startEvent.output;
if (showdown.helper.isUndefined((text))) {
text = '';
}
text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
function (wholeMatch, m1, m2, m3) {
var c = m3;
let pattern = /(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm;
text = text.replace(pattern,
function (wholeMatch, m1, m2, c) {
let otp,
attributes = {};
c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
c = showdown.subParser('makehtml.encodeCode')(c, options, globals);
c = m1 + '<code>' + c + '</code>';
c = showdown.subParser('makehtml.hashHTMLSpans')(c, options, globals);
return c;
let captureStartEvent = new showdown.helper.Event('makehtml.codeSpans.onCapture', c);
captureStartEvent
.setOutput(null)
._setGlobals(globals)
._setOptions(options)
.setRegexp(pattern)
.setMatches({
code: c
})
.setAttributes({});
captureStartEvent = globals.converter.dispatch(captureStartEvent);
// if something was passed as output, it takes precedence
// and will be used as output
if (captureStartEvent.output && captureStartEvent.output !== '') {
otp = m1 + captureStartEvent.output;
} else {
c = captureStartEvent.matches.code;
c = showdown.subParser('makehtml.encodeCode')(c, options, globals);
otp = m1 + '<code' + showdown.helper._populateAttributes(attributes) + '>' + c + '</code>';
}
let beforeHashEvent = new showdown.helper.Event('makehtml.codeSpans.onHash', otp);
beforeHashEvent
.setOutput(otp)
._setGlobals(globals)
._setOptions(options);
beforeHashEvent = globals.converter.dispatch(beforeHashEvent);
otp = beforeHashEvent.output;
return showdown.subParser('makehtml.hashHTMLSpans')(otp, options, globals);
}
);
text = globals.converter._dispatch('makehtml.codeSpans.after', text, options, globals).getText();
return text;
let afterEvent = new showdown.helper.Event('makehtml.codeSpans.onEnd', text);
afterEvent
.setOutput(text)
._setGlobals(globals)
._setOptions(options);
return afterEvent.output;
});