turndown-plugin-gfm.min.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. var turndownPluginGfm = (function (exports) {
  2. 'use strict';
  3. var highlightRegExp = /highlight-(?:text|source)-([a-z0-9]+)/;
  4. function highlightedCodeBlock (turndownService) {
  5. turndownService.addRule('highlightedCodeBlock', {
  6. filter: function (node) {
  7. var firstChild = node.firstChild;
  8. return (
  9. node.nodeName === 'DIV' &&
  10. highlightRegExp.test(node.className) &&
  11. firstChild &&
  12. firstChild.nodeName === 'PRE'
  13. )
  14. },
  15. replacement: function (content, node, options) {
  16. var className = node.className || '';
  17. var language = (className.match(highlightRegExp) || [null, ''])[1];
  18. return (
  19. '\n\n' + options.fence + language + '\n' +
  20. node.firstChild.textContent +
  21. '\n' + options.fence + '\n\n'
  22. )
  23. }
  24. });
  25. }
  26. function strikethrough (turndownService) {
  27. turndownService.addRule('strikethrough', {
  28. filter: ['del', 's', 'strike'],
  29. replacement: function (content) {
  30. return '~' + content + '~'
  31. }
  32. });
  33. }
  34. var indexOf = Array.prototype.indexOf;
  35. var every = Array.prototype.every;
  36. var rules = {};
  37. rules.tableCell = {
  38. filter: ['th', 'td'],
  39. replacement: function (content, node) {
  40. return cell(content, node)
  41. }
  42. };
  43. rules.tableRow = {
  44. filter: 'tr',
  45. replacement: function (content, node) {
  46. var borderCells = '';
  47. var alignMap = { left: ':--', right: '--:', center: ':-:' };
  48. if (isHeadingRow(node)) {
  49. for (var i = 0; i < node.childNodes.length; i++) {
  50. var border = '---';
  51. var align = (
  52. node.childNodes[i].getAttribute('align') || ''
  53. ).toLowerCase();
  54. if (align) border = alignMap[align] || border;
  55. borderCells += cell(border, node.childNodes[i]);
  56. }
  57. }
  58. return '\n' + content + (borderCells ? '\n' + borderCells : '')
  59. }
  60. };
  61. rules.table = {
  62. // Only convert tables with a heading row.
  63. // Tables with no heading row are kept using `keep` (see below).
  64. filter: function (node) {
  65. return node.nodeName === 'TABLE' && isHeadingRow(node.rows[0])
  66. },
  67. replacement: function (content) {
  68. // Ensure there are no blank lines
  69. content = content.replace('\n\n', '\n');
  70. return '\n\n' + content + '\n\n'
  71. }
  72. };
  73. rules.tableSection = {
  74. filter: ['thead', 'tbody', 'tfoot'],
  75. replacement: function (content) {
  76. return content
  77. }
  78. };
  79. // A tr is a heading row if:
  80. // - the parent is a THEAD
  81. // - or if its the first child of the TABLE or the first TBODY (possibly
  82. // following a blank THEAD)
  83. // - and every cell is a TH
  84. function isHeadingRow (tr) {
  85. var parentNode = tr.parentNode;
  86. return (
  87. parentNode.nodeName === 'THEAD' ||
  88. (
  89. parentNode.firstChild === tr &&
  90. (parentNode.nodeName === 'TABLE' || isFirstTbody(parentNode)) &&
  91. every.call(tr.childNodes, function (n) { return n.nodeName === 'TH' })
  92. )
  93. )
  94. }
  95. function isFirstTbody (element) {
  96. var previousSibling = element.previousSibling;
  97. return (
  98. element.nodeName === 'TBODY' && (
  99. !previousSibling ||
  100. (
  101. previousSibling.nodeName === 'THEAD' &&
  102. /^\s*$/i.test(previousSibling.textContent)
  103. )
  104. )
  105. )
  106. }
  107. function cell (content, node) {
  108. var index = indexOf.call(node.parentNode.childNodes, node);
  109. var prefix = ' ';
  110. if (index === 0) prefix = '| ';
  111. return prefix + content + ' |'
  112. }
  113. function tables (turndownService) {
  114. turndownService.keep(function (node) {
  115. return node.nodeName === 'TABLE' && !isHeadingRow(node.rows[0])
  116. });
  117. for (var key in rules) turndownService.addRule(key, rules[key]);
  118. }
  119. function taskListItems (turndownService) {
  120. turndownService.addRule('taskListItems', {
  121. filter: function (node) {
  122. return node.type === 'checkbox' && node.parentNode.nodeName === 'LI'
  123. },
  124. replacement: function (content, node) {
  125. return (node.checked ? '[x]' : '[ ]') + ' '
  126. }
  127. });
  128. }
  129. function gfm (turndownService) {
  130. turndownService.use([
  131. highlightedCodeBlock,
  132. strikethrough,
  133. tables,
  134. taskListItems
  135. ]);
  136. }
  137. exports.gfm = gfm;
  138. exports.highlightedCodeBlock = highlightedCodeBlock;
  139. exports.strikethrough = strikethrough;
  140. exports.tables = tables;
  141. exports.taskListItems = taskListItems;
  142. return exports;
  143. }({}));