lists.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /**
  2. * Created by Estevao on 12-01-2015.
  3. */
  4. /**
  5. * Form HTML ordered (numbered) and unordered (bulleted) lists.
  6. */
  7. showdown.subParser('lists', function (text, options, globals) {
  8. 'use strict';
  9. /**
  10. * Process the contents of a single ordered or unordered list, splitting it
  11. * into individual list items.
  12. * @param listStr
  13. * @returns {string|*}
  14. */
  15. var processListItems = function (listStr) {
  16. // The $g_list_level global keeps track of when we're inside a list.
  17. // Each time we enter a list, we increment it; when we leave a list,
  18. // we decrement. If it's zero, we're not in a list anymore.
  19. //
  20. // We do this because when we're not inside a list, we want to treat
  21. // something like this:
  22. //
  23. // I recommend upgrading to version
  24. // 8. Oops, now this line is treated
  25. // as a sub-list.
  26. //
  27. // As a single paragraph, despite the fact that the second line starts
  28. // with a digit-period-space sequence.
  29. //
  30. // Whereas when we're inside a list (or sub-list), that line will be
  31. // treated as the start of a sub-list. What a kludge, huh? This is
  32. // an aspect of Markdown's syntax that's hard to parse perfectly
  33. // without resorting to mind-reading. Perhaps the solution is to
  34. // change the syntax rules such that sub-lists must start with a
  35. // starting cardinal number; e.g. "1." or "a.".
  36. globals.gListLevel++;
  37. // trim trailing blank lines:
  38. listStr = listStr.replace(/\n{2,}$/, '\n');
  39. // attacklab: add sentinel to emulate \z
  40. listStr += '~0';
  41. /*
  42. list_str = list_str.replace(/
  43. (\n)? // leading line = $1
  44. (^[ \t]*) // leading whitespace = $2
  45. ([*+-]|\d+[.]) [ \t]+ // list marker = $3
  46. ([^\r]+? // list item text = $4
  47. (\n{1,2}))
  48. (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
  49. /gm, function(){...});
  50. */
  51. listStr = listStr.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
  52. function (wholeMatch, m1, m2, m3, m4) {
  53. var item = showdown.subParser('outdent')(m4, options, globals);
  54. //m1 - LeadingLine
  55. if (m1 || (item.search(/\n{2,}/) > -1)) {
  56. item = showdown.subParser('blockGamut')(item, options, globals);
  57. } else {
  58. // Recursion for sub-lists:
  59. item = showdown.subParser('lists')(item, options, globals);
  60. item = item.replace(/\n$/, ''); // chomp(item)
  61. item = showdown.subParser('spanGamut')(item, options, globals);
  62. }
  63. return '<li>' + item + '</li>\n';
  64. });
  65. // attacklab: strip sentinel
  66. listStr = listStr.replace(/~0/g, '');
  67. globals.gListLevel--;
  68. return listStr;
  69. };
  70. // attacklab: add sentinel to hack around khtml/safari bug:
  71. // http://bugs.webkit.org/show_bug.cgi?id=11231
  72. text += '~0';
  73. // Re-usable pattern to match any entirel ul or ol list:
  74. /*
  75. var whole_list = /
  76. ( // $1 = whole list
  77. ( // $2
  78. [ ]{0,3} // attacklab: g_tab_width - 1
  79. ([*+-]|\d+[.]) // $3 = first list item marker
  80. [ \t]+
  81. )
  82. [^\r]+?
  83. ( // $4
  84. ~0 // sentinel for workaround; should be $
  85. |
  86. \n{2,}
  87. (?=\S)
  88. (?! // Negative lookahead for another list item marker
  89. [ \t]*
  90. (?:[*+-]|\d+[.])[ \t]+
  91. )
  92. )
  93. )/g
  94. */
  95. var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
  96. if (globals.gListLevel) {
  97. text = text.replace(wholeList, function (wholeMatch, m1, m2) {
  98. var list = m1,
  99. listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
  100. // Turn double returns into triple returns, so that we can make a
  101. // paragraph for the last item in a list, if necessary:
  102. list = list.replace(/\n{2,}/g, '\n\n\n');
  103. var result = processListItems(list);
  104. // Trim any trailing whitespace, to put the closing `</$list_type>`
  105. // up on the preceding line, to get it past the current stupid
  106. // HTML block parser. This is a hack to work around the terrible
  107. // hack that is the HTML block parser.
  108. result = result.replace(/\s+$/, '');
  109. result = '<' + listType + '>' + result + '</' + listType + '>\n';
  110. return result;
  111. });
  112. } else {
  113. wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
  114. text = text.replace(wholeList, function (wholeMatch, m1, m2, m3) {
  115. // Turn double returns into triple returns, so that we can make a
  116. // paragraph for the last item in a list, if necessary:
  117. var list = m2.replace(/\n{2,}/g, '\n\n\n'),
  118. listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol',
  119. result = processListItems(list);
  120. return m1 + '<' + listType + '>\n' + result + '</' + listType + '>\n';
  121. });
  122. }
  123. // attacklab: strip sentinel
  124. text = text.replace(/~0/, '');
  125. return text;
  126. });