mirror of
https://github.com/nextcloud/nextcloud.com.git
synced 2026-01-12 15:44:37 +00:00
430 lines
9.9 KiB
JavaScript
430 lines
9.9 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Local dependencies
|
|
*/
|
|
|
|
var has = require('./common/utils').has;
|
|
var unescapeMd = require('./common/utils').unescapeMd;
|
|
var replaceEntities = require('./common/utils').replaceEntities;
|
|
var escapeHtml = require('./common/utils').escapeHtml;
|
|
|
|
/**
|
|
* Renderer rules cache
|
|
*/
|
|
|
|
var rules = {};
|
|
|
|
/**
|
|
* Blockquotes
|
|
*/
|
|
|
|
rules.blockquote_open = function(/* tokens, idx, options, env */) {
|
|
return '<blockquote>\n';
|
|
};
|
|
|
|
rules.blockquote_close = function(tokens, idx /*, options, env */) {
|
|
return '</blockquote>' + getBreak(tokens, idx);
|
|
};
|
|
|
|
/**
|
|
* Code
|
|
*/
|
|
|
|
rules.code = function(tokens, idx /*, options, env */) {
|
|
if (tokens[idx].block) {
|
|
return '<pre><code>' + escapeHtml(tokens[idx].content) + '</code></pre>' + getBreak(tokens, idx);
|
|
}
|
|
return '<code>' + escapeHtml(tokens[idx].content) + '</code>';
|
|
};
|
|
|
|
/**
|
|
* Fenced code blocks
|
|
*/
|
|
|
|
rules.fence = function(tokens, idx, options, env, instance) {
|
|
var token = tokens[idx];
|
|
var langClass = '';
|
|
var langPrefix = options.langPrefix;
|
|
var langName = '', fences, fenceName;
|
|
var highlighted;
|
|
|
|
if (token.params) {
|
|
|
|
//
|
|
// ```foo bar
|
|
//
|
|
// Try custom renderer "foo" first. That will simplify overwrite
|
|
// for diagrams, latex, and any other fenced block with custom look
|
|
//
|
|
|
|
fences = token.params.split(/\s+/g);
|
|
fenceName = fences.join(' ');
|
|
|
|
if (has(instance.rules.fence_custom, fences[0])) {
|
|
return instance.rules.fence_custom[fences[0]](tokens, idx, options, env, instance);
|
|
}
|
|
|
|
langName = escapeHtml(replaceEntities(unescapeMd(fenceName)));
|
|
langClass = ' class="' + langPrefix + langName + '"';
|
|
}
|
|
|
|
if (options.highlight) {
|
|
highlighted = options.highlight.apply(options.highlight, [ token.content ].concat(fences))
|
|
|| escapeHtml(token.content);
|
|
} else {
|
|
highlighted = escapeHtml(token.content);
|
|
}
|
|
|
|
return '<pre><code' + langClass + '>'
|
|
+ highlighted
|
|
+ '</code></pre>'
|
|
+ getBreak(tokens, idx);
|
|
};
|
|
|
|
rules.fence_custom = {};
|
|
|
|
/**
|
|
* Headings
|
|
*/
|
|
|
|
rules.heading_open = function(tokens, idx /*, options, env */) {
|
|
return '<h' + tokens[idx].hLevel + '>';
|
|
};
|
|
rules.heading_close = function(tokens, idx /*, options, env */) {
|
|
return '</h' + tokens[idx].hLevel + '>\n';
|
|
};
|
|
|
|
/**
|
|
* Horizontal rules
|
|
*/
|
|
|
|
rules.hr = function(tokens, idx, options /*, env */) {
|
|
return (options.xhtmlOut ? '<hr />' : '<hr>') + getBreak(tokens, idx);
|
|
};
|
|
|
|
/**
|
|
* Bullets
|
|
*/
|
|
|
|
rules.bullet_list_open = function(/* tokens, idx, options, env */) {
|
|
return '<ul>\n';
|
|
};
|
|
rules.bullet_list_close = function(tokens, idx /*, options, env */) {
|
|
return '</ul>' + getBreak(tokens, idx);
|
|
};
|
|
|
|
/**
|
|
* List items
|
|
*/
|
|
|
|
rules.list_item_open = function(/* tokens, idx, options, env */) {
|
|
return '<li>';
|
|
};
|
|
rules.list_item_close = function(/* tokens, idx, options, env */) {
|
|
return '</li>\n';
|
|
};
|
|
|
|
/**
|
|
* Ordered list items
|
|
*/
|
|
|
|
rules.ordered_list_open = function(tokens, idx /*, options, env */) {
|
|
var token = tokens[idx];
|
|
var order = token.order > 1 ? ' start="' + token.order + '"' : '';
|
|
return '<ol' + order + '>\n';
|
|
};
|
|
rules.ordered_list_close = function(tokens, idx /*, options, env */) {
|
|
return '</ol>' + getBreak(tokens, idx);
|
|
};
|
|
|
|
/**
|
|
* Paragraphs
|
|
*/
|
|
|
|
rules.paragraph_open = function(tokens, idx /*, options, env */) {
|
|
return tokens[idx].tight ? '' : '<p>';
|
|
};
|
|
rules.paragraph_close = function(tokens, idx /*, options, env */) {
|
|
var addBreak = !(tokens[idx].tight && idx && tokens[idx - 1].type === 'inline' && !tokens[idx - 1].content);
|
|
return (tokens[idx].tight ? '' : '</p>') + (addBreak ? getBreak(tokens, idx) : '');
|
|
};
|
|
|
|
/**
|
|
* Links
|
|
*/
|
|
|
|
rules.link_open = function(tokens, idx, options /* env */) {
|
|
var title = tokens[idx].title ? (' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"') : '';
|
|
var target = options.linkTarget ? (' target="' + options.linkTarget + '"') : '';
|
|
return '<a href="' + escapeHtml(tokens[idx].href) + '"' + title + target + '>';
|
|
};
|
|
rules.link_close = function(/* tokens, idx, options, env */) {
|
|
return '</a>';
|
|
};
|
|
|
|
/**
|
|
* Images
|
|
*/
|
|
|
|
rules.image = function(tokens, idx, options /*, env */) {
|
|
var src = ' src="' + escapeHtml(tokens[idx].src) + '"';
|
|
var title = tokens[idx].title ? (' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"') : '';
|
|
var alt = ' alt="' + (tokens[idx].alt ? escapeHtml(replaceEntities(unescapeMd(tokens[idx].alt))) : '') + '"';
|
|
var suffix = options.xhtmlOut ? ' /' : '';
|
|
return '<img' + src + alt + title + suffix + '>';
|
|
};
|
|
|
|
/**
|
|
* Tables
|
|
*/
|
|
|
|
rules.table_open = function(/* tokens, idx, options, env */) {
|
|
return '<table>\n';
|
|
};
|
|
rules.table_close = function(/* tokens, idx, options, env */) {
|
|
return '</table>\n';
|
|
};
|
|
rules.thead_open = function(/* tokens, idx, options, env */) {
|
|
return '<thead>\n';
|
|
};
|
|
rules.thead_close = function(/* tokens, idx, options, env */) {
|
|
return '</thead>\n';
|
|
};
|
|
rules.tbody_open = function(/* tokens, idx, options, env */) {
|
|
return '<tbody>\n';
|
|
};
|
|
rules.tbody_close = function(/* tokens, idx, options, env */) {
|
|
return '</tbody>\n';
|
|
};
|
|
rules.tr_open = function(/* tokens, idx, options, env */) {
|
|
return '<tr>';
|
|
};
|
|
rules.tr_close = function(/* tokens, idx, options, env */) {
|
|
return '</tr>\n';
|
|
};
|
|
rules.th_open = function(tokens, idx /*, options, env */) {
|
|
var token = tokens[idx];
|
|
return '<th'
|
|
+ (token.align ? ' style="text-align:' + token.align + '"' : '')
|
|
+ '>';
|
|
};
|
|
rules.th_close = function(/* tokens, idx, options, env */) {
|
|
return '</th>';
|
|
};
|
|
rules.td_open = function(tokens, idx /*, options, env */) {
|
|
var token = tokens[idx];
|
|
return '<td'
|
|
+ (token.align ? ' style="text-align:' + token.align + '"' : '')
|
|
+ '>';
|
|
};
|
|
rules.td_close = function(/* tokens, idx, options, env */) {
|
|
return '</td>';
|
|
};
|
|
|
|
/**
|
|
* Bold
|
|
*/
|
|
|
|
rules.strong_open = function(/* tokens, idx, options, env */) {
|
|
return '<strong>';
|
|
};
|
|
rules.strong_close = function(/* tokens, idx, options, env */) {
|
|
return '</strong>';
|
|
};
|
|
|
|
/**
|
|
* Italicize
|
|
*/
|
|
|
|
rules.em_open = function(/* tokens, idx, options, env */) {
|
|
return '<em>';
|
|
};
|
|
rules.em_close = function(/* tokens, idx, options, env */) {
|
|
return '</em>';
|
|
};
|
|
|
|
/**
|
|
* Strikethrough
|
|
*/
|
|
|
|
rules.del_open = function(/* tokens, idx, options, env */) {
|
|
return '<del>';
|
|
};
|
|
rules.del_close = function(/* tokens, idx, options, env */) {
|
|
return '</del>';
|
|
};
|
|
|
|
/**
|
|
* Insert
|
|
*/
|
|
|
|
rules.ins_open = function(/* tokens, idx, options, env */) {
|
|
return '<ins>';
|
|
};
|
|
rules.ins_close = function(/* tokens, idx, options, env */) {
|
|
return '</ins>';
|
|
};
|
|
|
|
/**
|
|
* Highlight
|
|
*/
|
|
|
|
rules.mark_open = function(/* tokens, idx, options, env */) {
|
|
return '<mark>';
|
|
};
|
|
rules.mark_close = function(/* tokens, idx, options, env */) {
|
|
return '</mark>';
|
|
};
|
|
|
|
/**
|
|
* Super- and sub-script
|
|
*/
|
|
|
|
rules.sub = function(tokens, idx /*, options, env */) {
|
|
return '<sub>' + escapeHtml(tokens[idx].content) + '</sub>';
|
|
};
|
|
rules.sup = function(tokens, idx /*, options, env */) {
|
|
return '<sup>' + escapeHtml(tokens[idx].content) + '</sup>';
|
|
};
|
|
|
|
/**
|
|
* Breaks
|
|
*/
|
|
|
|
rules.hardbreak = function(tokens, idx, options /*, env */) {
|
|
return options.xhtmlOut ? '<br />\n' : '<br>\n';
|
|
};
|
|
rules.softbreak = function(tokens, idx, options /*, env */) {
|
|
return options.breaks ? (options.xhtmlOut ? '<br />\n' : '<br>\n') : '\n';
|
|
};
|
|
|
|
/**
|
|
* Text
|
|
*/
|
|
|
|
rules.text = function(tokens, idx /*, options, env */) {
|
|
return escapeHtml(tokens[idx].content);
|
|
};
|
|
|
|
/**
|
|
* Content
|
|
*/
|
|
|
|
rules.htmlblock = function(tokens, idx /*, options, env */) {
|
|
return tokens[idx].content;
|
|
};
|
|
rules.htmltag = function(tokens, idx /*, options, env */) {
|
|
return tokens[idx].content;
|
|
};
|
|
|
|
/**
|
|
* Abbreviations, initialism
|
|
*/
|
|
|
|
rules.abbr_open = function(tokens, idx /*, options, env */) {
|
|
return '<abbr title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '">';
|
|
};
|
|
rules.abbr_close = function(/* tokens, idx, options, env */) {
|
|
return '</abbr>';
|
|
};
|
|
|
|
/**
|
|
* Footnotes
|
|
*/
|
|
|
|
rules.footnote_ref = function(tokens, idx) {
|
|
var n = Number(tokens[idx].id + 1).toString();
|
|
var id = 'fnref' + n;
|
|
if (tokens[idx].subId > 0) {
|
|
id += ':' + tokens[idx].subId;
|
|
}
|
|
return '<sup class="footnote-ref"><a href="#fn' + n + '" id="' + id + '">[' + n + ']</a></sup>';
|
|
};
|
|
rules.footnote_block_open = function(tokens, idx, options) {
|
|
var hr = options.xhtmlOut
|
|
? '<hr class="footnotes-sep" />\n'
|
|
: '<hr class="footnotes-sep">\n';
|
|
return hr + '<section class="footnotes">\n<ol class="footnotes-list">\n';
|
|
};
|
|
rules.footnote_block_close = function() {
|
|
return '</ol>\n</section>\n';
|
|
};
|
|
rules.footnote_open = function(tokens, idx) {
|
|
var id = Number(tokens[idx].id + 1).toString();
|
|
return '<li id="fn' + id + '" class="footnote-item">';
|
|
};
|
|
rules.footnote_close = function() {
|
|
return '</li>\n';
|
|
};
|
|
rules.footnote_anchor = function(tokens, idx) {
|
|
var n = Number(tokens[idx].id + 1).toString();
|
|
var id = 'fnref' + n;
|
|
if (tokens[idx].subId > 0) {
|
|
id += ':' + tokens[idx].subId;
|
|
}
|
|
return ' <a href="#' + id + '" class="footnote-backref">↩</a>';
|
|
};
|
|
|
|
/**
|
|
* Definition lists
|
|
*/
|
|
|
|
rules.dl_open = function() {
|
|
return '<dl>\n';
|
|
};
|
|
rules.dt_open = function() {
|
|
return '<dt>';
|
|
};
|
|
rules.dd_open = function() {
|
|
return '<dd>';
|
|
};
|
|
rules.dl_close = function() {
|
|
return '</dl>\n';
|
|
};
|
|
rules.dt_close = function() {
|
|
return '</dt>\n';
|
|
};
|
|
rules.dd_close = function() {
|
|
return '</dd>\n';
|
|
};
|
|
|
|
/**
|
|
* Helper functions
|
|
*/
|
|
|
|
function nextToken(tokens, idx) {
|
|
if (++idx >= tokens.length - 2) {
|
|
return idx;
|
|
}
|
|
if ((tokens[idx].type === 'paragraph_open' && tokens[idx].tight) &&
|
|
(tokens[idx + 1].type === 'inline' && tokens[idx + 1].content.length === 0) &&
|
|
(tokens[idx + 2].type === 'paragraph_close' && tokens[idx + 2].tight)) {
|
|
return nextToken(tokens, idx + 2);
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
/**
|
|
* Check to see if `\n` is needed before the next token.
|
|
*
|
|
* @param {Array} `tokens`
|
|
* @param {Number} `idx`
|
|
* @return {String} Empty string or newline
|
|
* @api private
|
|
*/
|
|
|
|
var getBreak = rules.getBreak = function getBreak(tokens, idx) {
|
|
idx = nextToken(tokens, idx);
|
|
if (idx < tokens.length && tokens[idx].type === 'list_item_close') {
|
|
return '';
|
|
}
|
|
return '\n';
|
|
};
|
|
|
|
/**
|
|
* Expose `rules`
|
|
*/
|
|
|
|
module.exports = rules;
|