GrowiRenderer.js 3.0 KB

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