2015-01-16 05:21:33 +08:00
|
|
|
/**
|
|
|
|
* Form HTML ordered (numbered) and unordered (bulleted) lists.
|
|
|
|
*/
|
|
|
|
showdown.subParser('lists', function (text, options, globals) {
|
2015-01-19 19:37:21 +08:00
|
|
|
'use strict';
|
|
|
|
|
2016-03-21 01:08:44 +08:00
|
|
|
text = globals.converter._dispatch('lists.before', text, options, globals);
|
2015-01-19 19:37:21 +08:00
|
|
|
/**
|
|
|
|
* Process the contents of a single ordered or unordered list, splitting it
|
|
|
|
* into individual list items.
|
2015-01-19 22:57:43 +08:00
|
|
|
* @param {string} listStr
|
2015-08-23 09:46:04 +08:00
|
|
|
* @param {boolean} trimTrailing
|
2015-06-13 21:59:14 +08:00
|
|
|
* @returns {string}
|
2015-01-19 19:37:21 +08:00
|
|
|
*/
|
2015-08-02 04:05:28 +08:00
|
|
|
function processListItems (listStr, trimTrailing) {
|
2015-01-19 19:37:21 +08:00
|
|
|
// 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';
|
2015-01-16 05:21:33 +08:00
|
|
|
|
2016-04-26 22:08:50 +08:00
|
|
|
var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
|
2015-08-02 04:05:28 +08:00
|
|
|
isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr));
|
2015-07-12 06:02:02 +08:00
|
|
|
|
|
|
|
listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
|
|
|
|
checked = (checked && checked.trim() !== '');
|
2015-08-02 04:05:28 +08:00
|
|
|
var item = showdown.subParser('outdent')(m4, options, globals),
|
|
|
|
bulletStyle = '';
|
2015-07-12 06:02:02 +08:00
|
|
|
|
2015-08-02 04:05:28 +08:00
|
|
|
// Support for github tasklists
|
|
|
|
if (taskbtn && options.tasklists) {
|
|
|
|
bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
|
2016-04-26 22:08:50 +08:00
|
|
|
item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () {
|
2015-08-02 04:05:28 +08:00
|
|
|
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;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// m1 - Leading line or
|
|
|
|
// Has a double return (multi paragraph) or
|
|
|
|
// Has sublist
|
2015-06-13 21:59:14 +08:00
|
|
|
if (m1 || (item.search(/\n{2,}/) > -1)) {
|
2015-08-02 04:05:28 +08:00
|
|
|
item = showdown.subParser('githubCodeBlocks')(item, options, globals);
|
2015-06-13 21:59:14 +08:00
|
|
|
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)
|
2015-08-02 04:05:28 +08:00
|
|
|
if (isParagraphed) {
|
|
|
|
item = showdown.subParser('paragraphs')(item, options, globals);
|
|
|
|
} else {
|
|
|
|
item = showdown.subParser('spanGamut')(item, options, globals);
|
|
|
|
}
|
2015-07-12 06:02:02 +08:00
|
|
|
}
|
2015-08-02 04:05:28 +08:00
|
|
|
item = '\n<li' + bulletStyle + '>' + item + '</li>\n';
|
|
|
|
return item;
|
2015-06-13 21:59:14 +08:00
|
|
|
});
|
2015-01-16 05:21:33 +08:00
|
|
|
|
|
|
|
// attacklab: strip sentinel
|
2015-01-19 19:37:21 +08:00
|
|
|
listStr = listStr.replace(/~0/g, '');
|
|
|
|
|
|
|
|
globals.gListLevel--;
|
2015-08-02 04:05:28 +08:00
|
|
|
|
|
|
|
if (trimTrailing) {
|
|
|
|
listStr = listStr.replace(/\s+$/, '');
|
|
|
|
}
|
|
|
|
|
2015-01-19 19:37:21 +08:00
|
|
|
return listStr;
|
2015-06-13 21:59:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-08-02 04:05:28 +08:00
|
|
|
* Check and parse consecutive lists (better fix for issue #142)
|
|
|
|
* @param {string} list
|
2015-06-13 21:59:14 +08:00
|
|
|
* @param {string} listType
|
2015-08-23 09:46:04 +08:00
|
|
|
* @param {boolean} trimTrailing
|
2015-08-02 04:05:28 +08:00
|
|
|
* @returns {string}
|
2015-06-13 21:59:14 +08:00
|
|
|
*/
|
2015-08-02 04:05:28 +08:00
|
|
|
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 UL and vice versa
|
|
|
|
var counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm,
|
|
|
|
subLists = [],
|
|
|
|
result = '';
|
|
|
|
|
|
|
|
if (list.search(counterRxg) !== -1) {
|
|
|
|
(function parseCL(txt) {
|
|
|
|
var pos = txt.search(counterRxg);
|
|
|
|
if (pos !== -1) {
|
|
|
|
// slice
|
|
|
|
result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n\n';
|
|
|
|
|
|
|
|
// invert counterType and listType
|
|
|
|
listType = (listType === 'ul') ? 'ol' : 'ul';
|
|
|
|
counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
|
|
|
|
|
|
|
|
//recurse
|
|
|
|
parseCL(txt.slice(pos));
|
|
|
|
} else {
|
|
|
|
result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n\n';
|
2015-06-13 21:59:14 +08:00
|
|
|
}
|
2015-08-02 04:05:28 +08:00
|
|
|
})(list);
|
|
|
|
for (var i = 0; i < subLists.length; ++i) {
|
|
|
|
|
2015-06-13 21:59:14 +08:00
|
|
|
}
|
2015-08-02 04:05:28 +08:00
|
|
|
} else {
|
|
|
|
result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n\n';
|
2015-06-13 21:59:14 +08:00
|
|
|
}
|
2015-08-02 04:05:28 +08:00
|
|
|
|
|
|
|
return result;
|
2015-06-13 21:59:14 +08:00
|
|
|
}
|
2015-01-19 19:37:21 +08:00
|
|
|
|
|
|
|
// attacklab: add sentinel to hack around khtml/safari bug:
|
|
|
|
// http://bugs.webkit.org/show_bug.cgi?id=11231
|
|
|
|
text += '~0';
|
|
|
|
|
2015-06-13 21:59:14 +08:00
|
|
|
// Re-usable pattern to match any entire ul or ol list:
|
2015-01-19 19:37:21 +08:00
|
|
|
var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
|
|
|
|
|
|
|
|
if (globals.gListLevel) {
|
2015-08-02 04:05:28 +08:00
|
|
|
text = text.replace(wholeList, function (wholeMatch, list, m2) {
|
|
|
|
var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
|
|
|
return parseConsecutiveLists(list, listType, true);
|
2015-01-19 19:37:21 +08:00
|
|
|
});
|
|
|
|
} else {
|
2015-08-02 04:05:28 +08:00
|
|
|
wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
|
2015-06-13 21:59:14 +08:00
|
|
|
//wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g;
|
2015-08-02 04:05:28 +08:00
|
|
|
text = text.replace(wholeList, function (wholeMatch, m1, list, m3) {
|
2015-01-19 19:37:21 +08:00
|
|
|
|
2015-08-02 04:05:28 +08:00
|
|
|
var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
|
|
|
|
return parseConsecutiveLists(list, listType);
|
2015-01-19 19:37:21 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// attacklab: strip sentinel
|
|
|
|
text = text.replace(/~0/, '');
|
|
|
|
|
2016-03-21 01:08:44 +08:00
|
|
|
text = globals.converter._dispatch('lists.after', text, options, globals);
|
2015-01-19 19:37:21 +08:00
|
|
|
return text;
|
2015-01-16 05:21:33 +08:00
|
|
|
});
|