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;
 });