} [languageSubset]
- @returns {AutoHighlightResult}
*/
- function highlightAuto(code, languageSubset) {
- languageSubset = languageSubset || options.languages || Object.keys(languages);
- var result = justTextHighlightResult(code);
- var secondBest = result;
- languageSubset.filter(getLanguage).filter(autoDetection).forEach(function(name) {
- var current = _highlight(name, code, false);
- current.language = name;
- if (current.relevance > secondBest.relevance) {
- secondBest = current;
- }
- if (current.relevance > result.relevance) {
- secondBest = result;
- result = current;
- }
- });
- if (secondBest.language) {
- // second_best (with underscore) is the expected API
- result.second_best = secondBest;
- }
- return result;
- }
/**
- Post-processing of the highlighted markup:
-
- - replace TABs with something more useful
- - replace real line-breaks with '
' for non-pre containers
-
- @param {string} html
- @returns {string}
- */
- function fixMarkup(html) {
- if (!(options.tabReplace || options.useBR)) {
- return html;
- }
-
- return html.replace(fixMarkupRe, match => {
- if (match === '\n') {
- return options.useBR ? '
' : match;
- } else if (options.tabReplace) {
- return match.replace(/\t/g, options.tabReplace);
- }
- return match;
- });
- }
-
- /**
- * Builds new class name for block given the language name
- *
- * @param {string} prevClassName
- * @param {string} [currentLang]
- * @param {string} [resultLang]
+ * @param {string} message
*/
- function buildClassName(prevClassName, currentLang, resultLang) {
- var language = currentLang ? aliases[currentLang] : resultLang;
- var result = [prevClassName.trim()];
-
- if (!prevClassName.match(/\bhljs\b/)) {
- result.push('hljs');
- }
-
- if (!prevClassName.includes(language)) {
- result.push(language);
- }
-
- return result.join(' ').trim();
- }
-
- /**
- * Applies highlighting to a DOM node containing code. Accepts a DOM node and
- * two optional parameters for fixMarkup.
- *
- * @param {HighlightedHTMLElement} element - the HTML element to highlight
- */
- function highlightBlock(element) {
- /** @type HTMLElement */
- let node = null;
- const language = blockLanguage(element);
-
- if (shouldNotHighlight(language)) return;
-
- fire("before:highlightBlock",
- { block: element, language: language });
-
- if (options.useBR) {
- node = document.createElement('div');
- node.innerHTML = element.innerHTML.replace(/\n/g, '').replace(/
/g, '\n');
- } else {
- node = element;
- }
- const text = node.textContent;
- const result = language ? highlight(language, text, true) : highlightAuto(text);
-
- const originalStream = nodeStream$1(node);
- if (originalStream.length) {
- const resultNode = document.createElement('div');
- resultNode.innerHTML = result.value;
- result.value = mergeStreams$1(originalStream, nodeStream$1(resultNode), text);
- }
- result.value = fixMarkup(result.value);
-
- fire("after:highlightBlock", { block: element, result: result });
-
- element.innerHTML = result.value;
- element.className = buildClassName(element.className, language, result.language);
- element.result = {
- language: result.language,
- // TODO: remove with version 11.0
- re: result.relevance,
- relavance: result.relevance
- };
- if (result.second_best) {
- element.second_best = {
- language: result.second_best.language,
- // TODO: remove with version 11.0
- re: result.second_best.relevance,
- relavance: result.second_best.relevance
- };
- }
- }
-
- /**
- * Updates highlight.js global options with the passed options
- *
- * @param {{}} userOptions
- */
- function configure(userOptions) {
- options = inherit$1(options, userOptions);
- }
-
- /**
- * Highlights to all blocks on a page
- *
- * @type {Function & {called?: boolean}}
- */
- const initHighlighting = () => {
- if (initHighlighting.called) return;
- initHighlighting.called = true;
-
- var blocks = document.querySelectorAll('pre code');
- ArrayProto.forEach.call(blocks, highlightBlock);
+ const error = (message) => {
+ console.error(message);
};
- // Higlights all when DOMContentLoaded fires
- function initHighlightingOnLoad() {
- // @ts-ignore
- window.addEventListener('DOMContentLoaded', initHighlighting, false);
- }
-
/**
- * Register a language grammar module
- *
- * @param {string} languageName
- * @param {LanguageFn} languageDefinition
- */
- function registerLanguage(languageName, languageDefinition) {
- var lang = null;
- try {
- lang = languageDefinition(hljs);
- } catch (error) {
- console.error("Language definition for '{}' could not be registered.".replace("{}", languageName));
- // hard or soft error
- if (!SAFE_MODE) { throw error; } else { console.error(error); }
- // languages that have serious errors are replaced with essentially a
- // "plaintext" stand-in so that the code blocks will still get normal
- // css classes applied to them - and one bad language won't break the
- // entire highlighter
- lang = PLAINTEXT_LANGUAGE;
- }
- // give it a temporary name if it doesn't have one in the meta-data
- if (!lang.name) lang.name = languageName;
- languages[languageName] = lang;
- lang.rawDefinition = languageDefinition.bind(null, hljs);
-
- if (lang.aliases) {
- registerAliases(lang.aliases, { languageName });
- }
- }
-
- /**
- * @returns {string[]} List of language internal names
- */
- function listLanguages() {
- return Object.keys(languages);
- }
-
- /**
- intended usage: When one language truly requires another
-
- Unlike `getLanguage`, this will throw when the requested language
- is not available.
-
- @param {string} name - name of the language to fetch/require
- @returns {Language | never}
- */
- function requireLanguage(name) {
- var lang = getLanguage(name);
- if (lang) { return lang; }
-
- var err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}', name));
- throw err;
- }
-
- /**
- * @param {string} name - name of the language to retrieve
- * @returns {Language | undefined}
- */
- function getLanguage(name) {
- name = (name || '').toLowerCase();
- return languages[name] || languages[aliases[name]];
- }
-
- /**
- *
- * @param {string|string[]} aliasList - single alias or list of aliases
- * @param {{languageName: string}} opts
- */
- function registerAliases(aliasList, { languageName }) {
- if (typeof aliasList === 'string') {
- aliasList = [aliasList];
- }
- aliasList.forEach(alias => { aliases[alias] = languageName; });
- }
-
- /**
- * Determines if a given language has auto-detection enabled
- * @param {string} name - name of the language
- */
- function autoDetection(name) {
- var lang = getLanguage(name);
- return lang && !lang.disableAutodetect;
- }
-
- /**
- * @param {HLJSPlugin} plugin
- */
- function addPlugin(plugin) {
- plugins.push(plugin);
- }
-
- /**
- *
- * @param {PluginEvent} event
+ * @param {string} message
* @param {any} args
*/
- function fire(event, args) {
- var cb = event;
- plugins.forEach(function(plugin) {
- if (plugin[cb]) {
- plugin[cb](args);
- }
- });
- }
+ const warn = (message, ...args) => {
+ console.log(`WARN: ${message}`, ...args);
+ };
- /* fixMarkup is deprecated and will be removed entirely in v11 */
- function deprecate_fixMarkup(arg) {
- console.warn("fixMarkup is deprecated and will be removed entirely in v11.0");
- console.warn("Please see https://github.com/highlightjs/highlight.js/issues/2534");
+ /**
+ * @param {string} version
+ * @param {string} message
+ */
+ const deprecated = (version, message) => {
+ console.log(`Deprecated as of ${version}. ${message}`);
+ };
- return fixMarkup(arg)
- }
+ /*
+ Syntax highlighting with language autodetection.
+ https://highlightjs.org/
+ */
- /* Interface definition */
- Object.assign(hljs, {
- highlight,
- highlightAuto,
- fixMarkup: deprecate_fixMarkup,
- highlightBlock,
- configure,
- initHighlighting,
- initHighlightingOnLoad,
- registerLanguage,
- listLanguages,
- getLanguage,
- registerAliases,
- requireLanguage,
- autoDetection,
- inherit: inherit$1,
- addPlugin,
- // plugins for frameworks
- vuePlugin: VuePlugin
- });
+ const escape = escapeHTML;
+ const inherit = inherit$1;
+ const NO_MATCH = Symbol("nomatch");
- hljs.debugMode = function() { SAFE_MODE = false; };
- hljs.safeMode = function() { SAFE_MODE = true; };
- hljs.versionString = version;
+ /**
+ * @param {any} hljs - object that is extended (legacy)
+ * @returns {HLJSApi}
+ */
+ const HLJS = function(hljs) {
+ // Global internal variables used within the highlight.js library.
+ /** @type {Record} */
+ const languages = Object.create(null);
+ /** @type {Record} */
+ const aliases = Object.create(null);
+ /** @type {HLJSPlugin[]} */
+ const plugins = [];
- for (const key in MODES) {
- // @ts-ignore
- if (typeof MODES[key] === "object") {
- // @ts-ignore
- deepFreeze(MODES[key]);
+ // safe/production mode - swallows more errors, tries to keep running
+ // even if a single syntax or parse hits a fatal error
+ let SAFE_MODE = true;
+ const fixMarkupRe = /(^(<[^>]+>|\t|)+|\n)/gm;
+ const LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?";
+ /** @type {Language} */
+ const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text', contains: [] };
+
+ // Global options used when within external APIs. This is modified when
+ // calling the `hljs.configure` function.
+ /** @type HLJSOptions */
+ let options = {
+ noHighlightRe: /^(no-?highlight)$/i,
+ languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i,
+ classPrefix: 'hljs-',
+ tabReplace: null,
+ useBR: false,
+ languages: null,
+ // beta configuration options, subject to change, welcome to discuss
+ // https://github.com/highlightjs/highlight.js/issues/1086
+ __emitter: TokenTreeEmitter
+ };
+
+ /* Utility functions */
+
+ /**
+ * Tests a language name to see if highlighting should be skipped
+ * @param {string} languageName
+ */
+ function shouldNotHighlight(languageName) {
+ return options.noHighlightRe.test(languageName);
}
- }
- // merge all the modes/regexs into our main object
- Object.assign(hljs, MODES);
+ /**
+ * @param {HighlightedHTMLElement} block - the HTML element to determine language for
+ */
+ function blockLanguage(block) {
+ let classes = block.className + ' ';
- return hljs;
- };
+ classes += block.parentNode ? block.parentNode.className : '';
- // export an "instance" of the highlighter
- var highlight = HLJS({});
+ // language-* takes precedence over non-prefixed class names.
+ const match = options.languageDetectRe.exec(classes);
+ if (match) {
+ const language = getLanguage(match[1]);
+ if (!language) {
+ warn(LANGUAGE_NOT_FOUND.replace("{}", match[1]));
+ warn("Falling back to no-highlight mode for this block.", block);
+ }
+ return language ? match[1] : 'no-highlight';
+ }
- return highlight;
+ return classes
+ .split(/\s+/)
+ .find((_class) => shouldNotHighlight(_class) || getLanguage(_class));
+ }
+
+ /**
+ * Core highlighting function.
+ *
+ * @param {string} languageName - the language to use for highlighting
+ * @param {string} code - the code to highlight
+ * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail
+ * @param {CompiledMode} [continuation] - current continuation mode, if any
+ *
+ * @returns {HighlightResult} Result - an object that represents the result
+ * @property {string} language - the language name
+ * @property {number} relevance - the relevance score
+ * @property {string} value - the highlighted HTML code
+ * @property {string} code - the original raw code
+ * @property {CompiledMode} top - top of the current mode stack
+ * @property {boolean} illegal - indicates whether any illegal matches were found
+ */
+ function highlight(languageName, code, ignoreIllegals, continuation) {
+ /** @type {BeforeHighlightContext} */
+ const context = {
+ code,
+ language: languageName
+ };
+ // the plugin can change the desired language or the code to be highlighted
+ // just be changing the object it was passed
+ fire("before:highlight", context);
+
+ // a before plugin can usurp the result completely by providing it's own
+ // in which case we don't even need to call highlight
+ const result = context.result
+ ? context.result
+ : _highlight(context.language, context.code, ignoreIllegals, continuation);
+
+ result.code = context.code;
+ // the plugin can change anything in result to suite it
+ fire("after:highlight", result);
+
+ return result;
+ }
+
+ /**
+ * private highlight that's used internally and does not fire callbacks
+ *
+ * @param {string} languageName - the language to use for highlighting
+ * @param {string} code - the code to highlight
+ * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail
+ * @param {CompiledMode} [continuation] - current continuation mode, if any
+ * @returns {HighlightResult} - result of the highlight operation
+ */
+ function _highlight(languageName, code, ignoreIllegals, continuation) {
+ const codeToHighlight = code;
+
+ /**
+ * Return keyword data if a match is a keyword
+ * @param {CompiledMode} mode - current mode
+ * @param {RegExpMatchArray} match - regexp match data
+ * @returns {KeywordData | false}
+ */
+ function keywordData(mode, match) {
+ const matchText = language.case_insensitive ? match[0].toLowerCase() : match[0];
+ return Object.prototype.hasOwnProperty.call(mode.keywords, matchText) && mode.keywords[matchText];
+ }
+
+ function processKeywords() {
+ if (!top.keywords) {
+ emitter.addText(modeBuffer);
+ return;
+ }
+
+ let lastIndex = 0;
+ top.keywordPatternRe.lastIndex = 0;
+ let match = top.keywordPatternRe.exec(modeBuffer);
+ let buf = "";
+
+ while (match) {
+ buf += modeBuffer.substring(lastIndex, match.index);
+ const data = keywordData(top, match);
+ if (data) {
+ const [kind, keywordRelevance] = data;
+ emitter.addText(buf);
+ buf = "";
+
+ relevance += keywordRelevance;
+ const cssClass = language.classNameAliases[kind] || kind;
+ emitter.addKeyword(match[0], cssClass);
+ } else {
+ buf += match[0];
+ }
+ lastIndex = top.keywordPatternRe.lastIndex;
+ match = top.keywordPatternRe.exec(modeBuffer);
+ }
+ buf += modeBuffer.substr(lastIndex);
+ emitter.addText(buf);
+ }
+
+ function processSubLanguage() {
+ if (modeBuffer === "") return;
+ /** @type HighlightResult */
+ let result = null;
+
+ if (typeof top.subLanguage === 'string') {
+ if (!languages[top.subLanguage]) {
+ emitter.addText(modeBuffer);
+ return;
+ }
+ result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]);
+ continuations[top.subLanguage] = /** @type {CompiledMode} */ (result.top);
+ } else {
+ result = highlightAuto(modeBuffer, top.subLanguage.length ? top.subLanguage : null);
+ }
+
+ // Counting embedded language score towards the host language may be disabled
+ // with zeroing the containing mode relevance. Use case in point is Markdown that
+ // allows XML everywhere and makes every XML snippet to have a much larger Markdown
+ // score.
+ if (top.relevance > 0) {
+ relevance += result.relevance;
+ }
+ emitter.addSublanguage(result.emitter, result.language);
+ }
+
+ function processBuffer() {
+ if (top.subLanguage != null) {
+ processSubLanguage();
+ } else {
+ processKeywords();
+ }
+ modeBuffer = '';
+ }
+
+ /**
+ * @param {Mode} mode - new mode to start
+ */
+ function startNewMode(mode) {
+ if (mode.className) {
+ emitter.openNode(language.classNameAliases[mode.className] || mode.className);
+ }
+ top = Object.create(mode, { parent: { value: top } });
+ return top;
+ }
+
+ /**
+ * @param {CompiledMode } mode - the mode to potentially end
+ * @param {RegExpMatchArray} match - the latest match
+ * @param {string} matchPlusRemainder - match plus remainder of content
+ * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode
+ */
+ function endOfMode(mode, match, matchPlusRemainder) {
+ let matched = startsWith(mode.endRe, matchPlusRemainder);
+
+ if (matched) {
+ if (mode["on:end"]) {
+ const resp = new Response(mode);
+ mode["on:end"](match, resp);
+ if (resp.ignore) matched = false;
+ }
+
+ if (matched) {
+ while (mode.endsParent && mode.parent) {
+ mode = mode.parent;
+ }
+ return mode;
+ }
+ }
+ // even if on:end fires an `ignore` it's still possible
+ // that we might trigger the end node because of a parent mode
+ if (mode.endsWithParent) {
+ return endOfMode(mode.parent, match, matchPlusRemainder);
+ }
+ }
+
+ /**
+ * Handle matching but then ignoring a sequence of text
+ *
+ * @param {string} lexeme - string containing full match text
+ */
+ function doIgnore(lexeme) {
+ if (top.matcher.regexIndex === 0) {
+ // no more regexs to potentially match here, so we move the cursor forward one
+ // space
+ modeBuffer += lexeme[0];
+ return 1;
+ } else {
+ // no need to move the cursor, we still have additional regexes to try and
+ // match at this very spot
+ resumeScanAtSamePosition = true;
+ return 0;
+ }
+ }
+
+ /**
+ * Handle the start of a new potential mode match
+ *
+ * @param {EnhancedMatch} match - the current match
+ * @returns {number} how far to advance the parse cursor
+ */
+ function doBeginMatch(match) {
+ const lexeme = match[0];
+ const newMode = match.rule;
+
+ const resp = new Response(newMode);
+ // first internal before callbacks, then the public ones
+ const beforeCallbacks = [newMode.__beforeBegin, newMode["on:begin"]];
+ for (const cb of beforeCallbacks) {
+ if (!cb) continue;
+ cb(match, resp);
+ if (resp.ignore) return doIgnore(lexeme);
+ }
+
+ if (newMode && newMode.endSameAsBegin) {
+ newMode.endRe = escape$1(lexeme);
+ }
+
+ if (newMode.skip) {
+ modeBuffer += lexeme;
+ } else {
+ if (newMode.excludeBegin) {
+ modeBuffer += lexeme;
+ }
+ processBuffer();
+ if (!newMode.returnBegin && !newMode.excludeBegin) {
+ modeBuffer = lexeme;
+ }
+ }
+ startNewMode(newMode);
+ // if (mode["after:begin"]) {
+ // let resp = new Response(mode);
+ // mode["after:begin"](match, resp);
+ // }
+ return newMode.returnBegin ? 0 : lexeme.length;
+ }
+
+ /**
+ * Handle the potential end of mode
+ *
+ * @param {RegExpMatchArray} match - the current match
+ */
+ function doEndMatch(match) {
+ const lexeme = match[0];
+ const matchPlusRemainder = codeToHighlight.substr(match.index);
+
+ const endMode = endOfMode(top, match, matchPlusRemainder);
+ if (!endMode) { return NO_MATCH; }
+
+ const origin = top;
+ if (origin.skip) {
+ modeBuffer += lexeme;
+ } else {
+ if (!(origin.returnEnd || origin.excludeEnd)) {
+ modeBuffer += lexeme;
+ }
+ processBuffer();
+ if (origin.excludeEnd) {
+ modeBuffer = lexeme;
+ }
+ }
+ do {
+ if (top.className) {
+ emitter.closeNode();
+ }
+ if (!top.skip && !top.subLanguage) {
+ relevance += top.relevance;
+ }
+ top = top.parent;
+ } while (top !== endMode.parent);
+ if (endMode.starts) {
+ if (endMode.endSameAsBegin) {
+ endMode.starts.endRe = endMode.endRe;
+ }
+ startNewMode(endMode.starts);
+ }
+ return origin.returnEnd ? 0 : lexeme.length;
+ }
+
+ function processContinuations() {
+ const list = [];
+ for (let current = top; current !== language; current = current.parent) {
+ if (current.className) {
+ list.unshift(current.className);
+ }
+ }
+ list.forEach(item => emitter.openNode(item));
+ }
+
+ /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */
+ let lastMatch = {};
+
+ /**
+ * Process an individual match
+ *
+ * @param {string} textBeforeMatch - text preceeding the match (since the last match)
+ * @param {EnhancedMatch} [match] - the match itself
+ */
+ function processLexeme(textBeforeMatch, match) {
+ const lexeme = match && match[0];
+
+ // add non-matched text to the current mode buffer
+ modeBuffer += textBeforeMatch;
+
+ if (lexeme == null) {
+ processBuffer();
+ return 0;
+ }
+
+ // we've found a 0 width match and we're stuck, so we need to advance
+ // this happens when we have badly behaved rules that have optional matchers to the degree that
+ // sometimes they can end up matching nothing at all
+ // Ref: https://github.com/highlightjs/highlight.js/issues/2140
+ if (lastMatch.type === "begin" && match.type === "end" && lastMatch.index === match.index && lexeme === "") {
+ // spit the "skipped" character that our regex choked on back into the output sequence
+ modeBuffer += codeToHighlight.slice(match.index, match.index + 1);
+ if (!SAFE_MODE) {
+ /** @type {AnnotatedError} */
+ const err = new Error('0 width match regex');
+ err.languageName = languageName;
+ err.badRule = lastMatch.rule;
+ throw err;
+ }
+ return 1;
+ }
+ lastMatch = match;
+
+ if (match.type === "begin") {
+ return doBeginMatch(match);
+ } else if (match.type === "illegal" && !ignoreIllegals) {
+ // illegal match, we do not continue processing
+ /** @type {AnnotatedError} */
+ const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '') + '"');
+ err.mode = top;
+ throw err;
+ } else if (match.type === "end") {
+ const processed = doEndMatch(match);
+ if (processed !== NO_MATCH) {
+ return processed;
+ }
+ }
+
+ // edge case for when illegal matches $ (end of line) which is technically
+ // a 0 width match but not a begin/end match so it's not caught by the
+ // first handler (when ignoreIllegals is true)
+ if (match.type === "illegal" && lexeme === "") {
+ // advance so we aren't stuck in an infinite loop
+ return 1;
+ }
+
+ // infinite loops are BAD, this is a last ditch catch all. if we have a
+ // decent number of iterations yet our index (cursor position in our
+ // parsing) still 3x behind our index then something is very wrong
+ // so we bail
+ if (iterations > 100000 && iterations > match.index * 3) {
+ const err = new Error('potential infinite loop, way more iterations than matches');
+ throw err;
+ }
+
+ /*
+ Why might be find ourselves here? Only one occasion now. An end match that was
+ triggered but could not be completed. When might this happen? When an `endSameasBegin`
+ rule sets the end rule to a specific match. Since the overall mode termination rule that's
+ being used to scan the text isn't recompiled that means that any match that LOOKS like
+ the end (but is not, because it is not an exact match to the beginning) will
+ end up here. A definite end match, but when `doEndMatch` tries to "reapply"
+ the end rule and fails to match, we wind up here, and just silently ignore the end.
+
+ This causes no real harm other than stopping a few times too many.
+ */
+
+ modeBuffer += lexeme;
+ return lexeme.length;
+ }
+
+ const language = getLanguage(languageName);
+ if (!language) {
+ error(LANGUAGE_NOT_FOUND.replace("{}", languageName));
+ throw new Error('Unknown language: "' + languageName + '"');
+ }
+
+ const md = compileLanguage(language, { plugins });
+ let result = '';
+ /** @type {CompiledMode} */
+ let top = continuation || md;
+ /** @type Record */
+ const continuations = {}; // keep continuations for sub-languages
+ const emitter = new options.__emitter(options);
+ processContinuations();
+ let modeBuffer = '';
+ let relevance = 0;
+ let index = 0;
+ let iterations = 0;
+ let resumeScanAtSamePosition = false;
+
+ try {
+ top.matcher.considerAll();
+
+ for (;;) {
+ iterations++;
+ if (resumeScanAtSamePosition) {
+ // only regexes not matched previously will now be
+ // considered for a potential match
+ resumeScanAtSamePosition = false;
+ } else {
+ top.matcher.considerAll();
+ }
+ top.matcher.lastIndex = index;
+
+ const match = top.matcher.exec(codeToHighlight);
+ // console.log("match", match[0], match.rule && match.rule.begin)
+
+ if (!match) break;
+
+ const beforeMatch = codeToHighlight.substring(index, match.index);
+ const processedCount = processLexeme(beforeMatch, match);
+ index = match.index + processedCount;
+ }
+ processLexeme(codeToHighlight.substr(index));
+ emitter.closeAllNodes();
+ emitter.finalize();
+ result = emitter.toHTML();
+
+ return {
+ // avoid possible breakage with v10 clients expecting
+ // this to always be an integer
+ relevance: Math.floor(relevance),
+ value: result,
+ language: languageName,
+ illegal: false,
+ emitter: emitter,
+ top: top
+ };
+ } catch (err) {
+ if (err.message && err.message.includes('Illegal')) {
+ return {
+ illegal: true,
+ illegalBy: {
+ msg: err.message,
+ context: codeToHighlight.slice(index - 100, index + 100),
+ mode: err.mode
+ },
+ sofar: result,
+ relevance: 0,
+ value: escape(codeToHighlight),
+ emitter: emitter
+ };
+ } else if (SAFE_MODE) {
+ return {
+ illegal: false,
+ relevance: 0,
+ value: escape(codeToHighlight),
+ emitter: emitter,
+ language: languageName,
+ top: top,
+ errorRaised: err
+ };
+ } else {
+ throw err;
+ }
+ }
+ }
+
+ /**
+ * returns a valid highlight result, without actually doing any actual work,
+ * auto highlight starts with this and it's possible for small snippets that
+ * auto-detection may not find a better match
+ * @param {string} code
+ * @returns {HighlightResult}
+ */
+ function justTextHighlightResult(code) {
+ const result = {
+ relevance: 0,
+ emitter: new options.__emitter(options),
+ value: escape(code),
+ illegal: false,
+ top: PLAINTEXT_LANGUAGE
+ };
+ result.emitter.addText(code);
+ return result;
+ }
+
+ /**
+ Highlighting with language detection. Accepts a string with the code to
+ highlight. Returns an object with the following properties:
+
+ - language (detected language)
+ - relevance (int)
+ - value (an HTML string with highlighting markup)
+ - second_best (object with the same structure for second-best heuristically
+ detected language, may be absent)
+
+ @param {string} code
+ @param {Array} [languageSubset]
+ @returns {AutoHighlightResult}
+ */
+ function highlightAuto(code, languageSubset) {
+ languageSubset = languageSubset || options.languages || Object.keys(languages);
+ const plaintext = justTextHighlightResult(code);
+
+ const results = languageSubset.filter(getLanguage).filter(autoDetection).map(name =>
+ _highlight(name, code, false)
+ );
+ results.unshift(plaintext); // plaintext is always an option
+
+ const sorted = results.sort((a, b) => {
+ // sort base on relevance
+ if (a.relevance !== b.relevance) return b.relevance - a.relevance;
+
+ // always award the tie to the base language
+ // ie if C++ and Arduino are tied, it's more likely to be C++
+ if (a.language && b.language) {
+ if (getLanguage(a.language).supersetOf === b.language) {
+ return 1;
+ } else if (getLanguage(b.language).supersetOf === a.language) {
+ return -1;
+ }
+ }
+
+ // otherwise say they are equal, which has the effect of sorting on
+ // relevance while preserving the original ordering - which is how ties
+ // have historically been settled, ie the language that comes first always
+ // wins in the case of a tie
+ return 0;
+ });
+
+ const [best, secondBest] = sorted;
+
+ /** @type {AutoHighlightResult} */
+ const result = best;
+ result.second_best = secondBest;
+
+ return result;
+ }
+
+ /**
+ Post-processing of the highlighted markup:
+
+ - replace TABs with something more useful
+ - replace real line-breaks with '
' for non-pre containers
+
+ @param {string} html
+ @returns {string}
+ */
+ function fixMarkup(html) {
+ if (!(options.tabReplace || options.useBR)) {
+ return html;
+ }
+
+ return html.replace(fixMarkupRe, match => {
+ if (match === '\n') {
+ return options.useBR ? '
' : match;
+ } else if (options.tabReplace) {
+ return match.replace(/\t/g, options.tabReplace);
+ }
+ return match;
+ });
+ }
+
+ /**
+ * Builds new class name for block given the language name
+ *
+ * @param {HTMLElement} element
+ * @param {string} [currentLang]
+ * @param {string} [resultLang]
+ */
+ function updateClassName(element, currentLang, resultLang) {
+ const language = currentLang ? aliases[currentLang] : resultLang;
+
+ element.classList.add("hljs");
+ if (language) element.classList.add(language);
+ }
+
+ /** @type {HLJSPlugin} */
+ const brPlugin = {
+ "before:highlightElement": ({ el }) => {
+ if (options.useBR) {
+ el.innerHTML = el.innerHTML.replace(/\n/g, '').replace(/
/g, '\n');
+ }
+ },
+ "after:highlightElement": ({ result }) => {
+ if (options.useBR) {
+ result.value = result.value.replace(/\n/g, "
");
+ }
+ }
+ };
+
+ const TAB_REPLACE_RE = /^(<[^>]+>|\t)+/gm;
+ /** @type {HLJSPlugin} */
+ const tabReplacePlugin = {
+ "after:highlightElement": ({ result }) => {
+ if (options.tabReplace) {
+ result.value = result.value.replace(TAB_REPLACE_RE, (m) =>
+ m.replace(/\t/g, options.tabReplace)
+ );
+ }
+ }
+ };
+
+ /**
+ * Applies highlighting to a DOM node containing code. Accepts a DOM node and
+ * two optional parameters for fixMarkup.
+ *
+ * @param {HighlightedHTMLElement} element - the HTML element to highlight
+ */
+ function highlightElement(element) {
+ /** @type HTMLElement */
+ let node = null;
+ const language = blockLanguage(element);
+
+ if (shouldNotHighlight(language)) return;
+
+ // support for v10 API
+ fire("before:highlightElement",
+ { el: element, language: language });
+
+ node = element;
+ const text = node.textContent;
+ const result = language ? highlight(language, text, true) : highlightAuto(text);
+
+ // support for v10 API
+ fire("after:highlightElement", { el: element, result, text });
+
+ element.innerHTML = result.value;
+ updateClassName(element, language, result.language);
+ element.result = {
+ language: result.language,
+ // TODO: remove with version 11.0
+ re: result.relevance,
+ relavance: result.relevance
+ };
+ if (result.second_best) {
+ element.second_best = {
+ language: result.second_best.language,
+ // TODO: remove with version 11.0
+ re: result.second_best.relevance,
+ relavance: result.second_best.relevance
+ };
+ }
+ }
+
+ /**
+ * Updates highlight.js global options with the passed options
+ *
+ * @param {Partial} userOptions
+ */
+ function configure(userOptions) {
+ if (userOptions.useBR) {
+ deprecated("10.3.0", "'useBR' will be removed entirely in v11.0");
+ deprecated("10.3.0", "Please see https://github.com/highlightjs/highlight.js/issues/2559");
+ }
+ options = inherit(options, userOptions);
+ }
+
+ /**
+ * Highlights to all blocks on a page
+ *
+ * @type {Function & {called?: boolean}}
+ */
+ // TODO: remove v12, deprecated
+ const initHighlighting = () => {
+ if (initHighlighting.called) return;
+ initHighlighting.called = true;
+
+ deprecated("10.6.0", "initHighlighting() is deprecated. Use highlightAll() instead.");
+
+ const blocks = document.querySelectorAll('pre code');
+ blocks.forEach(highlightElement);
+ };
+
+ // Higlights all when DOMContentLoaded fires
+ // TODO: remove v12, deprecated
+ function initHighlightingOnLoad() {
+ deprecated("10.6.0", "initHighlightingOnLoad() is deprecated. Use highlightAll() instead.");
+ wantsHighlight = true;
+ }
+
+ let wantsHighlight = false;
+
+ /**
+ * auto-highlights all pre>code elements on the page
+ */
+ function highlightAll() {
+ // if we are called too early in the loading process
+ if (document.readyState === "loading") {
+ wantsHighlight = true;
+ return;
+ }
+
+ const blocks = document.querySelectorAll('pre code');
+ blocks.forEach(highlightElement);
+ }
+
+ function boot() {
+ // if a highlight was requested before DOM was loaded, do now
+ if (wantsHighlight) highlightAll();
+ }
+
+ // make sure we are in the browser environment
+ if (typeof window !== 'undefined' && window.addEventListener) {
+ window.addEventListener('DOMContentLoaded', boot, false);
+ }
+
+ /**
+ * Register a language grammar module
+ *
+ * @param {string} languageName
+ * @param {LanguageFn} languageDefinition
+ */
+ function registerLanguage(languageName, languageDefinition) {
+ let lang = null;
+ try {
+ lang = languageDefinition(hljs);
+ } catch (error$1) {
+ error("Language definition for '{}' could not be registered.".replace("{}", languageName));
+ // hard or soft error
+ if (!SAFE_MODE) { throw error$1; } else { error(error$1); }
+ // languages that have serious errors are replaced with essentially a
+ // "plaintext" stand-in so that the code blocks will still get normal
+ // css classes applied to them - and one bad language won't break the
+ // entire highlighter
+ lang = PLAINTEXT_LANGUAGE;
+ }
+ // give it a temporary name if it doesn't have one in the meta-data
+ if (!lang.name) lang.name = languageName;
+ languages[languageName] = lang;
+ lang.rawDefinition = languageDefinition.bind(null, hljs);
+
+ if (lang.aliases) {
+ registerAliases(lang.aliases, { languageName });
+ }
+ }
+
+ /**
+ * Remove a language grammar module
+ *
+ * @param {string} languageName
+ */
+ function unregisterLanguage(languageName) {
+ delete languages[languageName];
+ for (const alias of Object.keys(aliases)) {
+ if (aliases[alias] === languageName) {
+ delete aliases[alias];
+ }
+ }
+ }
+
+ /**
+ * @returns {string[]} List of language internal names
+ */
+ function listLanguages() {
+ return Object.keys(languages);
+ }
+
+ /**
+ intended usage: When one language truly requires another
+
+ Unlike `getLanguage`, this will throw when the requested language
+ is not available.
+
+ @param {string} name - name of the language to fetch/require
+ @returns {Language | never}
+ */
+ function requireLanguage(name) {
+ deprecated("10.4.0", "requireLanguage will be removed entirely in v11.");
+ deprecated("10.4.0", "Please see https://github.com/highlightjs/highlight.js/pull/2844");
+
+ const lang = getLanguage(name);
+ if (lang) { return lang; }
+
+ const err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}', name));
+ throw err;
+ }
+
+ /**
+ * @param {string} name - name of the language to retrieve
+ * @returns {Language | undefined}
+ */
+ function getLanguage(name) {
+ name = (name || '').toLowerCase();
+ return languages[name] || languages[aliases[name]];
+ }
+
+ /**
+ *
+ * @param {string|string[]} aliasList - single alias or list of aliases
+ * @param {{languageName: string}} opts
+ */
+ function registerAliases(aliasList, { languageName }) {
+ if (typeof aliasList === 'string') {
+ aliasList = [aliasList];
+ }
+ aliasList.forEach(alias => { aliases[alias.toLowerCase()] = languageName; });
+ }
+
+ /**
+ * Determines if a given language has auto-detection enabled
+ * @param {string} name - name of the language
+ */
+ function autoDetection(name) {
+ const lang = getLanguage(name);
+ return lang && !lang.disableAutodetect;
+ }
+
+ /**
+ * Upgrades the old highlightBlock plugins to the new
+ * highlightElement API
+ * @param {HLJSPlugin} plugin
+ */
+ function upgradePluginAPI(plugin) {
+ // TODO: remove with v12
+ if (plugin["before:highlightBlock"] && !plugin["before:highlightElement"]) {
+ plugin["before:highlightElement"] = (data) => {
+ plugin["before:highlightBlock"](
+ Object.assign({ block: data.el }, data)
+ );
+ };
+ }
+ if (plugin["after:highlightBlock"] && !plugin["after:highlightElement"]) {
+ plugin["after:highlightElement"] = (data) => {
+ plugin["after:highlightBlock"](
+ Object.assign({ block: data.el }, data)
+ );
+ };
+ }
+ }
+
+ /**
+ * @param {HLJSPlugin} plugin
+ */
+ function addPlugin(plugin) {
+ upgradePluginAPI(plugin);
+ plugins.push(plugin);
+ }
+
+ /**
+ *
+ * @param {PluginEvent} event
+ * @param {any} args
+ */
+ function fire(event, args) {
+ const cb = event;
+ plugins.forEach(function(plugin) {
+ if (plugin[cb]) {
+ plugin[cb](args);
+ }
+ });
+ }
+
+ /**
+ Note: fixMarkup is deprecated and will be removed entirely in v11
+
+ @param {string} arg
+ @returns {string}
+ */
+ function deprecateFixMarkup(arg) {
+ deprecated("10.2.0", "fixMarkup will be removed entirely in v11.0");
+ deprecated("10.2.0", "Please see https://github.com/highlightjs/highlight.js/issues/2534");
+
+ return fixMarkup(arg);
+ }
+
+ /**
+ *
+ * @param {HighlightedHTMLElement} el
+ */
+ function deprecateHighlightBlock(el) {
+ deprecated("10.7.0", "highlightBlock will be removed entirely in v12.0");
+ deprecated("10.7.0", "Please use highlightElement now.");
+
+ return highlightElement(el);
+ }
+
+ /* Interface definition */
+ Object.assign(hljs, {
+ highlight,
+ highlightAuto,
+ highlightAll,
+ fixMarkup: deprecateFixMarkup,
+ highlightElement,
+ // TODO: Remove with v12 API
+ highlightBlock: deprecateHighlightBlock,
+ configure,
+ initHighlighting,
+ initHighlightingOnLoad,
+ registerLanguage,
+ unregisterLanguage,
+ listLanguages,
+ getLanguage,
+ registerAliases,
+ requireLanguage,
+ autoDetection,
+ inherit,
+ addPlugin,
+ // plugins for frameworks
+ vuePlugin: BuildVuePlugin(hljs).VuePlugin
+ });
+
+ hljs.debugMode = function() { SAFE_MODE = false; };
+ hljs.safeMode = function() { SAFE_MODE = true; };
+ hljs.versionString = version;
+
+ for (const key in MODES) {
+ // @ts-ignore
+ if (typeof MODES[key] === "object") {
+ // @ts-ignore
+ deepFreezeEs6(MODES[key]);
+ }
+ }
+
+ // merge all the modes/regexs into our main object
+ Object.assign(hljs, MODES);
+
+ // built-in plugins, likely to be moved out of core in the future
+ hljs.addPlugin(brPlugin); // slated to be removed in v11
+ hljs.addPlugin(mergeHTMLPlugin);
+ hljs.addPlugin(tabReplacePlugin);
+ return hljs;
+ };
+
+ // export an "instance" of the highlighter
+ var highlight = HLJS({});
+
+ return highlight;
}());
if (typeof exports === 'object' && typeof module !== 'undefined') { module.exports = hljs; }
diff --git a/vendor/assets/javascripts/highlightjs/highlight.min.js b/vendor/assets/javascripts/highlightjs/highlight.min.js
index 261ae84b3e4..9b9dd12ee19 100644
--- a/vendor/assets/javascripts/highlightjs/highlight.min.js
+++ b/vendor/assets/javascripts/highlightjs/highlight.min.js
@@ -1,6 +1,304 @@
/*
- Highlight.js 10.2.1 (fc4910b5)
+ Highlight.js 10.6.0 (7f3240ea)
License: BSD-3-Clause
- Copyright (c) 2006-2020, Ivan Sagalaev
+ Copyright (c) 2006-2021, Ivan Sagalaev
*/
-var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""+a(e)+">"}function g(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var d=l();if(s+=t(r.substring(i,d[0].offset)),i=d[0].offset,d===e){o.reverse().forEach(u);do{g(d.splice(0,1)[0]),d=l()}while(d===e&&d.length&&d[0].offset===i);o.reverse().forEach(c)}else"start"===d[0].event?o.push(d[0].node):o.pop(),g(d.splice(0,1)[0])}return s+t(r.substr(i))}});const s=e=>!!e.kind;class o{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!s(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){s(e)&&(this.buffer+="