mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
feat(tasklists): add support for GFM tasklists
Github Flavored Markdown supports tasklist by `[x]` or `[ ]` after list item marker. This commit adds this feature to showdown through an option called "tasklists". Related to #164
This commit is contained in:
parent
c33f98884b
commit
dc72403acc
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
npm-debug.log
|
||||
localtest.html
|
||||
|
|
BIN
dist/showdown.js
vendored
BIN
dist/showdown.js
vendored
Binary file not shown.
BIN
dist/showdown.js.map
vendored
BIN
dist/showdown.js.map
vendored
Binary file not shown.
BIN
dist/showdown.min.js
vendored
BIN
dist/showdown.min.js
vendored
Binary file not shown.
BIN
dist/showdown.min.js.map
vendored
BIN
dist/showdown.min.js.map
vendored
Binary file not shown.
|
@ -17,7 +17,8 @@ var showdown = {},
|
|||
strikethrough: false,
|
||||
tables: false,
|
||||
tablesHeaderId: false,
|
||||
ghCodeBlocks: true // true due to historical reasons
|
||||
ghCodeBlocks: true, // true due to historical reasons
|
||||
tasklists: false
|
||||
},
|
||||
globalOptions = JSON.parse(JSON.stringify(defaultOptions)); //clone default options out of laziness =P
|
||||
|
||||
|
|
|
@ -42,25 +42,28 @@ showdown.subParser('lists', function (text, options, globals) {
|
|||
// attacklab: add sentinel to emulate \z
|
||||
listStr += '~0';
|
||||
|
||||
/*
|
||||
list_str = list_str.replace(/
|
||||
(\n)? // leading line = $1
|
||||
(^[ \t]*) // leading whitespace = $2
|
||||
([*+-]|\d+[.]) [ \t]+ // list marker = $3
|
||||
([^\r]+? // list item text = $4
|
||||
(\n{1,2}))
|
||||
(?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
|
||||
/gm, function(){...});
|
||||
*/
|
||||
var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm;
|
||||
var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm;
|
||||
|
||||
listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
|
||||
checked = (checked && checked.trim() !== '');
|
||||
|
||||
listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4) {
|
||||
var item = showdown.subParser('outdent')(m4, options, globals);
|
||||
//m1 - LeadingLine
|
||||
|
||||
//m1 - LeadingLine
|
||||
if (m1 || (item.search(/\n{2,}/) > -1)) {
|
||||
item = showdown.subParser('blockGamut')(item, options, globals);
|
||||
} else {
|
||||
if (taskbtn && options.tasklists) {
|
||||
item = item.replace(taskbtn, function () {
|
||||
var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
|
||||
if (checked) {
|
||||
otp += ' checked';
|
||||
}
|
||||
otp += '>';
|
||||
return otp;
|
||||
});
|
||||
}
|
||||
|
||||
// Recursion for sub-lists:
|
||||
item = showdown.subParser('lists')(item, options, globals);
|
||||
item = item.replace(/\n$/, ''); // chomp(item)
|
||||
|
@ -69,8 +72,14 @@ showdown.subParser('lists', function (text, options, globals) {
|
|||
|
||||
// this is a "hack" to differentiate between ordered and unordered lists
|
||||
// related to issue #142
|
||||
var tp = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
||||
return spl + tp + '<li>' + item + '</li>\n';
|
||||
var tp = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol',
|
||||
bulletStyle = '';
|
||||
|
||||
if (taskbtn) {
|
||||
bulletStyle = ' style="list-style-type: none;"';
|
||||
}
|
||||
|
||||
return spl + tp + '<li' + bulletStyle + '>' + item + '</li>\n';
|
||||
});
|
||||
|
||||
// attacklab: strip sentinel
|
||||
|
@ -87,6 +96,8 @@ showdown.subParser('lists', function (text, options, globals) {
|
|||
* @returns {string|*}
|
||||
*/
|
||||
function splitConsecutiveLists (results, listType) {
|
||||
// parsing html with regex...
|
||||
// This will surely fail if some extension decides to change paragraph markup directly
|
||||
var cthulhu = /(<p[^>]+?>|<p>|<\/p>)/img,
|
||||
holder = [[]],
|
||||
res = '',
|
||||
|
@ -125,28 +136,6 @@ showdown.subParser('lists', function (text, options, globals) {
|
|||
text += '~0';
|
||||
|
||||
// Re-usable pattern to match any entire ul or ol list:
|
||||
|
||||
/*
|
||||
var whole_list = /
|
||||
( // $1 = whole list
|
||||
( // $2
|
||||
[ ]{0,3} // attacklab: g_tab_width - 1
|
||||
([*+-]|\d+[.]) // $3 = first list item marker
|
||||
[ \t]+
|
||||
)
|
||||
[^\r]+?
|
||||
( // $4
|
||||
~0 // sentinel for workaround; should be $
|
||||
|
|
||||
\n{2,}
|
||||
(?=\S)
|
||||
(?! // Negative lookahead for another list item marker
|
||||
[ \t]*
|
||||
(?:[*+-]|\d+[.])[ \t]+
|
||||
)
|
||||
)
|
||||
)/g
|
||||
*/
|
||||
var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
|
||||
|
||||
if (globals.gListLevel) {
|
||||
|
|
10
test/features/#164.4.tasklists.html
Normal file
10
test/features/#164.4.tasklists.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<h1 id="mythings">my things</h1>
|
||||
|
||||
<ul>
|
||||
<li>foo</li>
|
||||
<li style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"> bar</li>
|
||||
<li style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"> baz</li>
|
||||
<li style="list-style-type: none;"><input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;" checked> bazinga</li>
|
||||
</ul>
|
||||
|
||||
<p>otherthings</p>
|
8
test/features/#164.4.tasklists.md
Normal file
8
test/features/#164.4.tasklists.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# my things
|
||||
|
||||
- foo
|
||||
- [] bar
|
||||
- [ ] baz
|
||||
- [x] bazinga
|
||||
|
||||
otherthings
|
|
@ -28,7 +28,8 @@ describe('showdown.options', function () {
|
|||
strikethrough: false,
|
||||
tables: false,
|
||||
tablesHeaderId: false,
|
||||
ghCodeBlocks: true
|
||||
ghCodeBlocks: true,
|
||||
tasklists: false
|
||||
};
|
||||
expect(showdown.getDefaultOptions()).to.be.eql(opts);
|
||||
});
|
||||
|
|
|
@ -21,8 +21,10 @@ describe('makeHtml() features testsuite', function () {
|
|||
converter = new showdown.Converter({literalMidWordUnderscores: true});
|
||||
} else if (testsuite[i].name === '#164.3.strikethrough') {
|
||||
converter = new showdown.Converter({strikethrough: true});
|
||||
} else if (testsuite[i].name === 'disable_gh_codeblocks') {
|
||||
} else if (testsuite[i].name === 'disable_gh_codeblocks') {
|
||||
converter = new showdown.Converter({ghCodeBlocks: false});
|
||||
} else if (testsuite[i].name === '#164.4.tasklists') {
|
||||
converter = new showdown.Converter({tasklists: true});
|
||||
} else {
|
||||
converter = new showdown.Converter();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user