feat(converter.makeMarkdown): add an HTML to MD converter

Showdown now supports a simple HTML to Markdown converter.

**Usage:**

```
var conv = new showdown.Converter();
var md = conv.makeMarkdown('<a href="/url">a link</a>');
```

Closes #388, #233
This commit is contained in:
Estevao Soares dos Santos 2018-11-10 02:39:38 +00:00
parent 5c0d67e04a
commit e4b0e69724
60 changed files with 5514 additions and 24 deletions

View File

@ -25,6 +25,7 @@ module.exports = function (grunt) {
'src/helpers.js',
'src/converter.js',
'src/subParsers/*.js',
'src/subParsers/makeMarkdown/*.js',
'src/loader.js'
],
dest: 'dist/<%= pkg.name %>.js'

BIN
dist/showdown.js vendored

Binary file not shown.

BIN
dist/showdown.js.map vendored

Binary file not shown.

BIN
dist/showdown.min.js vendored

Binary file not shown.

Binary file not shown.

4676
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@
},
"devDependencies": {
"chai": "^4.0.2",
"grunt": "^1.0.1",
"grunt": "^1.0.3",
"grunt-contrib-clean": "^1.0.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-jshint": "^1.1.0",
@ -50,6 +50,7 @@
"grunt-endline": "^0.6.1",
"grunt-eslint": "^19.0.0",
"grunt-simple-mocha": "^0.4.0",
"jsdom": "^13.0.0",
"load-grunt-tasks": "^3.2.0",
"performance-now": "^2.0.0",
"quiet-grunt": "^0.2.3",

View File

@ -343,6 +343,112 @@ showdown.Converter = function (converterOptions) {
return text;
};
/**
* Converts an HTML string into a markdown string
* @param src
* @param [HTMLParser] A WHATWG DOM and HTML parser, such as JSDOM. If none is supplied, window.document will be used.
* @returns {string}
*/
this.makeMarkdown = this.makeMd = function (src, HTMLParser) {
// replace \r\n with \n
src = src.replace(/\r\n/g, '\n');
src = src.replace(/\r/g, '\n'); // old macs
// due to an edge case, we need to find this: > <
// to prevent removing of non silent white spaces
// ex: <em>this is</em> <strong>sparta</strong>
src = src.replace(/>[ \t]+</, '>¨NBSP;<');
if (!HTMLParser) {
if (window && window.document) {
HTMLParser = window.document;
} else {
throw new Error('HTMLParser is undefined. If in a webworker or nodejs environment, you need to provide a WHATWG DOM and HTML such as JSDOM');
}
}
var doc = HTMLParser.createElement('div');
doc.innerHTML = src;
var globals = {
preList: substitutePreCodeTags(doc)
};
// remove all newlines and collapse spaces
clean(doc);
// some stuff, like accidental reference links must now be escaped
// TODO
// doc.innerHTML = doc.innerHTML.replace(/\[[\S\t ]]/);
var nodes = doc.childNodes,
mdDoc = '';
for (var i = 0; i < nodes.length; i++) {
mdDoc += showdown.subParser('makeMarkdown.node')(nodes[i], globals);
}
function clean (node) {
for (var n = 0; n < node.childNodes.length; ++n) {
var child = node.childNodes[n];
if (child.nodeType === 3) {
if (!/\S/.test(child.nodeValue)) {
node.removeChild(child);
--n;
} else {
child.nodeValue = child.nodeValue.split('\n').join(' ');
child.nodeValue = child.nodeValue.replace(/(\s)+/g, '$1');
}
} else if (child.nodeType === 1) {
clean(child);
}
}
}
// find all pre tags and replace contents with placeholder
// we need this so that we can remove all indentation from html
// to ease up parsing
function substitutePreCodeTags (doc) {
var pres = doc.querySelectorAll('pre'),
presPH = [];
for (var i = 0; i < pres.length; ++i) {
if (pres[i].childElementCount === 1 && pres[i].firstChild.tagName.toLowerCase() === 'code') {
var content = pres[i].firstChild.innerHTML.trim(),
language = pres[i].firstChild.getAttribute('data-language') || '';
// if data-language attribute is not defined, then we look for class language-*
if (language === '') {
var classes = pres[i].firstChild.className.split(' ');
for (var c = 0; c < classes.length; ++c) {
var matches = classes[c].match(/^language-(.+)$/);
if (matches !== null) {
language = matches[1];
break;
}
}
}
// unescape html entities in content
content = showdown.helper.unescapeHTMLEntities(content);
presPH.push(content);
pres[i].outerHTML = '<precode language="' + language + '" precodenum="' + i.toString() + '"></precode>';
} else {
presPH.push(pres[i].innerHTML);
pres[i].innerHTML = '';
pres[i].setAttribute('prenum', i.toString());
}
}
return presPH;
}
return mdDoc;
};
/**
* Set an option of this Converter instance
* @param {string} key

View File

@ -140,6 +140,21 @@ showdown.helper.escapeCharacters = function (text, charsToEscape, afterBackslash
return text;
};
/**
* Unescape HTML entities
* @param txt
* @returns {string}
*/
showdown.helper.unescapeHTMLEntities = function (txt) {
'use strict';
return txt
.replace(/&quot;/g, '"')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&');
};
var rgxFindMatchPos = function (str, left, right, flags) {
'use strict';
var f = flags || '',
@ -350,6 +365,31 @@ showdown.helper.encodeEmailAddress = function (mail) {
return mail;
};
/**
*
* @param str
* @param targetLength
* @param padString
* @returns {string}
*/
showdown.helper.padEnd = function padEnd (str, targetLength, padString) {
'use strict';
/*jshint bitwise: false*/
// eslint-disable-next-line space-infix-ops
targetLength = targetLength>>0; //floor if number or convert non-number to 0;
/*jshint bitwise: true*/
padString = String(padString || ' ');
if (str.length > targetLength) {
return String(str);
} else {
targetLength = targetLength - str.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
}
return String(str) + padString.slice(0,targetLength);
}
};
/**
* POLYFILLS
*/

View File

@ -0,0 +1,22 @@
showdown.subParser('makeMarkdown.blockquote', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
var innerTxt = showdown.subParser('makeMarkdown.node')(children[i], globals);
if (innerTxt === '') {
continue;
}
txt += innerTxt;
}
}
// cleanup
txt = txt.trim();
txt = '> ' + txt.split('\n').join('\n> ');
return txt;
});

View File

@ -0,0 +1,7 @@
showdown.subParser('makeMarkdown.codeBlock', function (node, globals) {
'use strict';
var lang = node.getAttribute('language'),
num = node.getAttribute('precodenum');
return '```' + lang + '\n' + globals.preList[num] + '\n```';
});

View File

@ -0,0 +1,5 @@
showdown.subParser('makeMarkdown.codeSpan', function (node) {
'use strict';
return '`' + node.innerHTML + '`';
});

View File

@ -0,0 +1,15 @@
showdown.subParser('makeMarkdown.emphasis', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
txt += '*';
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
txt += '*';
}
return txt;
});

View File

@ -0,0 +1,17 @@
showdown.subParser('makeMarkdown.header', function (node, globals, headerLevel) {
'use strict';
var headerMark = new Array(headerLevel + 1).join('#'),
txt = '';
if (node.hasChildNodes()) {
txt = headerMark + ' ';
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
}
return txt;
});

View File

@ -0,0 +1,5 @@
showdown.subParser('makeMarkdown.hr', function () {
'use strict';
return '---';
});

View File

@ -0,0 +1,18 @@
showdown.subParser('makeMarkdown.image', function (node) {
'use strict';
var txt = '';
if (node.hasAttribute('src')) {
txt += '![' + node.getAttribute('alt') + '](';
txt += '<' + node.getAttribute('src') + '>';
if (node.hasAttribute('width') && node.hasAttribute('height')) {
txt += ' =' + node.getAttribute('width') + 'x' + node.getAttribute('height');
}
if (node.hasAttribute('title')) {
txt += ' "' + node.getAttribute('title') + '"';
}
txt += ')';
}
return txt;
});

View File

@ -0,0 +1,20 @@
showdown.subParser('makeMarkdown.links', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes() && node.hasAttribute('href')) {
var children = node.childNodes,
childrenLength = children.length;
txt = '[';
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
txt += '](';
txt += '<' + node.getAttribute('href') + '>';
if (node.hasAttribute('title')) {
txt += ' "' + node.getAttribute('title') + '"';
}
txt += ')';
}
return txt;
});

View File

@ -0,0 +1,33 @@
showdown.subParser('makeMarkdown.list', function (node, globals, type) {
'use strict';
var txt = '';
if (!node.hasChildNodes()) {
return '';
}
var listItems = node.childNodes,
listItemsLenght = listItems.length,
listNum = node.getAttribute('start') || 1;
for (var i = 0; i < listItemsLenght; ++i) {
if (typeof listItems[i].tagName === 'undefined' || listItems[i].tagName.toLowerCase() !== 'li') {
continue;
}
// define the bullet to use in list
var bullet = '';
if (type === 'ol') {
bullet = listNum.toString() + '. ';
} else {
bullet = '- ';
}
// parse list item
txt += bullet + showdown.subParser('makeMarkdown.listItem')(listItems[i], globals);
++listNum;
}
// add comment at the end to prevent consecutive lists to be parsed as one
txt += '\n<!-- -->\n';
return txt.trim();
});

View File

@ -0,0 +1,25 @@
showdown.subParser('makeMarkdown.listItem', function (node, globals) {
'use strict';
var listItemTxt = '';
var children = node.childNodes,
childrenLenght = children.length;
for (var i = 0; i < childrenLenght; ++i) {
listItemTxt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
// if it's only one liner, we need to add a newline at the end
if (!/\n$/.test(listItemTxt)) {
listItemTxt += '\n';
} else {
// it's multiparagraph, so we need to indent
listItemTxt = listItemTxt
.split('\n')
.join('\n ')
.replace(/^ {4}$/gm, '')
.replace(/\n\n+/g, '\n\n');
}
return listItemTxt;
});

View File

@ -0,0 +1,120 @@
showdown.subParser('makeMarkdown.node', function (node, globals, spansOnly) {
'use strict';
spansOnly = spansOnly || false;
var txt = '';
// edge case of text without wrapper paragraph
if (node.nodeType === 3) {
return showdown.subParser('makeMarkdown.txt')(node, globals);
}
// HTML comment
if (node.nodeType === 8) {
return '<!--' + node.data + '-->\n\n';
}
// process only node elements
if (node.nodeType !== 1) {
return '';
}
var tagName = node.tagName.toLowerCase();
switch (tagName) {
//
// BLOCKS
//
case 'h1':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 1) + '\n\n'; }
break;
case 'h2':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 2) + '\n\n'; }
break;
case 'h3':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 3) + '\n\n'; }
break;
case 'h4':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 4) + '\n\n'; }
break;
case 'h5':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 5) + '\n\n'; }
break;
case 'h6':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 6) + '\n\n'; }
break;
case 'p':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.paragraph')(node, globals) + '\n\n'; }
break;
case 'blockquote':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.blockquote')(node, globals) + '\n\n'; }
break;
case 'hr':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.hr')(node, globals) + '\n\n'; }
break;
case 'ol':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ol') + '\n\n'; }
break;
case 'ul':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ul') + '\n\n'; }
break;
case 'precode':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.codeBlock')(node, globals) + '\n\n'; }
break;
case 'pre':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.pre')(node, globals) + '\n\n'; }
break;
case 'table':
if (!spansOnly) { txt = showdown.subParser('makeMarkdown.table')(node, globals) + '\n\n'; }
break;
//
// SPANS
//
case 'code':
txt = showdown.subParser('makeMarkdown.codeSpan')(node, globals);
break;
case 'em':
case 'i':
txt = showdown.subParser('makeMarkdown.emphasis')(node, globals);
break;
case 'strong':
case 'b':
txt = showdown.subParser('makeMarkdown.strong')(node, globals);
break;
case 'del':
txt = showdown.subParser('makeMarkdown.strikethrough')(node, globals);
break;
case 'a':
txt = showdown.subParser('makeMarkdown.links')(node, globals);
break;
case 'img':
txt = showdown.subParser('makeMarkdown.image')(node, globals);
break;
default:
txt = node.outerHTML + '\n\n';
}
// common normalization
// TODO eventually
return txt;
});

View File

@ -0,0 +1,17 @@
showdown.subParser('makeMarkdown.paragraph', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
}
// some text normalization
txt = txt.trim();
return txt;
});

View File

@ -0,0 +1,6 @@
showdown.subParser('makeMarkdown.pre', function (node, globals) {
'use strict';
var num = node.getAttribute('prenum');
return '<pre>' + globals.preList[num] + '</pre>';
});

View File

@ -0,0 +1,15 @@
showdown.subParser('makeMarkdown.strikethrough', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
txt += '~~';
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
txt += '~~';
}
return txt;
});

View File

@ -0,0 +1,15 @@
showdown.subParser('makeMarkdown.strong', function (node, globals) {
'use strict';
var txt = '';
if (node.hasChildNodes()) {
txt += '**';
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
}
txt += '**';
}
return txt;
});

View File

@ -0,0 +1,70 @@
showdown.subParser('makeMarkdown.table', function (node, globals) {
'use strict';
var txt = '',
tableArray = [[], []],
headings = node.querySelectorAll('thead>tr>th'),
rows = node.querySelectorAll('tbody>tr'),
i, ii;
for (i = 0; i < headings.length; ++i) {
var headContent = showdown.subParser('makeMarkdown.tableCell')(headings[i], globals),
allign = '---';
if (headings[i].hasAttribute('style')) {
var style = headings[i].getAttribute('style').toLowerCase().replace(/\s/g, '');
switch (style) {
case 'text-align:left;':
allign = ':---';
break;
case 'text-align:right;':
allign = '---:';
break;
case 'text-align:center;':
allign = ':---:';
break;
}
}
tableArray[0][i] = headContent.trim();
tableArray[1][i] = allign;
}
for (i = 0; i < rows.length; ++i) {
var r = tableArray.push([]) - 1,
cols = rows[i].getElementsByTagName('td');
for (ii = 0; ii < headings.length; ++ii) {
var cellContent = ' ';
if (typeof cols[ii] !== 'undefined') {
cellContent = showdown.subParser('makeMarkdown.tableCell')(cols[ii], globals);
}
tableArray[r].push(cellContent);
}
}
var cellSpacesCount = 3;
for (i = 0; i < tableArray.length; ++i) {
for (ii = 0; ii < tableArray[i].length; ++ii) {
var strLen = tableArray[i][ii].length;
if (strLen > cellSpacesCount) {
cellSpacesCount = strLen;
}
}
}
for (i = 0; i < tableArray.length; ++i) {
for (ii = 0; ii < tableArray[i].length; ++ii) {
if (i === 1) {
if (tableArray[i][ii].slice(-1) === ':') {
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii].slice(-1), cellSpacesCount - 1, '-') + ':';
} else {
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount, '-');
}
} else {
tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount);
}
}
txt += '| ' + tableArray[i].join(' | ') + ' |\n';
}
return txt.trim();
});

View File

@ -0,0 +1,15 @@
showdown.subParser('makeMarkdown.tableCell', function (node, globals) {
'use strict';
var txt = '';
if (!node.hasChildNodes()) {
return '';
}
var children = node.childNodes,
childrenLength = children.length;
for (var i = 0; i < childrenLength; ++i) {
txt += showdown.subParser('makeMarkdown.node')(children[i], globals, true);
}
return txt.trim();
});

View File

@ -0,0 +1,43 @@
showdown.subParser('makeMarkdown.txt', function (node) {
'use strict';
var txt = node.nodeValue;
// multiple spaces are collapsed
txt = txt.replace(/ +/g, ' ');
// replace the custom ¨NBSP; with a space
txt = txt.replace(/¨NBSP;/g, ' ');
// ", <, > and & should replace escaped html entities
txt = showdown.helper.unescapeHTMLEntities(txt);
// escape markdown magic characters
// emphasis, strong and strikethrough - can appear everywhere
// we also escape pipe (|) because of tables
// and escape ` because of code blocks and spans
txt = txt.replace(/([*_~|`])/g, '\\$1');
// escape > because of blockquotes
txt = txt.replace(/^(\s*)>/g, '\\$1>');
// hash character, only troublesome at the beginning of a line because of headers
txt = txt.replace(/^#/gm, '\\#');
// horizontal rules
txt = txt.replace(/^(\s*)([-=]{3,})(\s*)$/, '$1\\$2$3');
// dot, because of ordered lists, only troublesome at the beginning of a line when preceded by an integer
txt = txt.replace(/^( {0,3}\d+)\./gm, '$1\\.');
// +, * and -, at the beginning of a line becomes a list, so we need to escape them also (asterisk was already escaped)
txt = txt.replace(/^( {0,3})([+-])/gm, '$1\\$2');
// images and links, ] followed by ( is problematic, so we escape it
txt = txt.replace(/]([\s]*)\(/g, '\\]$1\\(');
// reference URIs must also be escaped
txt = txt.replace(/^ {0,3}\[([\S \t]*?)]:/gm, '\\[$1]:');
return txt;
});

31
test/bootstrap.js vendored
View File

@ -9,6 +9,8 @@
require('source-map-support').install();
require('chai').should();
var fs = require('fs');
var jsdom = require('jsdom');
var document = new jsdom.JSDOM('', {}).window.document; // jshint ignore:line
function getTestSuite (dir) {
return fs.readdirSync(dir)
@ -16,6 +18,12 @@
.map(map(dir));
}
function getHtmlToMdTestSuite (dir) {
return fs.readdirSync(dir)
.filter(filter())
.map(map2(dir));
}
function filter () {
return function (file) {
var ext = file.slice(-3);
@ -39,9 +47,27 @@
};
}
function assertion (testCase, converter) {
function map2 (dir) {
return function (file) {
var name = file.replace('.md', ''),
htmlPath = dir + name + '.html',
html = fs.readFileSync(htmlPath, 'utf8'),
mdPath = dir + name + '.md',
md = fs.readFileSync(mdPath, 'utf8');
return {
name: name,
input: html,
expected: md
};
};
}
function assertion (testCase, converter, type) {
return function () {
testCase.actual = converter.makeHtml(testCase.input);
//var conv = (type === 'makeMd') ? converter.makeMd : converter.makeHtml;
testCase.actual = (type === 'makeMd') ? converter.makeMd(testCase.input, document) : converter.makeHtml(testCase.input);
testCase = normalize(testCase);
// Compare
@ -81,6 +107,7 @@
module.exports = {
getTestSuite: getTestSuite,
getHtmlToMdTestSuite: getHtmlToMdTestSuite,
assertion: assertion,
normalize: normalize,
showdown: require('../.build/showdown.js')

View File

@ -0,0 +1,4 @@
<blockquote>some
multiline
blockquote
</blockquote>

View File

@ -0,0 +1 @@
> some multiline blockquote

View File

@ -0,0 +1,8 @@
<pre><code>some code
</code></pre>
<pre><code data-language="javascript">
function foo() {
return 'bar';
}
</code></pre>

9
test/makeMd/codeblock.md Normal file
View File

@ -0,0 +1,9 @@
```
some code
```
```javascript
function foo() {
return 'bar';
}
```

View File

@ -0,0 +1 @@
some <code>code</code> span

1
test/makeMd/codespan.md Normal file
View File

@ -0,0 +1 @@
some `code` span

View File

@ -0,0 +1 @@
<em>foobar</em>

1
test/makeMd/emphasis.md Normal file
View File

@ -0,0 +1 @@
*foobar*

6
test/makeMd/heading.html Normal file
View File

@ -0,0 +1,6 @@
<h1>foo h1</h1>
<h2>foo h2</h2>
<h3>foo h3</h3>
<h4>foo h4</h4>
<h5>foo h5</h5>
<h6>foo h6</h6>

11
test/makeMd/heading.md Normal file
View File

@ -0,0 +1,11 @@
# foo h1
## foo h2
### foo h3
#### foo h4
##### foo h5
###### foo h6

3
test/makeMd/hr.html Normal file
View File

@ -0,0 +1,3 @@
<hr />
<hr>

3
test/makeMd/hr.md Normal file
View File

@ -0,0 +1,3 @@
---
---

1
test/makeMd/image.html Normal file
View File

@ -0,0 +1 @@
<img src="img.png" alt="an image" width="200px" height="300px" title="a title"/>

1
test/makeMd/image.md Normal file
View File

@ -0,0 +1 @@
![an image](<img.png> =200pxx300px "a title")

1
test/makeMd/link.html Normal file
View File

@ -0,0 +1 @@
<a href="/url" title="a title">some link</a>

1
test/makeMd/link.md Normal file
View File

@ -0,0 +1 @@
[some link](</url> "a title")

15
test/makeMd/list.html Normal file
View File

@ -0,0 +1,15 @@
<ul>
<li>foo</li>
<li>bar</li>
<li>baz</li>
</ul>
<ul>
<li><p>foo</p></li>
<li><p>bar</p></li>
<li>baz</li>
</ul>
<ol>
<li>one</li>
<li>2</li>
<li>three</li>
</ol>

19
test/makeMd/list.md Normal file
View File

@ -0,0 +1,19 @@
- foo
- bar
- baz
<!-- -->
- foo
- bar
- baz
<!-- -->
1. one
2. 2
3. three
<!-- -->

View File

@ -0,0 +1,5 @@
<div>this is a div</div>
<label for="ipt">a label</label>
<input id="ipt" type="text">
<iframe></iframe>
<textarea>some textarea</textarea>

View File

@ -0,0 +1,9 @@
<div>this is a div</div>
<label for="ipt">a label</label>
<input id="ipt" type="text">
<iframe></iframe>
<textarea>some textarea</textarea>

View File

@ -0,0 +1,3 @@
<p>a paragraph
of multi-line
text</p>

1
test/makeMd/paragraph.md Normal file
View File

@ -0,0 +1 @@
a paragraph of multi-line text

View File

@ -0,0 +1 @@
<del>deleted text</del>

View File

@ -0,0 +1 @@
~~deleted text~~

1
test/makeMd/strong.html Normal file
View File

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

1
test/makeMd/strong.md Normal file
View File

@ -0,0 +1 @@
**strong text**

21
test/makeMd/table.html Normal file
View File

@ -0,0 +1,21 @@
<table>
<thead>
<tr>
<th>head 1</th>
<th>head 2</th>
<th>head 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>foo</td>
<td><em>bar</em></td>
<td>baz</td>
</tr>
<tr>
<td><del>a</del></td>
<td><strong>b</strong></td>
<td><code>cccc</code></td>
</tr>
</tbody>
</table>

4
test/makeMd/table.md Normal file
View File

@ -0,0 +1,4 @@
| head 1 | head 2 | head 3 |
| ------ | ------ | ------ |
| foo | *bar* | baz |
| ~~a~~ | **b** | `cccc` |

View File

@ -27,6 +27,27 @@ describe('showdown.Converter', function () {
});
});
describe('Converter.options extensions', function () {
var runCount;
showdown.extension('testext', function () {
return [{
type: 'output',
filter: function (text) {
runCount = runCount + 1;
return text;
}
}];
});
var converter = new showdown.Converter({extensions: ['testext']});
it('output extensions should run once', function () {
runCount = 0;
converter.makeHtml('# testext');
runCount.should.equal(1);
});
});
describe('metadata methods', function () {
var converter = new showdown.Converter();

View File

@ -10,27 +10,6 @@ describe('showdown.Converter', function () {
var showdown = require('../bootstrap').showdown;
describe('Converter.options extensions', function () {
var runCount;
showdown.extension('testext', function () {
return [{
type: 'output',
filter: function (text) {
runCount = runCount + 1;
return text;
}
}];
});
var converter = new showdown.Converter({extensions: ['testext']});
it('output extensions should run once', function () {
runCount = 0;
converter.makeHtml('# testext');
runCount.should.equal(1);
});
});
describe('makeHtml() with option omitExtraWLInCodeBlocks', function () {
var converter = new showdown.Converter({omitExtraWLInCodeBlocks: true}),
text = 'var foo = bar;',

View File

@ -0,0 +1,25 @@
/**
* Created by Estevao on 15-01-2015.
*/
describe('showdown.Converter', function () {
'use strict';
require('source-map-support').install();
require('chai').should();
var jsdom = require('jsdom');
var document = new jsdom.JSDOM('', {}).window.document; // jshint ignore:line
var showdown = require('../bootstrap').showdown;
describe('makeMarkdown()', function () {
var converter = new showdown.Converter();
it('should parse a simple html string', function () {
var html = '<a href="/somefoo.html">a link</a>\n';
var md = '[a link](</somefoo.html>)';
converter.makeMd(html, document).should.equal(md);
});
});
});

View File

@ -0,0 +1,12 @@
var bootstrap = require('../bootstrap.js'),
converter = new bootstrap.showdown.Converter(),
testsuite = bootstrap.getHtmlToMdTestSuite('test/makeMd/'),
assertion = bootstrap.assertion;
describe('makeMd() standard testsuite', function () {
'use strict';
for (var i = 0; i < testsuite.length; ++i) {
it(testsuite[i].name.replace(/-/g, ' '), assertion(testsuite[i], converter, 'makeMd'));
}
});