renderer.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * Original marked.js library renderer with fixes:
  7. * - No formatting for output HTML string — all newlines between tags are removed to create clean output.
  8. * - Changed long string concatenations to ES5 template strings.
  9. * - Changed code style.
  10. *
  11. * @see {@link https://github.com/chjj/marked#renderer} Methods description.
  12. * @param options
  13. * @constructor
  14. */
  15. function Renderer( options ) {
  16. this.options = options || {};
  17. }
  18. Renderer.prototype.code = function( code, lang, escaped ) {
  19. if ( this.options.highlight ) {
  20. const out = this.options.highlight( code, lang );
  21. if ( out !== null && out !== code ) {
  22. escaped = true;
  23. code = out;
  24. }
  25. }
  26. if ( !lang ) {
  27. return `<pre><code>${ escaped ? code : escape( code, true ) }</code></pre>`;
  28. }
  29. const cssClass = this.options.langPrefix + escape( lang, true );
  30. return `<pre><code class="${ cssClass }">${ escaped ? code : escape( code, true ) }</code></pre>`;
  31. };
  32. Renderer.prototype.blockquote = function( quote ) {
  33. return `<blockquote>${ quote }</blockquote>`;
  34. };
  35. Renderer.prototype.html = function( html ) {
  36. return html;
  37. };
  38. Renderer.prototype.heading = function( text, level, raw ) {
  39. return `<h${ level }>${ text }</h${ level }>`;
  40. };
  41. Renderer.prototype.hr = function() {
  42. return this.options.xhtml ? '<hr/>' : '<hr>';
  43. };
  44. Renderer.prototype.list = function( body, ordered ) {
  45. const type = ordered ? 'ol' : 'ul';
  46. return `<${ type }>${ body }</${ type }>`;
  47. };
  48. Renderer.prototype.listitem = function( text ) {
  49. return `<li>${ text }</li>`;
  50. };
  51. Renderer.prototype.paragraph = function( text ) {
  52. return `<p>${ text }</p>`;
  53. };
  54. Renderer.prototype.table = function( header, body ) {
  55. return `<table><thead>${ header }</thead><tbody>${ body }</tbody></table>`;
  56. };
  57. Renderer.prototype.tablerow = function( content ) {
  58. return '<tr>' + content + '</tr>';
  59. };
  60. Renderer.prototype.tablecell = function( content, flags ) {
  61. const type = flags.header ? 'th' : 'td';
  62. const tag = flags.align ? `<${ type } align="${ flags.align }">` : `<${ type }>`;
  63. return tag + content + `</${ type }>`;
  64. };
  65. // span level renderer
  66. Renderer.prototype.strong = function( text ) {
  67. return `<strong>${ text }</strong>`;
  68. };
  69. Renderer.prototype.em = function( text ) {
  70. return `<em>${ text }</em>`;
  71. };
  72. Renderer.prototype.codespan = function( text ) {
  73. return `<code>${ text.trim() }</code>`;
  74. };
  75. Renderer.prototype.br = function() {
  76. return this.options.xhtml ? '<br/>' : '<br>';
  77. };
  78. Renderer.prototype.del = function( text ) {
  79. return `<del>${ text }</del>`;
  80. };
  81. Renderer.prototype.link = function( href, title, text ) {
  82. if ( this.options.sanitize ) {
  83. let prot;
  84. try {
  85. prot = decodeURIComponent( unescape( href ) )
  86. .replace( /[^\w:]/g, '' )
  87. .toLowerCase();
  88. } catch ( e ) {
  89. return '';
  90. }
  91. if ( prot.indexOf( 'javascript:' ) === 0 || prot.indexOf( 'vbscript:' ) === 0 ) { // jshint ignore:line
  92. return '';
  93. }
  94. }
  95. let out = '<a href="' + href + '"';
  96. if ( title ) {
  97. out += ' title="' + title + '"';
  98. }
  99. out += '>' + text + '</a>';
  100. return out;
  101. };
  102. Renderer.prototype.image = function( href, title, text ) {
  103. let out = '<img src="' + href + '" alt="' + text + '"';
  104. if ( title ) {
  105. out += ' title="' + title + '"';
  106. }
  107. out += this.options.xhtml ? '/>' : '>';
  108. return out;
  109. };
  110. Renderer.prototype.text = function( text ) {
  111. return text;
  112. };
  113. export default Renderer;
  114. function escape( html, encode ) {
  115. return html
  116. .replace( !encode ? /&(?!#?\w+;)/g : /&/g, '&amp;' )
  117. .replace( /</g, '&lt;' )
  118. .replace( />/g, '&gt;' )
  119. .replace( /"/g, '&quot;' )
  120. .replace( /'/g, '&#39;' );
  121. }
  122. function unescape( html ) {
  123. // explicitly match decimal, hex, and named HTML entities
  124. return html.replace( /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function( _, n ) {
  125. n = n.toLowerCase();
  126. if ( n === 'colon' ) {
  127. return ':';
  128. }
  129. if ( n.charAt( 0 ) === '#' ) {
  130. return n.charAt( 1 ) === 'x' ?
  131. String.fromCharCode( parseInt( n.substring( 2 ), 16 ) ) :
  132. String.fromCharCode( +n.substring( 1 ) ); // jscs:ignore
  133. }
  134. return '';
  135. } );
  136. }