Skip to content

Extensions

Estevão Soares dos Santos edited this page Oct 18, 2015 · 12 revisions

Showdown allows additional functionality to be loaded via extensions.

Client-side Extension Usage

<script src="src/showdown.js" />
<script src="src/extensions/twitter.js" />

var converter = new showdown.Converter({ extensions: ['twitter'] });

Server-side Extension Usage

// Using a bundled extension
var Showdown = require('showdown');
var converter = new showdown.Converter({ extensions: ['twitter'] });

// Using a custom extension
var mine = require('./custom-extensions/mine');
var converter = new showdown.Converter({ extensions: ['twitter', mine] });

Creating Markdown Extensions

A showdown extension is simply a function which returns an array of language extensions and/or output modifiers:

  • Language Extension -- Language extensions are specified with the lang type, and add new markdown syntax to showdown. For example, say you wanted ^^youtube http://www.youtube.com/watch?v=oHg5SJYRHA0 to automatically render as an embedded YouTube video, that would be a language extension.
  • Output Modifiers -- Output Modifiers are specified with the output type. After showdown has generated HTML, an output modifier can make changes to the generated HTML. For example, if you wanted to change <div class="header"> to be <header>, you could implement an output modifier.

Each showdown extension can provide language extensions and/or output modifiers.

Regex/Replace

Regex/replace style extensions are very similar to javascripts string.replace function. Two properties are given, regex and replace. regex is a string and replace can be either a string or a function. If replace is a string, it can use the $1 syntax for group substitution, exactly as if it were making use of string.replace (internally it does this actually); The value of regex is assumed to be a global replacement.

Example:

var demo = function(converter) {
  return [
    // Replace escaped @ symbols
    { type: 'lang', regex: '\\@', replace: '@' }
  ];
}

Filter

Alternately, if you'd just like to do everything yourself, you can specify a filter which is a callback with a single input parameter, text (the current source text within the showdown engine).

Example:

var demo = function(converter) {
  return [
    // Replace escaped @ symbols
    { type: 'lang', filter: function(text) {
      return text.replace(/\\@/g, '@');
    }}
  ];
}

Implementation Concerns

One bit which should be taken into account is maintaining both client-side and server-side compatibility. This can be achieved with a few lines of boilerplate code. First, to prevent polluting the global scope for client-side code, the extension definition should be wrapped in a self-executing function.

(function(){
  // Your extension here
}());

Second, client-side extensions should add a property onto Showdown.extensions which matches the name of the file. As an example, a file named demo.js should then add Showdown.extensions.demo. Server-side extensions can simply export themselves.

(function(){
  var demo = function(converter) {
    // ... extension code here ...
  };

  // Client-side export
  if (typeof window !== 'undefined' && window.showdown && window.showdown.extensions) {
    window.showdown.extensions.demo = demo;
  }
  // Server-side export
  if (typeof module !== 'undefined') module.exports = demo;
}());

Gotchas

Showdown does the following escape/normalization:

  • Replaces ~ (tilde) with ~T
  • Replaces $ (dollar sign) with ~D
  • Normalizes line endings (\r, \r\n are converted into \n)
  • uses \r as a char placeholder

This happens before language extensions are run, which means that if your extension relies on any of those chars, you have to take this into consideration and make the appropriate modifications.

This only applies to language extensions since these chars are unescaped before output extensions are run.

Testing Extensions

The showdown test runner is setup to automatically test cases for extensions. To add test cases for an extension, create a new folder under ./test/extensions which matches the name of the .js file in ./src/extensions. Place any test cases into the filter using the md/html format and they will automatically be run when tests are run.