GrowiRenderer.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import MarkdownIt from 'markdown-it';
  2. import * as entities from 'entities';
  3. import Linker from './PreProcessor/Linker';
  4. import CsvToTable from './PreProcessor/CsvToTable';
  5. import XssFilter from './PreProcessor/XssFilter';
  6. import Template from './LangProcessor/Template';
  7. import EmojiConfigurer from './markdown-it/emoji';
  8. import MathJaxConfigurer from './markdown-it/mathjax';
  9. import PlantUMLConfigurer from './markdown-it/plantuml';
  10. export default class GrowiRenderer {
  11. constructor(crowi) {
  12. this.crowi = crowi;
  13. this.preProcessors = [
  14. new Linker(crowi),
  15. new CsvToTable(crowi),
  16. new XssFilter(crowi),
  17. ];
  18. this.postProcessors = [
  19. ];
  20. this.markdownItConfigurers = [
  21. new EmojiConfigurer(crowi),
  22. new MathJaxConfigurer(crowi),
  23. new PlantUMLConfigurer(crowi),
  24. ];
  25. this.langProcessors = {
  26. 'template': new Template(crowi),
  27. };
  28. this.configure = this.configure.bind(this);
  29. this.configurePlugins = this.configurePlugins.bind(this);
  30. this.parseMarkdown = this.parseMarkdown.bind(this);
  31. this.codeRenderer = this.codeRenderer.bind(this);
  32. this.md = new MarkdownIt();
  33. this.configure(this.crowi.getConfig());
  34. this.configurePlugins(this.crowi.getConfig());
  35. }
  36. /**
  37. * configure markdown-it
  38. * @param {any} config
  39. */
  40. configure(config) {
  41. this.md.set({
  42. html: true,
  43. linkify: true,
  44. breaks: config.isEnabledLineBreaks,
  45. highlight: this.codeRenderer,
  46. });
  47. }
  48. /**
  49. * configure markdown-it plugins
  50. * @param {any} config
  51. */
  52. configurePlugins(config) {
  53. this.markdownItConfigurers.forEach((configurer) => {
  54. configurer.configure(this.md);
  55. });
  56. }
  57. preProcess(markdown) {
  58. for (let i = 0; i < this.preProcessors.length; i++) {
  59. if (!this.preProcessors[i].process) {
  60. continue;
  61. }
  62. markdown = this.preProcessors[i].process(markdown);
  63. }
  64. return markdown;
  65. }
  66. postProcess(html, dom) {
  67. for (let i = 0; i < this.postProcessors.length; i++) {
  68. if (!this.postProcessors[i].process) {
  69. continue;
  70. }
  71. html = this.postProcessors[i].process(html, dom);
  72. }
  73. return html;
  74. }
  75. codeRenderer(code, langExt) {
  76. if (langExt) {
  77. const langAndFn = langExt.split(':');
  78. const lang = langAndFn[0];
  79. const langFn = langAndFn[1] || null;
  80. // process langProcessors
  81. if (this.langProcessors[lang] != null) {
  82. return this.langProcessors[lang].process(code, langExt);
  83. }
  84. if (hljs.getLanguage(lang)) {
  85. let citeTag = (langFn) ? `<cite>${langFn}</cite>` : '';
  86. try {
  87. return `<pre class="hljs">${citeTag}<code class="language-${lang}">${hljs.highlight(lang, code, true).value}</code></pre>`;
  88. } catch (__) {}
  89. }
  90. }
  91. return '';
  92. }
  93. parseMarkdown(markdown, dom, markedOpts) {
  94. let parsed = '';
  95. parsed = this.md.render(markdown);
  96. return parsed;
  97. }
  98. /**
  99. * render
  100. *
  101. * @param {string} markdown
  102. * @param {Element} dom
  103. * @returns
  104. *
  105. * @memberOf CrowiRenderer
  106. */
  107. render(markdown, dom) {
  108. let html = '';
  109. html = this.parseMarkdown(markdown, dom);
  110. html = this.postProcess(html, dom);
  111. return html;
  112. }
  113. }