diff --git a/docs/event_system.md b/docs/event_system.md index ca725f8..cc2dd49 100644 --- a/docs/event_system.md +++ b/docs/event_system.md @@ -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 diff --git a/src/helpers.js b/src/helpers.js index 146435f..e0f9b5b 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -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 { /** diff --git a/src/subParsers/makehtml/blockQuotes.js b/src/subParsers/makehtml/blockQuotes.js index bb90f2f..4398e19 100644 --- a/src/subParsers/makehtml/blockQuotes.js +++ b/src/subParsers/makehtml/blockQuotes.js @@ -75,13 +75,7 @@ showdown.subParser('makehtml.blockQuotes', function (text, options, globals) { return pre; }); attributes = captureStartEvent.attributes; - otp = '
\n' + bq + '\n'; + otp = '
\n' + bq + '\n'; } let beforeHashEvent = new showdown.helper.Event('makehtml.blockQuotes.onHash', otp); diff --git a/src/subParsers/makehtml/codeBlocks.js b/src/subParsers/makehtml/codeBlocks.js index 9a39c12..a5925b1 100644 --- a/src/subParsers/makehtml/codeBlocks.js +++ b/src/subParsers/makehtml/codeBlocks.js @@ -65,24 +65,8 @@ showdown.subParser('makehtml.codeBlocks', function (text, options, globals) { otp = '
';
if (!showdown.helper.isUndefined(attributes)) {
- otp = '';
+ otp = '';
+ otp += '';
}
if (options.omitExtraWLInCodeBlocks) {
end = '';
diff --git a/src/subParsers/makehtml/codeSpans.js b/src/subParsers/makehtml/codeSpans.js
index e968180..5780aff 100644
--- a/src/subParsers/makehtml/codeSpans.js
+++ b/src/subParsers/makehtml/codeSpans.js
@@ -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 + '' + c + '
';
- 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 + '' + c + '
';
+ }
+
+ 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;
});