You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
10 KiB
415 lines
10 KiB
(function (Prism) { |
|
|
|
// Allow only one line break |
|
var inner = /(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source; |
|
|
|
/** |
|
* This function is intended for the creation of the bold or italic pattern. |
|
* |
|
* This also adds a lookbehind group to the given pattern to ensure that the pattern is not backslash-escaped. |
|
* |
|
* _Note:_ Keep in mind that this adds a capturing group. |
|
* |
|
* @param {string} pattern |
|
* @returns {RegExp} |
|
*/ |
|
function createInline(pattern) { |
|
pattern = pattern.replace(/<inner>/g, function () { return inner; }); |
|
return RegExp(/((?:^|[^\\])(?:\\{2})*)/.source + '(?:' + pattern + ')'); |
|
} |
|
|
|
|
|
var tableCell = /(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source; |
|
var tableRow = /\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g, function () { return tableCell; }); |
|
var tableLine = /\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source; |
|
|
|
|
|
Prism.languages.markdown = Prism.languages.extend('markup', {}); |
|
Prism.languages.insertBefore('markdown', 'prolog', { |
|
'front-matter-block': { |
|
pattern: /(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/, |
|
lookbehind: true, |
|
greedy: true, |
|
inside: { |
|
'punctuation': /^---|---$/, |
|
'front-matter': { |
|
pattern: /\S+(?:\s+\S+)*/, |
|
alias: ['yaml', 'language-yaml'], |
|
inside: Prism.languages.yaml |
|
} |
|
} |
|
}, |
|
'blockquote': { |
|
// > ... |
|
pattern: /^>(?:[\t ]*>)*/m, |
|
alias: 'punctuation' |
|
}, |
|
'table': { |
|
pattern: RegExp('^' + tableRow + tableLine + '(?:' + tableRow + ')*', 'm'), |
|
inside: { |
|
'table-data-rows': { |
|
pattern: RegExp('^(' + tableRow + tableLine + ')(?:' + tableRow + ')*$'), |
|
lookbehind: true, |
|
inside: { |
|
'table-data': { |
|
pattern: RegExp(tableCell), |
|
inside: Prism.languages.markdown |
|
}, |
|
'punctuation': /\|/ |
|
} |
|
}, |
|
'table-line': { |
|
pattern: RegExp('^(' + tableRow + ')' + tableLine + '$'), |
|
lookbehind: true, |
|
inside: { |
|
'punctuation': /\||:?-{3,}:?/ |
|
} |
|
}, |
|
'table-header-row': { |
|
pattern: RegExp('^' + tableRow + '$'), |
|
inside: { |
|
'table-header': { |
|
pattern: RegExp(tableCell), |
|
alias: 'important', |
|
inside: Prism.languages.markdown |
|
}, |
|
'punctuation': /\|/ |
|
} |
|
} |
|
} |
|
}, |
|
'code': [ |
|
{ |
|
// Prefixed by 4 spaces or 1 tab and preceded by an empty line |
|
pattern: /((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/, |
|
lookbehind: true, |
|
alias: 'keyword' |
|
}, |
|
{ |
|
// ```optional language |
|
// code block |
|
// ``` |
|
pattern: /^```[\s\S]*?^```$/m, |
|
greedy: true, |
|
inside: { |
|
'code-block': { |
|
pattern: /^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m, |
|
lookbehind: true |
|
}, |
|
'code-language': { |
|
pattern: /^(```).+/, |
|
lookbehind: true |
|
}, |
|
'punctuation': /```/ |
|
} |
|
} |
|
], |
|
'title': [ |
|
{ |
|
// title 1 |
|
// ======= |
|
|
|
// title 2 |
|
// ------- |
|
pattern: /\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m, |
|
alias: 'important', |
|
inside: { |
|
punctuation: /==+$|--+$/ |
|
} |
|
}, |
|
{ |
|
// # title 1 |
|
// ###### title 6 |
|
pattern: /(^\s*)#.+/m, |
|
lookbehind: true, |
|
alias: 'important', |
|
inside: { |
|
punctuation: /^#+|#+$/ |
|
} |
|
} |
|
], |
|
'hr': { |
|
// *** |
|
// --- |
|
// * * * |
|
// ----------- |
|
pattern: /(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m, |
|
lookbehind: true, |
|
alias: 'punctuation' |
|
}, |
|
'list': { |
|
// * item |
|
// + item |
|
// - item |
|
// 1. item |
|
pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m, |
|
lookbehind: true, |
|
alias: 'punctuation' |
|
}, |
|
'url-reference': { |
|
// [id]: http://example.com "Optional title" |
|
// [id]: http://example.com 'Optional title' |
|
// [id]: http://example.com (Optional title) |
|
// [id]: <http://example.com> "Optional title" |
|
pattern: /!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/, |
|
inside: { |
|
'variable': { |
|
pattern: /^(!?\[)[^\]]+/, |
|
lookbehind: true |
|
}, |
|
'string': /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/, |
|
'punctuation': /^[\[\]!:]|[<>]/ |
|
}, |
|
alias: 'url' |
|
}, |
|
'bold': { |
|
// **strong** |
|
// __strong__ |
|
|
|
// allow one nested instance of italic text using the same delimiter |
|
pattern: createInline(/\b__(?:(?!_)<inner>|_(?:(?!_)<inner>)+_)+__\b|\*\*(?:(?!\*)<inner>|\*(?:(?!\*)<inner>)+\*)+\*\*/.source), |
|
lookbehind: true, |
|
greedy: true, |
|
inside: { |
|
'content': { |
|
pattern: /(^..)[\s\S]+(?=..$)/, |
|
lookbehind: true, |
|
inside: {} // see below |
|
}, |
|
'punctuation': /\*\*|__/ |
|
} |
|
}, |
|
'italic': { |
|
// *em* |
|
// _em_ |
|
|
|
// allow one nested instance of bold text using the same delimiter |
|
pattern: createInline(/\b_(?:(?!_)<inner>|__(?:(?!_)<inner>)+__)+_\b|\*(?:(?!\*)<inner>|\*\*(?:(?!\*)<inner>)+\*\*)+\*/.source), |
|
lookbehind: true, |
|
greedy: true, |
|
inside: { |
|
'content': { |
|
pattern: /(^.)[\s\S]+(?=.$)/, |
|
lookbehind: true, |
|
inside: {} // see below |
|
}, |
|
'punctuation': /[*_]/ |
|
} |
|
}, |
|
'strike': { |
|
// ~~strike through~~ |
|
// ~strike~ |
|
// eslint-disable-next-line regexp/strict |
|
pattern: createInline(/(~~?)(?:(?!~)<inner>)+\2/.source), |
|
lookbehind: true, |
|
greedy: true, |
|
inside: { |
|
'content': { |
|
pattern: /(^~~?)[\s\S]+(?=\1$)/, |
|
lookbehind: true, |
|
inside: {} // see below |
|
}, |
|
'punctuation': /~~?/ |
|
} |
|
}, |
|
'code-snippet': { |
|
// `code` |
|
// ``code`` |
|
pattern: /(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/, |
|
lookbehind: true, |
|
greedy: true, |
|
alias: ['code', 'keyword'] |
|
}, |
|
'url': { |
|
// [example](http://example.com "Optional title") |
|
// [example][id] |
|
// [example] [id] |
|
pattern: createInline(/!?\[(?:(?!\])<inner>)+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\])<inner>)+\])/.source), |
|
lookbehind: true, |
|
greedy: true, |
|
inside: { |
|
'operator': /^!/, |
|
'content': { |
|
pattern: /(^\[)[^\]]+(?=\])/, |
|
lookbehind: true, |
|
inside: {} // see below |
|
}, |
|
'variable': { |
|
pattern: /(^\][ \t]?\[)[^\]]+(?=\]$)/, |
|
lookbehind: true |
|
}, |
|
'url': { |
|
pattern: /(^\]\()[^\s)]+/, |
|
lookbehind: true |
|
}, |
|
'string': { |
|
pattern: /(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/, |
|
lookbehind: true |
|
} |
|
} |
|
} |
|
}); |
|
|
|
['url', 'bold', 'italic', 'strike'].forEach(function (token) { |
|
['url', 'bold', 'italic', 'strike', 'code-snippet'].forEach(function (inside) { |
|
if (token !== inside) { |
|
Prism.languages.markdown[token].inside.content.inside[inside] = Prism.languages.markdown[inside]; |
|
} |
|
}); |
|
}); |
|
|
|
Prism.hooks.add('after-tokenize', function (env) { |
|
if (env.language !== 'markdown' && env.language !== 'md') { |
|
return; |
|
} |
|
|
|
function walkTokens(tokens) { |
|
if (!tokens || typeof tokens === 'string') { |
|
return; |
|
} |
|
|
|
for (var i = 0, l = tokens.length; i < l; i++) { |
|
var token = tokens[i]; |
|
|
|
if (token.type !== 'code') { |
|
walkTokens(token.content); |
|
continue; |
|
} |
|
|
|
/* |
|
* Add the correct `language-xxxx` class to this code block. Keep in mind that the `code-language` token |
|
* is optional. But the grammar is defined so that there is only one case we have to handle: |
|
* |
|
* token.content = [ |
|
* <span class="punctuation">```</span>, |
|
* <span class="code-language">xxxx</span>, |
|
* '\n', // exactly one new lines (\r or \n or \r\n) |
|
* <span class="code-block">...</span>, |
|
* '\n', // exactly one new lines again |
|
* <span class="punctuation">```</span> |
|
* ]; |
|
*/ |
|
|
|
var codeLang = token.content[1]; |
|
var codeBlock = token.content[3]; |
|
|
|
if (codeLang && codeBlock && |
|
codeLang.type === 'code-language' && codeBlock.type === 'code-block' && |
|
typeof codeLang.content === 'string') { |
|
|
|
// this might be a language that Prism does not support |
|
|
|
// do some replacements to support C++, C#, and F# |
|
var lang = codeLang.content.replace(/\b#/g, 'sharp').replace(/\b\+\+/g, 'pp'); |
|
// only use the first word |
|
lang = (/[a-z][\w-]*/i.exec(lang) || [''])[0].toLowerCase(); |
|
var alias = 'language-' + lang; |
|
|
|
// add alias |
|
if (!codeBlock.alias) { |
|
codeBlock.alias = [alias]; |
|
} else if (typeof codeBlock.alias === 'string') { |
|
codeBlock.alias = [codeBlock.alias, alias]; |
|
} else { |
|
codeBlock.alias.push(alias); |
|
} |
|
} |
|
} |
|
} |
|
|
|
walkTokens(env.tokens); |
|
}); |
|
|
|
Prism.hooks.add('wrap', function (env) { |
|
if (env.type !== 'code-block') { |
|
return; |
|
} |
|
|
|
var codeLang = ''; |
|
for (var i = 0, l = env.classes.length; i < l; i++) { |
|
var cls = env.classes[i]; |
|
var match = /language-(.+)/.exec(cls); |
|
if (match) { |
|
codeLang = match[1]; |
|
break; |
|
} |
|
} |
|
|
|
var grammar = Prism.languages[codeLang]; |
|
|
|
if (!grammar) { |
|
if (codeLang && codeLang !== 'none' && Prism.plugins.autoloader) { |
|
var id = 'md-' + new Date().valueOf() + '-' + Math.floor(Math.random() * 1e16); |
|
env.attributes['id'] = id; |
|
|
|
Prism.plugins.autoloader.loadLanguages(codeLang, function () { |
|
var ele = document.getElementById(id); |
|
if (ele) { |
|
ele.innerHTML = Prism.highlight(ele.textContent, Prism.languages[codeLang], codeLang); |
|
} |
|
}); |
|
} |
|
} else { |
|
env.content = Prism.highlight(textContent(env.content), grammar, codeLang); |
|
} |
|
}); |
|
|
|
var tagPattern = RegExp(Prism.languages.markup.tag.pattern.source, 'gi'); |
|
|
|
/** |
|
* A list of known entity names. |
|
* |
|
* This will always be incomplete to save space. The current list is the one used by lowdash's unescape function. |
|
* |
|
* @see {@link https://github.com/lodash/lodash/blob/2da024c3b4f9947a48517639de7560457cd4ec6c/unescape.js#L2} |
|
*/ |
|
var KNOWN_ENTITY_NAMES = { |
|
'amp': '&', |
|
'lt': '<', |
|
'gt': '>', |
|
'quot': '"', |
|
}; |
|
|
|
// IE 11 doesn't support `String.fromCodePoint` |
|
var fromCodePoint = String.fromCodePoint || String.fromCharCode; |
|
|
|
/** |
|
* Returns the text content of a given HTML source code string. |
|
* |
|
* @param {string} html |
|
* @returns {string} |
|
*/ |
|
function textContent(html) { |
|
// remove all tags |
|
var text = html.replace(tagPattern, ''); |
|
|
|
// decode known entities |
|
text = text.replace(/&(\w{1,8}|#x?[\da-f]{1,8});/gi, function (m, code) { |
|
code = code.toLowerCase(); |
|
|
|
if (code[0] === '#') { |
|
var value; |
|
if (code[1] === 'x') { |
|
value = parseInt(code.slice(2), 16); |
|
} else { |
|
value = Number(code.slice(1)); |
|
} |
|
|
|
return fromCodePoint(value); |
|
} else { |
|
var known = KNOWN_ENTITY_NAMES[code]; |
|
if (known) { |
|
return known; |
|
} |
|
|
|
// unable to decode |
|
return m; |
|
} |
|
}); |
|
|
|
return text; |
|
} |
|
|
|
Prism.languages.md = Prism.languages.markdown; |
|
|
|
}(Prism));
|
|
|