mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
b1c458a762
The option simpleLineBreaks was not working with non-ASCII characters such as chinese characters and when lines started or ended with markdown delimiters such as `*` or `~` Closes #318, #323
192 lines
7.1 KiB
JavaScript
192 lines
7.1 KiB
JavaScript
/**
|
|
* Form HTML ordered (numbered) and unordered (bulleted) lists.
|
|
*/
|
|
showdown.subParser('lists', function (text, options, globals) {
|
|
'use strict';
|
|
text = globals.converter._dispatch('lists.before', text, options, globals);
|
|
|
|
/**
|
|
* Process the contents of a single ordered or unordered list, splitting it
|
|
* into individual list items.
|
|
* @param {string} listStr
|
|
* @param {boolean} trimTrailing
|
|
* @returns {string}
|
|
*/
|
|
function processListItems (listStr, trimTrailing) {
|
|
// The $g_list_level global keeps track of when we're inside a list.
|
|
// Each time we enter a list, we increment it; when we leave a list,
|
|
// we decrement. If it's zero, we're not in a list anymore.
|
|
//
|
|
// We do this because when we're not inside a list, we want to treat
|
|
// something like this:
|
|
//
|
|
// I recommend upgrading to version
|
|
// 8. Oops, now this line is treated
|
|
// as a sub-list.
|
|
//
|
|
// As a single paragraph, despite the fact that the second line starts
|
|
// with a digit-period-space sequence.
|
|
//
|
|
// Whereas when we're inside a list (or sub-list), that line will be
|
|
// treated as the start of a sub-list. What a kludge, huh? This is
|
|
// an aspect of Markdown's syntax that's hard to parse perfectly
|
|
// without resorting to mind-reading. Perhaps the solution is to
|
|
// change the syntax rules such that sub-lists must start with a
|
|
// starting cardinal number; e.g. "1." or "a.".
|
|
globals.gListLevel++;
|
|
|
|
// trim trailing blank lines:
|
|
listStr = listStr.replace(/\n{2,}$/, '\n');
|
|
|
|
// attacklab: add sentinel to emulate \z
|
|
listStr += '~0';
|
|
|
|
var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
|
|
isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr));
|
|
|
|
// Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
|
|
// which is a syntax breaking change
|
|
// activating this option reverts to old behavior
|
|
if (options.disableForced4SpacesIndentedSublists) {
|
|
rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|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() !== '');
|
|
|
|
var item = showdown.subParser('outdent')(m4, options, globals),
|
|
bulletStyle = '';
|
|
|
|
// Support for github tasklists
|
|
if (taskbtn && options.tasklists) {
|
|
bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
|
|
item = item.replace(/^[ \t]*\[(x|X| )?]/m, 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;
|
|
});
|
|
}
|
|
|
|
// ISSUE #312
|
|
// This input: - - - a
|
|
// causes trouble to the parser, since it interprets it as:
|
|
// <ul><li><li><li>a</li></li></li></ul>
|
|
// instead of:
|
|
// <ul><li>- - a</li></ul>
|
|
// So, to prevent it, we will put a marker (~A)in the beginning of the line
|
|
// Kind of hackish/monkey patching, but seems more effective than overcomplicating the list parser
|
|
item = item.replace(/^([-*+]|\d\.)[ \t]+[\S\n ]*/g, function (wm2) {
|
|
return '~A' + wm2;
|
|
});
|
|
|
|
// m1 - Leading line or
|
|
// Has a double return (multi paragraph) or
|
|
// Has sublist
|
|
if (m1 || (item.search(/\n{2,}/) > -1)) {
|
|
item = showdown.subParser('githubCodeBlocks')(item, options, globals);
|
|
item = showdown.subParser('blockGamut')(item, options, globals);
|
|
} else {
|
|
// Recursion for sub-lists:
|
|
item = showdown.subParser('lists')(item, options, globals);
|
|
item = item.replace(/\n$/, ''); // chomp(item)
|
|
item = showdown.subParser('hashHTMLBlocks')(item, options, globals);
|
|
// Colapse double linebreaks
|
|
item = item.replace(/\n\n+/g, '\n\n');
|
|
// replace double linebreaks with a placeholder
|
|
item = item.replace(/\n\n/g, '~B');
|
|
if (isParagraphed) {
|
|
item = showdown.subParser('paragraphs')(item, options, globals);
|
|
} else {
|
|
item = showdown.subParser('spanGamut')(item, options, globals);
|
|
}
|
|
item = item.replace(/~B/g, '\n\n');
|
|
}
|
|
|
|
// now we need to remove the marker (~A)
|
|
item = item.replace('~A', '');
|
|
// we can finally wrap the line in list item tags
|
|
item = '<li' + bulletStyle + '>' + item + '</li>\n';
|
|
|
|
return item;
|
|
});
|
|
|
|
// attacklab: strip sentinel
|
|
listStr = listStr.replace(/~0/g, '');
|
|
|
|
globals.gListLevel--;
|
|
|
|
if (trimTrailing) {
|
|
listStr = listStr.replace(/\s+$/, '');
|
|
}
|
|
|
|
return listStr;
|
|
}
|
|
|
|
/**
|
|
* Check and parse consecutive lists (better fix for issue #142)
|
|
* @param {string} list
|
|
* @param {string} listType
|
|
* @param {boolean} trimTrailing
|
|
* @returns {string}
|
|
*/
|
|
function parseConsecutiveLists(list, listType, trimTrailing) {
|
|
// check if we caught 2 or more consecutive lists by mistake
|
|
// we use the counterRgx, meaning if listType is UL we look for OL and vice versa
|
|
var olRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?\d+\.[ \t]/gm : /^ {0,3}\d+\.[ \t]/gm,
|
|
ulRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?[*+-][ \t]/gm : /^ {0,3}[*+-][ \t]/gm,
|
|
counterRxg = (listType === 'ul') ? olRgx : ulRgx,
|
|
result = '';
|
|
|
|
if (list.search(counterRxg) !== -1) {
|
|
(function parseCL(txt) {
|
|
var pos = txt.search(counterRxg);
|
|
if (pos !== -1) {
|
|
// slice
|
|
result += '\n<' + listType + '>\n' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n';
|
|
|
|
// invert counterType and listType
|
|
listType = (listType === 'ul') ? 'ol' : 'ul';
|
|
counterRxg = (listType === 'ul') ? olRgx : ulRgx;
|
|
|
|
//recurse
|
|
parseCL(txt.slice(pos));
|
|
} else {
|
|
result += '\n<' + listType + '>\n' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n';
|
|
}
|
|
})(list);
|
|
} else {
|
|
result = '\n<' + listType + '>\n' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n';
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// add sentinel to hack around khtml/safari bug:
|
|
// http://bugs.webkit.org/show_bug.cgi?id=11231
|
|
text += '~0';
|
|
|
|
if (globals.gListLevel) {
|
|
text = text.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
|
|
function (wholeMatch, list, m2) {
|
|
var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
|
return parseConsecutiveLists(list, listType, true);
|
|
}
|
|
);
|
|
} else {
|
|
text = text.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
|
|
function (wholeMatch, m1, list, m3) {
|
|
var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
|
return parseConsecutiveLists(list, listType, false);
|
|
}
|
|
);
|
|
}
|
|
|
|
// strip sentinel
|
|
text = text.replace(/~0/, '');
|
|
text = globals.converter._dispatch('lists.after', text, options, globals);
|
|
return text;
|
|
});
|