showdown/docs/event_system.md
Estevão Soares dos Santos 9f8c7199ea
feat(makehtml.events): implements event system refactor for converter.makeHtml (#919)
* startrefactoring the event system

* refactor: blockquotes, code blocks and links refactored

* refactor codeblock to new event system

* refactor subparser until ghcode to new events

* finish adating ghcodeblock to new event

* add headings to new events

* add image to event system

* add emphasisAndStrong to event system

* fix wrong event name in emphasisAndStrong onEnd event

* spanGamut and build

* showdown.helper.event refactored to showdown.Event

* partial

* add links

* add metadata

* add strikethrough and table

* build

* add underline

* add unescapeSpecialChars

* small refactoring

* remove old tables parser

* add lists

* add simple event trigger tests

* build

* fix browserstack

* fix browserstack

* remove testing for ie11 and bumped firefox min version to 45

* fixes and closes #920

* build
2022-04-27 21:42:24 +01:00

10 KiB

Event System )

Introduction

The Event System is the basis of the new Listener extensions, replacing the old extension system altogether.

The life cycle can be summarized as this:

  • A subparsers emmits an event
    • Each subparser can emmit a bunch of events (see list below)
  • Extension A, which is a Listener Extension registers and listens to a specific event
    • An extension can only register to a specific event
  • Extension A receives an event object and modifies it
    • Certain properties of the event object can be changed, ehich will change the behavior or output of the subparser
  • The extension then returns the event object
  • The converter passes this event object to the next extension in the chain

Note:

The Event Object

Properties

matches (type: object)

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.

Example:

// blockquote onCapture event
{
  _wholeMatch: "> some awesome quote",
  blockquote: "some awesome quote"
}

Basic Event

Event Types

Events are raised when a subparser is run (or about to be run). Within a subparser, the events always follow a certain order (sequence). For instance, .before events always run before .captureStart. Each subparser raises several events sequentially:

onStart

<converter>.<subparser>.onStart: always runs except if the subparser is disabled

Raised when the subparser has started, but no capturing or any modification to the text was done. Always runs (except if the subparser is deactivated through options).

Properties:

property type access description
input string read The full text that was passed to the subparser
output string write The full text with modification that will be passed along the chain
regexp null
matches null
attributes null

Usually you would want to use this event if you wish to change the input to the subparser. Please note, however, that the input text is the complete text that was fed to the converter.

onCapture

<converter>.<subparser>.onCapture: might not be run;

Raised when a regex match is found and a capture was successful. Some normalization and modification of the regex captured groups might be performed.

Might not be run if no regex match is found.

Properties:

property type access description
input string readonly The captured text
output string write null or well formed HTML (see note)
regexp RegExp readonly Regular Expression used to capture groups
matches object read/write Matches groups. Changes to this object are reflected in the output
attributes object read/write Attributes to add to the HTML output

Usually you would want to use this event if you wish to modify the subparser behavior, text or modify the HTML output 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 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 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.
// showdown extension 1 that is listening to makehtml.blockquote.onCapture
function extension_1 (showdownEvent) {
  // i'm bad and write to output
  showdownEvent.output = '<blockquote>foo</blockquote>'; // must be an well formed html string
  showdownEvent.matches.blockquote = 'some nice quote'; 
}

// showdown extension 1 that is also listening to makehtml.blockquote.onCapture
function extension_2 (showdownEvent) {
  // I make blockquotes become bold
  let quote = showdownEvent.matches.blockquote;
  showdownEvent.matches.blockquote = '<strong>' + quote + '</strong>'; 
}

// the result will always be <blockquote>foo</blockquote>, regardless of the order of extension loading and call

onHash

<converter>.<subparser>.onHash: always runs;

Raised before the output is hashed. Always runs (except if the subparser was deactivated through options), even if no hashing is performed.

Properties:

property type access description
input string read The captured text
output string write The text that will be passed to the subparser/other listeners
regexp null
matches null
attributes null

Usually you would want to use this event if you wish change the subparser's raw output before it is hashed.

onEnd

<converter>.<subparser>.onEnd: always runs;

Raised when the subparser has finished its work and is about to exit.

Always runs (except if the subparser is deactivated through options).

Properties:

property type access description
input string read The full text with the subparser modifications (contains hashes)
output string write The text that will be passed to other subparsers
regexp null
matches null

Usually you would want to use this event if you want to run code or perform changes to the text after the subparser was run and it's output was hashed. Keep in mind that the input is the full text and might contain hashed elements.

Special Events

There are some special events that are useful for "positioning" a listener extension in the main chain of events. Usually these extensions introduce new syntax that, due to precedence These events are always guaranteed to be called, regardless of options or circumstances.

  1. .before_{subparserName}: always runs

    Raised just before the {subparserName} is about to be entered.

    Properties:

    property type access description
    input string read The full text that was passed to the subparser
    output string write The full text with modification that will be passed along the chain
    regexp null
    matches null
  2. .after.{subparserName}: always runs;

    Raised when the {subparserName} has exited and before the next one is called.

    Properties:

    property type access description
    input string read The partial/full text with the subparser modifications
    output string write The text that will be passed to other subparsers
    regexp null
    matches null

Notes

  • There are 2 main differences between before.{subparserName} and {subparserName}.start.

    1. before.{subparserName} is always guaranteed to be called, even if the subparser is disabled, while {subparserName}.start doesn't.

      ex: makehtml.before.strikethrough is always called even if the option strikethrough is false

    2. before.{subparserName} is only raised once in a span context while {subparserName}.start is raised everytime {subparserName} is called.

    As a rule of thumb,

Events List

blockquote

metadata

Common pitfalls

Infinite loop

If you pass the input as output in a onCapture event, you might trigger an infinite loop.