feat(metadata): add support for embedded metadata

A simple metadata parser can be useful in markdown documents.
This commit introduces the feature, with the following syntax:

--- or ««« at tstart of the document,
(optionally) followed by a alphanumeric format identifier
followed by key value pairs separated by a colon and a space
followed by --- or ÂÂÂ

Also, adds methods for retrieving the parsed metadata, namely:

getMetadata() and getMetadataFormat

Closes #260
This commit is contained in:
Estevao Soares dos Santos 2017-12-10 07:15:09 +00:00
parent a8427c9423
commit 63d949f731
31 changed files with 662 additions and 41 deletions

182
dist/showdown.js vendored
View File

@ -153,10 +153,15 @@ function getDefaultOpts (simple) {
description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
type: 'boolean'
},
completeHTMLOutput: {
completeHTMLDocument: {
defaultValue: false,
description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
type: 'boolean'
},
metadata: {
defaultValue: false,
description: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).',
type: 'boolean'
}
};
if (simple === false) {
@ -2172,7 +2177,17 @@ showdown.Converter = function (converterOptions) {
/**
* The flavor set in this converter
*/
setConvFlavor = setFlavor;
setConvFlavor = setFlavor,
/**
* Metadata of the document
* @type {{parsed: {}, raw: string, format: string}}
*/
metadata = {
parsed: {},
raw: '',
format: ''
};
_constructor();
@ -2384,7 +2399,12 @@ showdown.Converter = function (converterOptions) {
langExtensions: langExtensions,
outputModifiers: outputModifiers,
converter: this,
ghCodeBlocks: []
ghCodeBlocks: [],
metadata: {
parsed: {},
raw: '',
format: ''
}
};
// This lets us use ¨ trema as an escape char to avoid md5 hashes
@ -2428,6 +2448,7 @@ showdown.Converter = function (converterOptions) {
});
// run the sub parsers
text = showdown.subParser('metadata')(text, options, globals);
text = showdown.subParser('hashPreCodeTags')(text, options, globals);
text = showdown.subParser('githubCodeBlocks')(text, options, globals);
text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
@ -2444,13 +2465,15 @@ showdown.Converter = function (converterOptions) {
text = text.replace(/¨T/g, '¨');
// render a complete html document instead of a partial if the option is enabled
text = showdown.subParser('completeHTMLOutput')(text, options, globals);
text = showdown.subParser('completeHTMLDocument')(text, options, globals);
// Run output modifiers
showdown.helper.forEach(outputModifiers, function (ext) {
text = showdown.subParser('runExtension')(ext, text, options, globals);
});
// update metadata
metadata = globals.metadata;
return text;
};
@ -2558,6 +2581,52 @@ showdown.Converter = function (converterOptions) {
output: outputModifiers
};
};
/**
* Get the metadata of the previously parsed document
* @param raw
* @returns {string|{}}
*/
this.getMetadata = function (raw) {
if (raw) {
return metadata.raw;
} else {
return metadata.parsed;
}
};
/**
* Get the metadata format of the previously parsed document
* @returns {string}
*/
this.getMetadataFormat = function () {
return metadata.format;
};
/**
* Private: set a single key, value metadata pair
* @param {string} key
* @param {string} value
*/
this._setMetadataPair = function (key, value) {
metadata.parsed[key] = value;
};
/**
* Private: set metadata format
* @param {string} format
*/
this._setMetadataFormat = function (format) {
metadata.format = format;
};
/**
* Private: set metadata raw text
* @param {string} raw
*/
this._setMetadataRaw = function (raw) {
metadata.raw = raw;
};
};
/**
@ -2899,18 +2968,63 @@ showdown.subParser('codeSpans', function (text, options, globals) {
/**
* Turn Markdown link shortcuts into XHTML <a> tags.
*/
showdown.subParser('completeHTMLOutput', function (text, options, globals) {
showdown.subParser('completeHTMLDocument', function (text, options, globals) {
'use strict';
if (!options.completeHTMLOutput) {
if (!options.completeHTMLDocument) {
return text;
}
text = globals.converter._dispatch('completeHTMLOutput.before', text, options, globals);
text = globals.converter._dispatch('completeHTMLDocument.before', text, options, globals);
text = '<html>\n<head>\n<meta charset="UTF-8">\n</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
var doctype = 'html',
doctypeParsed = '<!DOCTYPE HTML>\n',
title = '',
charset = '<meta charset="utf-8">\n',
lang = '',
metadata = '';
text = globals.converter._dispatch('completeHTMLOutput.after', text, options, globals);
if (typeof globals.metadata.parsed.doctype !== 'undefined') {
doctypeParsed = '<!DOCTYPE ' + globals.metadata.parsed.doctype + '>\n';
doctype = globals.metadata.parsed.doctype.toString().toLowerCase();
if (doctype === 'html' || doctype === 'html5') {
charset = '<meta charset="utf-8">';
}
}
for (var meta in globals.metadata.parsed) {
if (globals.metadata.parsed.hasOwnProperty(meta)) {
switch (meta.toLowerCase()) {
case 'doctype':
break;
case 'title':
title = '<title>' + globals.metadata.parsed.title + '</title>\n';
break;
case 'charset':
if (doctype === 'html' || doctype === 'html5') {
charset = '<meta charset="' + globals.metadata.parsed.charset + '">\n';
} else {
charset = '<meta name="charset" content="' + globals.metadata.parsed.charset + '">\n';
}
break;
case 'language':
case 'lang':
lang = ' lang="' + globals.metadata.parsed[meta] + '"';
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
break;
default:
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
}
}
}
text = doctypeParsed + '<html' + lang + '>\n<head>\n' + title + charset + metadata + '</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
text = globals.converter._dispatch('completeHTMLDocument.after', text, options, globals);
return text;
});
@ -3886,6 +4000,56 @@ showdown.subParser('lists', function (text, options, globals) {
return text;
});
/**
* Parse metadata at the top of the document
*/
showdown.subParser('metadata', function (text, options, globals) {
'use strict';
if (!options.metadata) {
return text;
}
text = globals.converter._dispatch('metadata.before', text, options, globals);
function parseMetadataContents (content) {
// raw is raw so it's not changed in any way
globals.metadata.raw = content;
// escape chars forbidden in html attributes
// double quotes
content = content
// ampersand first
.replace(/&/g, '&amp;')
// double quotes
.replace(/"/g, '&quot;');
content = content.replace(/\n {4}/g, ' ');
content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
globals.metadata.parsed[key] = value;
return '';
});
}
text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) {
parseMetadataContents(content);
return '¨M';
});
text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) {
if (format) {
globals.metadata.format = format;
}
parseMetadataContents(content);
return '¨M';
});
text = text.replace(/¨M/g, '');
text = globals.converter._dispatch('metadata.after', text, options, globals);
return text;
});
/**
* Remove one level of line-leading tabs or spaces
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -43,7 +43,17 @@ showdown.Converter = function (converterOptions) {
/**
* The flavor set in this converter
*/
setConvFlavor = setFlavor;
setConvFlavor = setFlavor,
/**
* Metadata of the document
* @type {{parsed: {}, raw: string, format: string}}
*/
metadata = {
parsed: {},
raw: '',
format: ''
};
_constructor();
@ -255,7 +265,12 @@ showdown.Converter = function (converterOptions) {
langExtensions: langExtensions,
outputModifiers: outputModifiers,
converter: this,
ghCodeBlocks: []
ghCodeBlocks: [],
metadata: {
parsed: {},
raw: '',
format: ''
}
};
// This lets us use ¨ trema as an escape char to avoid md5 hashes
@ -299,6 +314,7 @@ showdown.Converter = function (converterOptions) {
});
// run the sub parsers
text = showdown.subParser('metadata')(text, options, globals);
text = showdown.subParser('hashPreCodeTags')(text, options, globals);
text = showdown.subParser('githubCodeBlocks')(text, options, globals);
text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
@ -315,13 +331,15 @@ showdown.Converter = function (converterOptions) {
text = text.replace(/¨T/g, '¨');
// render a complete html document instead of a partial if the option is enabled
text = showdown.subParser('completeHTMLOutput')(text, options, globals);
text = showdown.subParser('completeHTMLDocument')(text, options, globals);
// Run output modifiers
showdown.helper.forEach(outputModifiers, function (ext) {
text = showdown.subParser('runExtension')(ext, text, options, globals);
});
// update metadata
metadata = globals.metadata;
return text;
};
@ -429,4 +447,50 @@ showdown.Converter = function (converterOptions) {
output: outputModifiers
};
};
/**
* Get the metadata of the previously parsed document
* @param raw
* @returns {string|{}}
*/
this.getMetadata = function (raw) {
if (raw) {
return metadata.raw;
} else {
return metadata.parsed;
}
};
/**
* Get the metadata format of the previously parsed document
* @returns {string}
*/
this.getMetadataFormat = function () {
return metadata.format;
};
/**
* Private: set a single key, value metadata pair
* @param {string} key
* @param {string} value
*/
this._setMetadataPair = function (key, value) {
metadata.parsed[key] = value;
};
/**
* Private: set metadata format
* @param {string} format
*/
this._setMetadataFormat = function (format) {
metadata.format = format;
};
/**
* Private: set metadata raw text
* @param {string} raw
*/
this._setMetadataRaw = function (raw) {
metadata.raw = raw;
};
};

View File

@ -151,10 +151,15 @@ function getDefaultOpts (simple) {
description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
type: 'boolean'
},
completeHTMLOutput: {
completeHTMLDocument: {
defaultValue: false,
description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
type: 'boolean'
},
metadata: {
defaultValue: false,
description: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).',
type: 'boolean'
}
};
if (simple === false) {

View File

@ -0,0 +1,62 @@
/**
* Turn Markdown link shortcuts into XHTML <a> tags.
*/
showdown.subParser('completeHTMLDocument', function (text, options, globals) {
'use strict';
if (!options.completeHTMLDocument) {
return text;
}
text = globals.converter._dispatch('completeHTMLDocument.before', text, options, globals);
var doctype = 'html',
doctypeParsed = '<!DOCTYPE HTML>\n',
title = '',
charset = '<meta charset="utf-8">\n',
lang = '',
metadata = '';
if (typeof globals.metadata.parsed.doctype !== 'undefined') {
doctypeParsed = '<!DOCTYPE ' + globals.metadata.parsed.doctype + '>\n';
doctype = globals.metadata.parsed.doctype.toString().toLowerCase();
if (doctype === 'html' || doctype === 'html5') {
charset = '<meta charset="utf-8">';
}
}
for (var meta in globals.metadata.parsed) {
if (globals.metadata.parsed.hasOwnProperty(meta)) {
switch (meta.toLowerCase()) {
case 'doctype':
break;
case 'title':
title = '<title>' + globals.metadata.parsed.title + '</title>\n';
break;
case 'charset':
if (doctype === 'html' || doctype === 'html5') {
charset = '<meta charset="' + globals.metadata.parsed.charset + '">\n';
} else {
charset = '<meta name="charset" content="' + globals.metadata.parsed.charset + '">\n';
}
break;
case 'language':
case 'lang':
lang = ' lang="' + globals.metadata.parsed[meta] + '"';
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
break;
default:
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
}
}
}
text = doctypeParsed + '<html' + lang + '>\n<head>\n' + title + charset + metadata + '</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
text = globals.converter._dispatch('completeHTMLDocument.after', text, options, globals);
return text;
});

View File

@ -1,17 +0,0 @@
/**
* Turn Markdown link shortcuts into XHTML <a> tags.
*/
showdown.subParser('completeHTMLOutput', function (text, options, globals) {
'use strict';
if (!options.completeHTMLOutput) {
return text;
}
text = globals.converter._dispatch('completeHTMLOutput.before', text, options, globals);
text = '<html>\n<head>\n<meta charset="UTF-8">\n</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
text = globals.converter._dispatch('completeHTMLOutput.after', text, options, globals);
return text;
});

View File

@ -0,0 +1,49 @@
/**
* Parse metadata at the top of the document
*/
showdown.subParser('metadata', function (text, options, globals) {
'use strict';
if (!options.metadata) {
return text;
}
text = globals.converter._dispatch('metadata.before', text, options, globals);
function parseMetadataContents (content) {
// raw is raw so it's not changed in any way
globals.metadata.raw = content;
// escape chars forbidden in html attributes
// double quotes
content = content
// ampersand first
.replace(/&/g, '&amp;')
// double quotes
.replace(/"/g, '&quot;');
content = content.replace(/\n {4}/g, ' ');
content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
globals.metadata.parsed[key] = value;
return '';
});
}
text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) {
parseMetadataContents(content);
return '¨M';
});
text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) {
if (format) {
globals.metadata.format = format;
}
parseMetadataContents(content);
return '¨M';
});
text = text.replace(/¨M/g, '');
text = globals.converter._dispatch('metadata.after', text, options, globals);
return text;
});

View File

@ -1,14 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta charset="utf-8">
</head>
<body>
<p>This is a <strong>markdown</strong> file</p>
<p>Converted into a full HTML document</p>
<ul>
<li>this</li>
<li>is</li>
<li>awesome</li>
<li>this</li>
<li>is</li>
<li>awesome</li>
</ul>
</body>
</html>

View File

@ -0,0 +1,8 @@
<p><strong>some</strong> markdown text</p>
<ul>
<li>a list</li>
<li>another list ---</li>
<li>and stuff</li>
</ul>
<p>a paragraph --- with dashes</p>
<hr />

View File

@ -0,0 +1,16 @@
---
title: This is the document title
language: en
author: Tivie
---
**some** markdown text
- a list
- another list ---
- and stuff
a paragraph --- with dashes
---

View File

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>This is the document title</title>
<meta charset="utf-8">
<meta name="language" content="en">
<meta name="author" content="Tivie">
<meta name="contributors" content="John, Mary, Steve">
<meta name="description" content="This is a long text and so it spans on multiple lines. It must be indented, for showdown to parse it correctly. Markdown **such as bold** is not parsed and it will be rendered as plain text.">
<meta name="date" content="01-01-2010">
<meta name="keywords" content="foo, bar, baz">
</head>
<body>
<p><strong>some</strong> markdown text</p>
</body>
</html>

View File

@ -0,0 +1,16 @@
«««
title: This is the document title
language: en
author: Tivie
contributors: John, Mary, Steve
description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown **such as bold** is not parsed
and it will be rendered as plain text.
date: 01-01-2010
keywords: foo, bar, baz
»»»
**some** markdown text

View File

@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>This is the document title</title>
<meta charset="utf-8">
<meta name="language" content="en">
<meta name="author" content="Tivie">
<meta name="contributors" content="John, Mary, Steve">
<meta name="keywords" content="foo, bar, baz">
</head>
<body>
<hr />
<p>description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown <strong>such as bold</strong> is not parsed
and it will be rendered as plain text.</p>
<h2 id="date01012010">date: 01-01-2010</h2>
<p><strong>some</strong> markdown text</p>
</body>
</html>

View File

@ -0,0 +1,18 @@
«««
title: This is the document title
language: en
author: Tivie
contributors: John, Mary, Steve
keywords: foo, bar, baz
»»»
---
description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown **such as bold** is not parsed
and it will be rendered as plain text.
date: 01-01-2010
---
**some** markdown text

View File

@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>This is the document title</title>
<meta charset="utf-8">
<meta name="language" content="en">
<meta name="author" content="Tivie">
<meta name="contributors" content="John, Mary, Steve">
<meta name="keywords" content="foo, bar, baz">
</head>
<body>
<hr />
<p>description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown <strong>such as bold</strong> is not parsed
and it will be rendered as plain text.</p>
<h2 id="date01012010">date: 01-01-2010</h2>
<p><strong>some</strong> markdown text</p>
</body>
</html>

View File

@ -0,0 +1,18 @@
---
title: This is the document title
language: en
author: Tivie
contributors: John, Mary, Steve
keywords: foo, bar, baz
---
---
description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown **such as bold** is not parsed
and it will be rendered as plain text.
date: 01-01-2010
---
**some** markdown text

View File

@ -0,0 +1,15 @@
<hr />
<p>title: This is the document title
language: en
author: Tivie
contributors: John, Mary, Steve
description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown <strong>such as bold</strong> is not parsed
and it will be rendered as plain text.
date: 01-01-2010
keywords: foo, bar, baz</p>
<hr />
<p><strong>some</strong> markdown text</p>

View File

@ -0,0 +1,18 @@
---
title: This is the document title
language: en
author: Tivie
contributors: John, Mary, Steve
description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown **such as bold** is not parsed
and it will be rendered as plain text.
date: 01-01-2010
keywords: foo, bar, baz
---
**some** markdown text

View File

@ -0,0 +1 @@
<p>some <strong>text</strong></p>

View File

@ -0,0 +1,6 @@
«««
foo: bar
baz: bazinga
»»»
some **text**

View File

@ -0,0 +1 @@
<p><strong>some</strong> markdown text</p>

View File

@ -0,0 +1,16 @@
«««
title: This is the document title
language: en
author: Tivie
contributors: John, Mary, Steve
description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown **such as bold** is not parsed
and it will be rendered as plain text.
date: 01-01-2010
keywords: foo, bar, baz
»»»
**some** markdown text

View File

@ -0,0 +1 @@
<p><strong>some</strong> markdown text</p>

View File

@ -0,0 +1,16 @@
---
title: This is the document title
language: en
author: Tivie
contributors: John, Mary, Steve
description: This is a long text and so it
spans on multiple lines.
It must be indented,
for showdown to parse it correctly.
Markdown **such as bold** is not parsed
and it will be rendered as plain text.
date: 01-01-2010
keywords: foo, bar, baz
---
**some** markdown text

View File

@ -0,0 +1 @@
<p><strong>some</strong> markdown text</p>

View File

@ -0,0 +1,9 @@
---YAML
foo: bar
baz:
- bazinga
- bling
- blang
---
**some** markdown text

View File

@ -27,6 +27,20 @@ describe('showdown.Converter', function () {
});
});
describe('metadata methods', function () {
var converter = new showdown.Converter();
it('_setMetadataPair() should set foo to bar', function () {
converter._setMetadataPair('foo', 'bar');
converter.getMetadata().should.eql({foo: 'bar'});
});
it('_setMetadata should set metadata to {baz: bazinga}', function () {
converter._setMetadataRaw('{baz: bazinga}');
converter.getMetadata(true).should.eql('{baz: bazinga}');
});
});
describe('converter.setFlavor()', function () {
/**

View File

@ -59,4 +59,41 @@ describe('showdown.Converter', function () {
html.should.equal(expectedHtml);
});
});
describe('makeHtml() with option metadata', function () {
var converter = new showdown.Converter(),
text1 =
'---SIMPLE\n' +
'foo: bar\n' +
'baz: bazinga\n' +
'---\n',
text2 =
'---TIVIE\n' +
'a: b\n' +
'c: 123\n' +
'---\n';
it('should correctly set metadata', function () {
converter.setOption('metadata', true);
var expectedHtml = '',
expectedObj = {foo: 'bar', baz: 'bazinga'},
expectedRaw = 'foo: bar\nbaz: bazinga',
expectedFormat = 'SIMPLE';
converter.makeHtml(text1).should.equal(expectedHtml);
converter.getMetadata().should.eql(expectedObj);
converter.getMetadata(true).should.equal(expectedRaw);
converter.getMetadataFormat().should.equal(expectedFormat);
});
it('consecutive calls should reset metadata', function () {
converter.makeHtml(text2);
var expectedObj = {a: 'b', c: '123'},
expectedRaw = 'a: b\nc: 123',
expectedFormat = 'TIVIE';
converter.getMetadata().should.eql(expectedObj);
converter.getMetadata(true).should.equal(expectedRaw);
converter.getMetadataFormat().should.equal(expectedFormat);
});
});
});

View File

@ -15,7 +15,8 @@ var bootstrap = require('../bootstrap.js'),
underlineSuite = bootstrap.getTestSuite('test/features/underline/'),
literalMidWordUnderscoresSuite = bootstrap.getTestSuite('test/features/literalMidWordUnderscores/'),
literalMidWordAsterisksSuite = bootstrap.getTestSuite('test/features/literalMidWordAsterisks/'),
completeHTMLOutputSuite = bootstrap.getTestSuite('test/features/completeHTMLOutput/');
completeHTMLOutputSuite = bootstrap.getTestSuite('test/features/completeHTMLOutput/'),
metadataSuite = bootstrap.getTestSuite('test/features/metadata/');
describe('makeHtml() features testsuite', function () {
'use strict';
@ -233,12 +234,33 @@ describe('makeHtml() features testsuite', function () {
}
});
/** test completeHTMLOutput option **/
describe('completeHTMLOutput option', function () {
/** test completeHTMLDocument option **/
describe('completeHTMLDocument option', function () {
var converter,
suite = completeHTMLOutputSuite;
for (var i = 0; i < suite.length; ++i) {
converter = new showdown.Converter({completeHTMLOutput: true});
converter = new showdown.Converter({completeHTMLDocument: true});
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
});
/** test metadata option **/
describe('metadata option', function () {
var converter,
suite = metadataSuite;
for (var i = 0; i < suite.length; ++i) {
if (suite[i].name === 'embeded-in-output' ||
suite[i].name === 'embeded-two-consecutive-metadata-blocks' ||
suite[i].name === 'embeded-two-consecutive-metadata-blocks-different-symbols') {
converter = new showdown.Converter({metadata: true, completeHTMLDocument: true});
} else if (suite[i].name === 'ignore-metadata') {
converter = new showdown.Converter({metadata: false});
} else {
converter = new showdown.Converter({metadata: true});
}
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
}
});