mirror of
				https://github.com/JKorf/CryptoExchange.Net
				synced 2025-10-31 02:17:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			751 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			751 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| Syntax highlighting with language autodetection.
 | |
| https://highlightjs.org/
 | |
| */
 | |
| 
 | |
| import deepFreeze from './vendor/deep_freeze';
 | |
| import Response from './lib/response';
 | |
| import TokenTreeEmitter from './lib/token_tree';
 | |
| import * as regex from './lib/regex';
 | |
| import * as utils from './lib/utils';
 | |
| import * as MODES from './lib/modes';
 | |
| import { compileLanguage } from './lib/mode_compiler';
 | |
| import * as packageJSON from '../package.json';
 | |
| 
 | |
| const escape = utils.escapeHTML;
 | |
| const inherit = utils.inherit;
 | |
| 
 | |
| const { nodeStream, mergeStreams } = utils;
 | |
| const NO_MATCH = Symbol("nomatch");
 | |
| 
 | |
| const HLJS = function(hljs) {
 | |
|   // Convenience variables for build-in objects
 | |
|   var ArrayProto = [];
 | |
| 
 | |
|   // Global internal variables used within the highlight.js library.
 | |
|   var languages = {};
 | |
|   var aliases = {};
 | |
|   var plugins = [];
 | |
| 
 | |
|   // safe/production mode - swallows more errors, tries to keep running
 | |
|   // even if a single syntax or parse hits a fatal error
 | |
|   var SAFE_MODE = true;
 | |
| 
 | |
|   // Regular expressions used throughout the highlight.js library.
 | |
|   var fixMarkupRe = /(^(<[^>]+>|\t|)+|\n)/gm;
 | |
| 
 | |
|   var LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?";
 | |
| 
 | |
|   // Global options used when within external APIs. This is modified when
 | |
|   // calling the `hljs.configure` function.
 | |
|   var 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 */
 | |
| 
 | |
|   function shouldNotHighlight(language) {
 | |
|     return options.noHighlightRe.test(language);
 | |
|   }
 | |
| 
 | |
|   function blockLanguage(block) {
 | |
|     var classes = block.className + ' ';
 | |
| 
 | |
|     classes += block.parentNode ? block.parentNode.className : '';
 | |
| 
 | |
|     // language-* takes precedence over non-prefixed class names.
 | |
|     const match = options.languageDetectRe.exec(classes);
 | |
|     if (match) {
 | |
|       var language = getLanguage(match[1]);
 | |
|       if (!language) {
 | |
|         console.warn(LANGUAGE_NOT_FOUND.replace("{}", match[1]));
 | |
|         console.warn("Falling back to no-highlight mode for this block.", block);
 | |
|       }
 | |
|       return language ? match[1] : 'no-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 {array<mode>} continuation - array of continuation modes
 | |
|    *
 | |
|    * @returns 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 {mode} top - top of the current mode stack
 | |
|    * @property {boolean} illegal - indicates whether any illegal matches were found
 | |
|   */
 | |
|   function highlight(languageName, code, ignoreIllegals, continuation) {
 | |
|     var 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
 | |
|     var 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
 | |
|   function _highlight(languageName, code, ignoreIllegals, continuation) {
 | |
|     var codeToHighlight = code;
 | |
| 
 | |
|     function keywordData(mode, match) {
 | |
|       var 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(mode_buffer);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       let last_index = 0;
 | |
|       top.keywordPatternRe.lastIndex = 0;
 | |
|       let match = top.keywordPatternRe.exec(mode_buffer);
 | |
|       let buf = "";
 | |
| 
 | |
|       while (match) {
 | |
|         buf += mode_buffer.substring(last_index, match.index);
 | |
|         const data = keywordData(top, match);
 | |
|         if (data) {
 | |
|           const [kind, keywordRelevance] = data;
 | |
|           emitter.addText(buf);
 | |
|           buf = "";
 | |
| 
 | |
|           relevance += keywordRelevance;
 | |
|           emitter.addKeyword(match[0], kind);
 | |
|         } else {
 | |
|           buf += match[0];
 | |
|         }
 | |
|         last_index = top.keywordPatternRe.lastIndex;
 | |
|         match = top.keywordPatternRe.exec(mode_buffer);
 | |
|       }
 | |
|       buf += mode_buffer.substr(last_index);
 | |
|       emitter.addText(buf);
 | |
|     }
 | |
| 
 | |
|     function processSubLanguage() {
 | |
|       if (mode_buffer === "") return;
 | |
| 
 | |
|       var explicit = typeof top.subLanguage === 'string';
 | |
| 
 | |
|       if (explicit && !languages[top.subLanguage]) {
 | |
|         emitter.addText(mode_buffer);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       var result = explicit ?
 | |
|         _highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]) :
 | |
|         highlightAuto(mode_buffer, 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;
 | |
|       }
 | |
|       if (explicit) {
 | |
|         continuations[top.subLanguage] = result.top;
 | |
|       }
 | |
|       emitter.addSublanguage(result.emitter, result.language);
 | |
|     }
 | |
| 
 | |
|     function processBuffer() {
 | |
|       if (top.subLanguage != null) {
 | |
|         processSubLanguage();
 | |
|       } else {
 | |
|         processKeywords();
 | |
|       }
 | |
|       mode_buffer = '';
 | |
|     }
 | |
| 
 | |
|     function startNewMode(mode) {
 | |
|       if (mode.className) {
 | |
|         emitter.openNode(mode.className);
 | |
|       }
 | |
|       top = Object.create(mode, {parent: {value: top}});
 | |
|       return top;
 | |
|     }
 | |
| 
 | |
|     function endOfMode(mode, match, matchPlusRemainder) {
 | |
|       let matched = regex.startsWith(mode.endRe, matchPlusRemainder);
 | |
| 
 | |
|       if (matched) {
 | |
|         if (mode["on:end"]) {
 | |
|           let 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);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     function doIgnore(lexeme) {
 | |
|       if (top.matcher.regexIndex === 0) {
 | |
|         // no more regexs to potentially match here, so we move the cursor forward one
 | |
|         // space
 | |
|         mode_buffer += lexeme[0];
 | |
|         return 1;
 | |
|       } else {
 | |
|         // no need to move the cursor, we still have additional regexes to try and
 | |
|         // match at this very spot
 | |
|         continueScanAtSamePosition = true;
 | |
|         return 0;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     function doBeginMatch(match) {
 | |
|       var lexeme = match[0];
 | |
|       var new_mode = match.rule;
 | |
|       var mode;
 | |
| 
 | |
|       let resp = new Response(new_mode);
 | |
|       // first internal before callbacks, then the public ones
 | |
|       let beforeCallbacks = [new_mode.__beforeBegin, new_mode["on:begin"]];
 | |
|       for (let cb of beforeCallbacks) {
 | |
|         if (!cb) continue;
 | |
|         cb(match, resp);
 | |
|         if (resp.ignore) return doIgnore(lexeme);
 | |
|       }
 | |
| 
 | |
|       if (new_mode && new_mode.endSameAsBegin) {
 | |
|         new_mode.endRe = regex.escape(lexeme);
 | |
|       }
 | |
| 
 | |
|       if (new_mode.skip) {
 | |
|         mode_buffer += lexeme;
 | |
|       } else {
 | |
|         if (new_mode.excludeBegin) {
 | |
|           mode_buffer += lexeme;
 | |
|         }
 | |
|         processBuffer();
 | |
|         if (!new_mode.returnBegin && !new_mode.excludeBegin) {
 | |
|           mode_buffer = lexeme;
 | |
|         }
 | |
|       }
 | |
|       mode = startNewMode(new_mode);
 | |
|       // if (mode["after:begin"]) {
 | |
|       //   let resp = new Response(mode);
 | |
|       //   mode["after:begin"](match, resp);
 | |
|       // }
 | |
|       return new_mode.returnBegin ? 0 : lexeme.length;
 | |
|     }
 | |
| 
 | |
|     function doEndMatch(match) {
 | |
|       var lexeme = match[0];
 | |
|       var matchPlusRemainder = codeToHighlight.substr(match.index);
 | |
| 
 | |
|       var end_mode = endOfMode(top, match, matchPlusRemainder);
 | |
|       if (!end_mode) { return NO_MATCH; }
 | |
| 
 | |
|       var origin = top;
 | |
|       if (origin.skip) {
 | |
|         mode_buffer += lexeme;
 | |
|       } else {
 | |
|         if (!(origin.returnEnd || origin.excludeEnd)) {
 | |
|           mode_buffer += lexeme;
 | |
|         }
 | |
|         processBuffer();
 | |
|         if (origin.excludeEnd) {
 | |
|           mode_buffer = lexeme;
 | |
|         }
 | |
|       }
 | |
|       do {
 | |
|         if (top.className) {
 | |
|           emitter.closeNode();
 | |
|         }
 | |
|         if (!top.skip && !top.subLanguage) {
 | |
|           relevance += top.relevance;
 | |
|         }
 | |
|         top = top.parent;
 | |
|       } while (top !== end_mode.parent);
 | |
|       if (end_mode.starts) {
 | |
|         if (end_mode.endSameAsBegin) {
 | |
|           end_mode.starts.endRe = end_mode.endRe;
 | |
|         }
 | |
|         startNewMode(end_mode.starts);
 | |
|       }
 | |
|       return origin.returnEnd ? 0 : lexeme.length;
 | |
|     }
 | |
| 
 | |
|     function processContinuations() {
 | |
|       var list = [];
 | |
|       for (var current = top; current !== language; current = current.parent) {
 | |
|         if (current.className) {
 | |
|           list.unshift(current.className);
 | |
|         }
 | |
|       }
 | |
|       list.forEach(item => emitter.openNode(item));
 | |
|     }
 | |
| 
 | |
|     var lastMatch = {};
 | |
|     function processLexeme(textBeforeMatch, match) {
 | |
|       var lexeme = match && match[0];
 | |
| 
 | |
|       // add non-matched text to the current mode buffer
 | |
|       mode_buffer += 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
 | |
|         mode_buffer += codeToHighlight.slice(match.index, match.index + 1);
 | |
|         if (!SAFE_MODE) {
 | |
|           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
 | |
|         const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '<unnamed>') + '"');
 | |
|         err.mode = top;
 | |
|         throw err;
 | |
|       } else if (match.type === "end") {
 | |
|         var 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.
 | |
|       */
 | |
| 
 | |
|       mode_buffer += lexeme;
 | |
|       return lexeme.length;
 | |
|     }
 | |
| 
 | |
|     var language = getLanguage(languageName);
 | |
|     if (!language) {
 | |
|       console.error(LANGUAGE_NOT_FOUND.replace("{}", languageName));
 | |
|       throw new Error('Unknown language: "' + languageName + '"');
 | |
|     }
 | |
| 
 | |
|     compileLanguage(language);
 | |
|     var result = '';
 | |
|     var top = continuation || language;
 | |
|     var continuations = {}; // keep continuations for sub-languages
 | |
|     var emitter = new options.__emitter(options);
 | |
|     processContinuations();
 | |
|     var mode_buffer = '';
 | |
|     var relevance = 0;
 | |
|     var index = 0;
 | |
|     var iterations = 0;
 | |
|     var continueScanAtSamePosition = false;
 | |
| 
 | |
|     try {
 | |
|       top.matcher.considerAll();
 | |
| 
 | |
|       for (;;) {
 | |
|         iterations++;
 | |
|         if (continueScanAtSamePosition) {
 | |
|           continueScanAtSamePosition = false;
 | |
|           // only regexes not matched previously will now be
 | |
|           // considered for a potential match
 | |
|         } else {
 | |
|           top.matcher.lastIndex = index;
 | |
|           top.matcher.considerAll();
 | |
|         }
 | |
|         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 {
 | |
|         relevance: 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 {
 | |
|           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
 | |
|   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)
 | |
| 
 | |
|   */
 | |
|   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 '<br>' for non-pre containers
 | |
| 
 | |
|   */
 | |
|   function fixMarkup(value) {
 | |
|     if (!(options.tabReplace || options.useBR)) {
 | |
|       return value;
 | |
|     }
 | |
| 
 | |
|     return value.replace(fixMarkupRe, match => {
 | |
|       if (match === '\n') {
 | |
|         return options.useBR ? '<br>' : match;
 | |
|       } else if (options.tabReplace) {
 | |
|         return match.replace(/\t/g, options.tabReplace);
 | |
|       }
 | |
|       return match;
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   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.
 | |
|   */
 | |
|   function highlightBlock(block) {
 | |
|     let node = null;
 | |
|     const language = blockLanguage(block);
 | |
| 
 | |
|     if (shouldNotHighlight(language)) return;
 | |
| 
 | |
|     fire("before:highlightBlock",
 | |
|       { block: block, language: language });
 | |
| 
 | |
|     if (options.useBR) {
 | |
|       node = document.createElement('div');
 | |
|       node.innerHTML = block.innerHTML.replace(/\n/g, '').replace(/<br[ /]*>/g, '\n');
 | |
|     } else {
 | |
|       node = block;
 | |
|     }
 | |
|     const text = node.textContent;
 | |
|     const result = language ? highlight(language, text, true) : highlightAuto(text);
 | |
| 
 | |
|     const originalStream = nodeStream(node);
 | |
|     if (originalStream.length) {
 | |
|       const resultNode = document.createElement('div');
 | |
|       resultNode.innerHTML = result.value;
 | |
|       result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
 | |
|     }
 | |
|     result.value = fixMarkup(result.value);
 | |
| 
 | |
|     fire("after:highlightBlock", { block: block, result: result });
 | |
| 
 | |
|     block.innerHTML = result.value;
 | |
|     block.className = buildClassName(block.className, language, result.language);
 | |
|     block.result = {
 | |
|       language: result.language,
 | |
|       re: result.relevance
 | |
|     };
 | |
|     if (result.second_best) {
 | |
|       block.second_best = {
 | |
|         language: result.second_best.language,
 | |
|         re: result.second_best.relevance
 | |
|       };
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|   Updates highlight.js global options with values passed in the form of an object.
 | |
|   */
 | |
|   function configure(userOptions) {
 | |
|     options = inherit(options, userOptions);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|   Applies highlighting to all <pre><code>..</code></pre> blocks on a page.
 | |
|   */
 | |
|   function initHighlighting() {
 | |
|     if (initHighlighting.called) return;
 | |
|     initHighlighting.called = true;
 | |
| 
 | |
|     var blocks = document.querySelectorAll('pre code');
 | |
|     ArrayProto.forEach.call(blocks, highlightBlock);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|   Attaches highlighting to the page load event.
 | |
|   */
 | |
|   function initHighlightingOnLoad() {
 | |
|     window.addEventListener('DOMContentLoaded', initHighlighting, false);
 | |
|   }
 | |
| 
 | |
|   const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text' };
 | |
| 
 | |
|   function registerLanguage(name, language) {
 | |
|     var lang = null;
 | |
|     try {
 | |
|       lang = language(hljs);
 | |
|     } catch (error) {
 | |
|       console.error("Language definition for '{}' could not be registered.".replace("{}", name));
 | |
|       // 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 = name;
 | |
|     languages[name] = lang;
 | |
|     lang.rawDefinition = language.bind(null, hljs);
 | |
| 
 | |
|     if (lang.aliases) {
 | |
|       registerAliases(lang.aliases, { languageName: name });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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.
 | |
|   */
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   function getLanguage(name) {
 | |
|     name = (name || '').toLowerCase();
 | |
|     return languages[name] || languages[aliases[name]];
 | |
|   }
 | |
| 
 | |
|   function registerAliases(aliasList, {languageName}) {
 | |
|     if (typeof aliasList === 'string') {
 | |
|       aliasList = [aliasList]
 | |
|     }
 | |
|     aliasList.forEach(alias => aliases[alias] = languageName);
 | |
|   }
 | |
| 
 | |
|   function autoDetection(name) {
 | |
|     var lang = getLanguage(name);
 | |
|     return lang && !lang.disableAutodetect;
 | |
|   }
 | |
| 
 | |
|   function addPlugin(plugin) {
 | |
|     plugins.push(plugin);
 | |
|   }
 | |
| 
 | |
|   function fire(event, args) {
 | |
|     var cb = event;
 | |
|     plugins.forEach(function(plugin) {
 | |
|       if (plugin[cb]) {
 | |
|         plugin[cb](args);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /* Interface definition */
 | |
| 
 | |
|   Object.assign(hljs, {
 | |
|     highlight,
 | |
|     highlightAuto,
 | |
|     fixMarkup,
 | |
|     highlightBlock,
 | |
|     configure,
 | |
|     initHighlighting,
 | |
|     initHighlightingOnLoad,
 | |
|     registerLanguage,
 | |
|     listLanguages,
 | |
|     getLanguage,
 | |
|     registerAliases,
 | |
|     requireLanguage,
 | |
|     autoDetection,
 | |
|     inherit,
 | |
|     addPlugin
 | |
|   });
 | |
| 
 | |
|   hljs.debugMode = function() { SAFE_MODE = false; };
 | |
|   hljs.safeMode = function() { SAFE_MODE = true; };
 | |
|   hljs.versionString = packageJSON.version;
 | |
| 
 | |
|   for (const key in MODES) {
 | |
|     if (typeof MODES[key] === "object") {
 | |
|       deepFreeze(MODES[key]);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // merge all the modes/regexs into our main object
 | |
|   Object.assign(hljs, MODES);
 | |
| 
 | |
|   return hljs;
 | |
| };
 | |
| 
 | |
| // export an "instance" of the highlighter
 | |
| export default HLJS({});
 |