GrowiRenderer.js 3.4 KB

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