headers.js 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. showdown.subParser('headers', function (text, options, globals) {
  2. 'use strict';
  3. text = globals.converter._dispatch('headers.before', text, options, globals);
  4. var prefixHeader = options.prefixHeaderId,
  5. headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
  6. ghHeaderId = options.ghCompatibleHeaderId,
  7. // Set text-style headers:
  8. // Header 1
  9. // ========
  10. //
  11. // Header 2
  12. // --------
  13. //
  14. setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm,
  15. setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
  16. text = text.replace(setextRegexH1, function (wholeMatch, m1) {
  17. var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
  18. hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
  19. hLevel = headerLevelStart,
  20. hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
  21. return showdown.subParser('hashBlock')(hashBlock, options, globals);
  22. });
  23. text = text.replace(setextRegexH2, function (matchFound, m1) {
  24. var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
  25. hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
  26. hLevel = headerLevelStart + 1,
  27. hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
  28. return showdown.subParser('hashBlock')(hashBlock, options, globals);
  29. });
  30. // atx-style headers:
  31. // # Header 1
  32. // ## Header 2
  33. // ## Header 2 with closing hashes ##
  34. // ...
  35. // ###### Header 6
  36. //
  37. var atxStyle = (options.requireSpaceBeforeHeadingText) ? /^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm : /^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;
  38. text = text.replace(atxStyle, function (wholeMatch, m1, m2) {
  39. var span = showdown.subParser('spanGamut')(m2, options, globals),
  40. hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
  41. hLevel = headerLevelStart - 1 + m1.length,
  42. header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
  43. return showdown.subParser('hashBlock')(header, options, globals);
  44. });
  45. function headerId(m) {
  46. var title, escapedId;
  47. if (ghHeaderId) {
  48. escapedId = m
  49. .replace(/ /g, '-')
  50. //replace previously escaped chars (&, ~ and $)
  51. .replace(/&amp;/g, '')
  52. .replace(/~T/g, '')
  53. .replace(/~D/g, '')
  54. //replace rest of the chars (&~$ are repeated as they might have been escaped)
  55. .replace(/[&~$!@#*()=:/,;?+'.%\\]/g, '')
  56. .toLowerCase();
  57. } else {
  58. escapedId = m.replace(/[^\w]/g, '').toLowerCase();
  59. }
  60. if (globals.hashLinkCounts[escapedId]) {
  61. title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++);
  62. } else {
  63. title = escapedId;
  64. globals.hashLinkCounts[escapedId] = 1;
  65. }
  66. // Prefix id to prevent causing inadvertent pre-existing style matches.
  67. if (prefixHeader === true) {
  68. prefixHeader = 'section';
  69. }
  70. if (showdown.helper.isString(prefixHeader)) {
  71. return prefixHeader + title;
  72. }
  73. return title;
  74. }
  75. text = globals.converter._dispatch('headers.after', text, options, globals);
  76. return text;
  77. });