[devdocsgjs/main: 1671/1867] LaTeX: syntax highlighting




commit f139802767b0eee88deb309241e985e524c77e95
Author: Simon Legner <Simon Legner gmail com>
Date:   Wed May 26 21:13:38 2021 +0200

    LaTeX: syntax highlighting

 assets/javascripts/vendor/prism.js   | 2941 +++++++++++++++++++---------------
 lib/docs/filters/latex/clean_html.rb |    5 +
 2 files changed, 1647 insertions(+), 1299 deletions(-)
---
diff --git a/assets/javascripts/vendor/prism.js b/assets/javascripts/vendor/prism.js
index 2f830fe4..8fc99771 100644
--- a/assets/javascripts/vendor/prism.js
+++ b/assets/javascripts/vendor/prism.js
@@ -1,13 +1,13 @@
 /* PrismJS 1.23.0
-https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+cpp+coffeescript+crystal+d+dart+diff+django+elixir+erlang+go+groovy+java+json+julia+kotlin+lua+markup-templating+matlab+nginx+nim+ocaml+perl+php+python+jsx+ruby+rust+scss+shell-session+sql+typescript+yaml
 */
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+c+cpp+coffeescript+crystal+d+dart+diff+django+elixir+erlang+go+groovy+java+json+julia+kotlin+latex+lua+markup-templating+matlab+nginx+nim+ocaml+perl+php+python+jsx+ruby+rust+scss+shell-session+sql+typescript+yaml
 */
 /// <reference lib="WebWorker"/>
 
 var _self = (typeof window !== 'undefined')
        ? window   // if in browser
        : (
                (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
-               ? self // if in worker
-               : {}   // if in node js
+                       ? self // if in worker
+                       : {}   // if in node js
        );
 
 /**
@@ -18,1133 +18,1151 @@ var _self = (typeof window !== 'undefined')
  * @namespace
  * @public
  */
-var Prism = (function (_self){
+var Prism = (function (_self) {
 
-// Private helper vars
-var lang = /\blang(?:uage)?-([\w-]+)\b/i;
-var uniqueId = 0;
+       // Private helper vars
+       var lang = /\blang(?:uage)?-([\w-]+)\b/i;
+       var uniqueId = 0;
 
+       // The grammar object for plaintext
+       var plainTextGrammar = {};
 
-var _ = {
-       /**
-        * By default, Prism will attempt to highlight all code elements (by calling {@link 
Prism.highlightAll}) on the
-        * current page after the page finished loading. This might be a problem if e.g. you wanted to 
asynchronously load
-        * additional languages or plugins yourself.
-        *
-        * By setting this value to `true`, Prism will not automatically highlight all code elements on the 
page.
-        *
-        * You obviously have to change this value before the automatic highlighting started. To do this, you 
can add an
-        * empty Prism object into the global scope before loading the Prism script like this:
-        *
-        * ```js
-        * window.Prism = window.Prism || {};
-        * Prism.manual = true;
-        * // add a new <script> to load Prism's script
-        * ```
-        *
-        * @default false
-        * @type {boolean}
-        * @memberof Prism
-        * @public
-        */
-       manual: _self.Prism && _self.Prism.manual,
-       disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
-
-       /**
-        * A namespace for utility methods.
-        *
-        * All function in this namespace that are not explicitly marked as _public_ are for __internal use 
only__ and may
-        * change or disappear at any time.
-        *
-        * @namespace
-        * @memberof Prism
-        */
-       util: {
-               encode: function encode(tokens) {
-                       if (tokens instanceof Token) {
-                               return new Token(tokens.type, encode(tokens.content), tokens.alias);
-                       } else if (Array.isArray(tokens)) {
-                               return tokens.map(encode);
-                       } else {
-                               return tokens.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, 
' ');
-                       }
-               },
 
+       var _ = {
                /**
-                * Returns the name of the type of the given value.
+                * By default, Prism will attempt to highlight all code elements (by calling {@link 
Prism.highlightAll}) on the
+                * current page after the page finished loading. This might be a problem if e.g. you wanted 
to asynchronously load
+                * additional languages or plugins yourself.
                 *
-                * @param {any} o
-                * @returns {string}
-                * @example
-                * type(null)      === 'Null'
-                * type(undefined) === 'Undefined'
-                * type(123)       === 'Number'
-                * type('foo')     === 'String'
-                * type(true)      === 'Boolean'
-                * type([1, 2])    === 'Array'
-                * type({})        === 'Object'
-                * type(String)    === 'Function'
-                * type(/abc+/)    === 'RegExp'
-                */
-               type: function (o) {
-                       return Object.prototype.toString.call(o).slice(8, -1);
-               },
-
-               /**
-                * Returns a unique number for the given object. Later calls will still return the same 
number.
+                * By setting this value to `true`, Prism will not automatically highlight all code elements 
on the page.
+                *
+                * You obviously have to change this value before the automatic highlighting started. To do 
this, you can add an
+                * empty Prism object into the global scope before loading the Prism script like this:
                 *
-                * @param {Object} obj
-                * @returns {number}
+                * ```js
+                * window.Prism = window.Prism || {};
+                * Prism.manual = true;
+                * // add a new <script> to load Prism's script
+                * ```
+                *
+                * @default false
+                * @type {boolean}
+                * @memberof Prism
+                * @public
                 */
-               objId: function (obj) {
-                       if (!obj['__id']) {
-                               Object.defineProperty(obj, '__id', { value: ++uniqueId });
-                       }
-                       return obj['__id'];
-               },
+               manual: _self.Prism && _self.Prism.manual,
+               disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
 
                /**
-                * Creates a deep clone of the given object.
+                * A namespace for utility methods.
                 *
-                * The main intended use of this function is to clone language definitions.
+                * All function in this namespace that are not explicitly marked as _public_ are for 
__internal use only__ and may
+                * change or disappear at any time.
                 *
-                * @param {T} o
-                * @param {Record<number, any>} [visited]
-                * @returns {T}
-                * @template T
+                * @namespace
+                * @memberof Prism
                 */
-               clone: function deepClone(o, visited) {
-                       visited = visited || {};
-
-                       var clone, id;
-                       switch (_.util.type(o)) {
-                               case 'Object':
-                                       id = _.util.objId(o);
-                                       if (visited[id]) {
-                                               return visited[id];
-                                       }
-                                       clone = /** @type {Record<string, any>} */ ({});
-                                       visited[id] = clone;
+               util: {
+                       encode: function encode(tokens) {
+                               if (tokens instanceof Token) {
+                                       return new Token(tokens.type, encode(tokens.content), tokens.alias);
+                               } else if (Array.isArray(tokens)) {
+                                       return tokens.map(encode);
+                               } else {
+                                       return tokens.replace(/&/g, '&amp;').replace(/</g, 
'&lt;').replace(/\u00a0/g, ' ');
+                               }
+                       },
+
+                       /**
+                        * Returns the name of the type of the given value.
+                        *
+                        * @param {any} o
+                        * @returns {string}
+                        * @example
+                        * type(null)      === 'Null'
+                        * type(undefined) === 'Undefined'
+                        * type(123)       === 'Number'
+                        * type('foo')     === 'String'
+                        * type(true)      === 'Boolean'
+                        * type([1, 2])    === 'Array'
+                        * type({})        === 'Object'
+                        * type(String)    === 'Function'
+                        * type(/abc+/)    === 'RegExp'
+                        */
+                       type: function (o) {
+                               return Object.prototype.toString.call(o).slice(8, -1);
+                       },
+
+                       /**
+                        * Returns a unique number for the given object. Later calls will still return the 
same number.
+                        *
+                        * @param {Object} obj
+                        * @returns {number}
+                        */
+                       objId: function (obj) {
+                               if (!obj['__id']) {
+                                       Object.defineProperty(obj, '__id', { value: ++uniqueId });
+                               }
+                               return obj['__id'];
+                       },
 
-                                       for (var key in o) {
-                                               if (o.hasOwnProperty(key)) {
-                                                       clone[key] = deepClone(o[key], visited);
+                       /**
+                        * Creates a deep clone of the given object.
+                        *
+                        * The main intended use of this function is to clone language definitions.
+                        *
+                        * @param {T} o
+                        * @param {Record<number, any>} [visited]
+                        * @returns {T}
+                        * @template T
+                        */
+                       clone: function deepClone(o, visited) {
+                               visited = visited || {};
+
+                               var clone; var id;
+                               switch (_.util.type(o)) {
+                                       case 'Object':
+                                               id = _.util.objId(o);
+                                               if (visited[id]) {
+                                                       return visited[id];
                                                }
-                                       }
+                                               clone = /** @type {Record<string, any>} */ ({});
+                                               visited[id] = clone;
 
-                                       return /** @type {any} */ (clone);
+                                               for (var key in o) {
+                                                       if (o.hasOwnProperty(key)) {
+                                                               clone[key] = deepClone(o[key], visited);
+                                                       }
+                                               }
 
-                               case 'Array':
-                                       id = _.util.objId(o);
-                                       if (visited[id]) {
-                                               return visited[id];
-                                       }
-                                       clone = [];
-                                       visited[id] = clone;
+                                               return /** @type {any} */ (clone);
 
-                                       (/** @type {Array} */(/** @type {any} */(o))).forEach(function (v, i) 
{
-                                               clone[i] = deepClone(v, visited);
-                                       });
+                                       case 'Array':
+                                               id = _.util.objId(o);
+                                               if (visited[id]) {
+                                                       return visited[id];
+                                               }
+                                               clone = [];
+                                               visited[id] = clone;
 
-                                       return /** @type {any} */ (clone);
+                                               (/** @type {Array} */(/** @type {any} 
*/(o))).forEach(function (v, i) {
+                                                       clone[i] = deepClone(v, visited);
+                                               });
 
-                               default:
-                                       return o;
-                       }
-               },
+                                               return /** @type {any} */ (clone);
 
-               /**
-                * Returns the Prism language of the given element set by a `language-xxxx` or `lang-xxxx` 
class.
-                *
-                * If no language is set for the element or the element is `null` or `undefined`, `none` will 
be returned.
-                *
-                * @param {Element} element
-                * @returns {string}
-                */
-               getLanguage: function (element) {
-                       while (element && !lang.test(element.className)) {
-                               element = element.parentElement;
-                       }
-                       if (element) {
-                               return (element.className.match(lang) || [, 'none'])[1].toLowerCase();
-                       }
-                       return 'none';
-               },
+                                       default:
+                                               return o;
+                               }
+                       },
 
-               /**
-                * Returns the script element that is currently executing.
-                *
-                * This does __not__ work for line script element.
-                *
-                * @returns {HTMLScriptElement | null}
-                */
-               currentScript: function () {
-                       if (typeof document === 'undefined') {
-                               return null;
-                       }
-                       if ('currentScript' in document && 1 < 2 /* hack to trip TS' flow analysis */) {
-                               return /** @type {any} */ (document.currentScript);
-                       }
+                       /**
+                        * Returns the Prism language of the given element set by a `language-xxxx` or 
`lang-xxxx` class.
+                        *
+                        * If no language is set for the element or the element is `null` or `undefined`, 
`none` will be returned.
+                        *
+                        * @param {Element} element
+                        * @returns {string}
+                        */
+                       getLanguage: function (element) {
+                               while (element && !lang.test(element.className)) {
+                                       element = element.parentElement;
+                               }
+                               if (element) {
+                                       return (element.className.match(lang) || [, 'none'])[1].toLowerCase();
+                               }
+                               return 'none';
+                       },
 
-                       // IE11 workaround
-                       // we'll get the src of the current script by parsing IE11's error stack trace
-                       // this will not work for inline scripts
+                       /**
+                        * Returns the script element that is currently executing.
+                        *
+                        * This does __not__ work for line script element.
+                        *
+                        * @returns {HTMLScriptElement | null}
+                        */
+                       currentScript: function () {
+                               if (typeof document === 'undefined') {
+                                       return null;
+                               }
+                               if ('currentScript' in document && 1 < 2 /* hack to trip TS' flow analysis 
*/) {
+                                       return /** @type {any} */ (document.currentScript);
+                               }
 
-                       try {
-                               throw new Error();
-                       } catch (err) {
-                               // Get file src url from stack. Specifically works with the format of stack 
traces in IE.
-                               // A stack will look like this:
-                               //
-                               // Error
-                               //    at _.util.currentScript 
(http://localhost/components/prism-core.js:119:5)
-                               //    at Global code (http://localhost/components/prism-core.js:606:1)
-
-                               var src = (/at [^(\r\n]*\((.*):.+:.+\)$/i.exec(err.stack) || [])[1];
-                               if (src) {
-                                       var scripts = document.getElementsByTagName('script');
-                                       for (var i in scripts) {
-                                               if (scripts[i].src == src) {
-                                                       return scripts[i];
+                               // IE11 workaround
+                               // we'll get the src of the current script by parsing IE11's error stack trace
+                               // this will not work for inline scripts
+
+                               try {
+                                       throw new Error();
+                               } catch (err) {
+                                       // Get file src url from stack. Specifically works with the format of 
stack traces in IE.
+                                       // A stack will look like this:
+                                       //
+                                       // Error
+                                       //    at _.util.currentScript 
(http://localhost/components/prism-core.js:119:5)
+                                       //    at Global code (http://localhost/components/prism-core.js:606:1)
+
+                                       var src = (/at [^(\r\n]*\((.*):.+:.+\)$/i.exec(err.stack) || [])[1];
+                                       if (src) {
+                                               var scripts = document.getElementsByTagName('script');
+                                               for (var i in scripts) {
+                                                       if (scripts[i].src == src) {
+                                                               return scripts[i];
+                                                       }
                                                }
                                        }
+                                       return null;
+                               }
+                       },
+
+                       /**
+                        * Returns whether a given class is active for `element`.
+                        *
+                        * The class can be activated if `element` or one of its ancestors has the given 
class and it can be deactivated
+                        * if `element` or one of its ancestors has the negated version of the given class. 
The _negated version_ of the
+                        * given class is just the given class with a `no-` prefix.
+                        *
+                        * Whether the class is active is determined by the closest ancestor of `element` 
(where `element` itself is
+                        * closest ancestor) that has the given class or the negated version of it. If 
neither `element` nor any of its
+                        * ancestors have the given class or the negated version of it, then the default 
activation will be returned.
+                        *
+                        * In the paradoxical situation where the closest ancestor contains __both__ the 
given class and the negated
+                        * version of it, the class is considered active.
+                        *
+                        * @param {Element} element
+                        * @param {string} className
+                        * @param {boolean} [defaultActivation=false]
+                        * @returns {boolean}
+                        */
+                       isActive: function (element, className, defaultActivation) {
+                               var no = 'no-' + className;
+
+                               while (element) {
+                                       var classList = element.classList;
+                                       if (classList.contains(className)) {
+                                               return true;
+                                       }
+                                       if (classList.contains(no)) {
+                                               return false;
+                                       }
+                                       element = element.parentElement;
                                }
-                               return null;
+                               return !!defaultActivation;
                        }
                },
 
                /**
-                * Returns whether a given class is active for `element`.
+                * This namespace contains all currently loaded languages and the some helper functions to 
create and modify languages.
                 *
-                * The class can be activated if `element` or one of its ancestors has the given class and it 
can be deactivated
-                * if `element` or one of its ancestors has the negated version of the given class. The 
_negated version_ of the
-                * given class is just the given class with a `no-` prefix.
-                *
-                * Whether the class is active is determined by the closest ancestor of `element` (where 
`element` itself is
-                * closest ancestor) that has the given class or the negated version of it. If neither 
`element` nor any of its
-                * ancestors have the given class or the negated version of it, then the default activation 
will be returned.
-                *
-                * In the paradoxical situation where the closest ancestor contains __both__ the given class 
and the negated
-                * version of it, the class is considered active.
-                *
-                * @param {Element} element
-                * @param {string} className
-                * @param {boolean} [defaultActivation=false]
-                * @returns {boolean}
+                * @namespace
+                * @memberof Prism
+                * @public
                 */
-               isActive: function (element, className, defaultActivation) {
-                       var no = 'no-' + className;
+               languages: {
+                       /**
+                        * The grammar for plain, unformatted text.
+                        */
+                       plain: plainTextGrammar,
+                       plaintext: plainTextGrammar,
+                       text: plainTextGrammar,
+                       txt: plainTextGrammar,
 
-                       while (element) {
-                               var classList = element.classList;
-                               if (classList.contains(className)) {
-                                       return true;
+                       /**
+                        * Creates a deep copy of the language with the given id and appends the given tokens.
+                        *
+                        * If a token in `redef` also appears in the copied language, then the existing token 
in the copied language
+                        * will be overwritten at its original position.
+                        *
+                        * ## Best practices
+                        *
+                        * Since the position of overwriting tokens (token in `redef` that overwrite tokens 
in the copied language)
+                        * doesn't matter, they can technically be in any order. However, this can be 
confusing to others that trying to
+                        * understand the language definition because, normally, the order of tokens matters 
in Prism grammars.
+                        *
+                        * Therefore, it is encouraged to order overwriting tokens according to the positions 
of the overwritten tokens.
+                        * Furthermore, all non-overwriting tokens should be placed after the overwriting 
ones.
+                        *
+                        * @param {string} id The id of the language to extend. This has to be a key in 
`Prism.languages`.
+                        * @param {Grammar} redef The new tokens to append.
+                        * @returns {Grammar} The new language created.
+                        * @public
+                        * @example
+                        * Prism.languages['css-with-colors'] = Prism.languages.extend('css', {
+                        *     // Prism.languages.css already has a 'comment' token, so this token will 
overwrite CSS' 'comment' token
+                        *     // at its original position
+                        *     'comment': { ... },
+                        *     // CSS doesn't have a 'color' token, so this token will be appended
+                        *     'color': /\b(?:red|green|blue)\b/
+                        * });
+                        */
+                       extend: function (id, redef) {
+                               var lang = _.util.clone(_.languages[id]);
+
+                               for (var key in redef) {
+                                       lang[key] = redef[key];
+                               }
+
+                               return lang;
+                       },
+
+                       /**
+                        * Inserts tokens _before_ another token in a language definition or any other 
grammar.
+                        *
+                        * ## Usage
+                        *
+                        * This helper method makes it easy to modify existing languages. For example, the 
CSS language definition
+                        * not only defines CSS highlighting for CSS documents, but also needs to define 
highlighting for CSS embedded
+                        * in HTML through `<style>` elements. To do this, it needs to modify 
`Prism.languages.markup` and add the
+                        * appropriate tokens. However, `Prism.languages.markup` is a regular JavaScript 
object literal, so if you do
+                        * this:
+                        *
+                        * ```js
+                        * Prism.languages.markup.style = {
+                        *     // token
+                        * };
+                        * ```
+                        *
+                        * then the `style` token will be added (and processed) at the end. `insertBefore` 
allows you to insert tokens
+                        * before existing tokens. For the CSS example above, you would use it like this:
+                        *
+                        * ```js
+                        * Prism.languages.insertBefore('markup', 'cdata', {
+                        *     'style': {
+                        *         // token
+                        *     }
+                        * });
+                        * ```
+                        *
+                        * ## Special cases
+                        *
+                        * If the grammars of `inside` and `insert` have tokens with the same name, the 
tokens in `inside`'s grammar
+                        * will be ignored.
+                        *
+                        * This behavior can be used to insert tokens after `before`:
+                        *
+                        * ```js
+                        * Prism.languages.insertBefore('markup', 'comment', {
+                        *     'comment': Prism.languages.markup.comment,
+                        *     // tokens after 'comment'
+                        * });
+                        * ```
+                        *
+                        * ## Limitations
+                        *
+                        * The main problem `insertBefore` has to solve is iteration order. Since ES2015, the 
iteration order for object
+                        * properties is guaranteed to be the insertion order (except for integer keys) but 
some browsers behave
+                        * differently when keys are deleted and re-inserted. So `insertBefore` can't be 
implemented by temporarily
+                        * deleting properties which is necessary to insert at arbitrary positions.
+                        *
+                        * To solve this problem, `insertBefore` doesn't actually insert the given tokens 
into the target object.
+                        * Instead, it will create a new object and replace all references to the target 
object with the new one. This
+                        * can be done without temporarily deleting properties, so the iteration order is 
well-defined.
+                        *
+                        * However, only references that can be reached from `Prism.languages` or `insert` 
will be replaced. I.e. if
+                        * you hold the target object in a variable, then the value of the variable will not 
change.
+                        *
+                        * ```js
+                        * var oldMarkup = Prism.languages.markup;
+                        * var newMarkup = Prism.languages.insertBefore('markup', 'comment', { ... });
+                        *
+                        * assert(oldMarkup !== Prism.languages.markup);
+                        * assert(newMarkup === Prism.languages.markup);
+                        * ```
+                        *
+                        * @param {string} inside The property of `root` (e.g. a language id in 
`Prism.languages`) that contains the
+                        * object to be modified.
+                        * @param {string} before The key to insert before.
+                        * @param {Grammar} insert An object containing the key-value pairs to be inserted.
+                        * @param {Object<string, any>} [root] The object containing `inside`, i.e. the 
object that contains the
+                        * object to be modified.
+                        *
+                        * Defaults to `Prism.languages`.
+                        * @returns {Grammar} The new grammar object.
+                        * @public
+                        */
+                       insertBefore: function (inside, before, insert, root) {
+                               root = root || /** @type {any} */ (_.languages);
+                               var grammar = root[inside];
+                               /** @type {Grammar} */
+                               var ret = {};
+
+                               for (var token in grammar) {
+                                       if (grammar.hasOwnProperty(token)) {
+
+                                               if (token == before) {
+                                                       for (var newToken in insert) {
+                                                               if (insert.hasOwnProperty(newToken)) {
+                                                                       ret[newToken] = insert[newToken];
+                                                               }
+                                                       }
+                                               }
+
+                                               // Do not insert token which also occur in insert. See #1525
+                                               if (!insert.hasOwnProperty(token)) {
+                                                       ret[token] = grammar[token];
+                                               }
+                                       }
                                }
-                               if (classList.contains(no)) {
-                                       return false;
+
+                               var old = root[inside];
+                               root[inside] = ret;
+
+                               // Update references in other language definitions
+                               _.languages.DFS(_.languages, function (key, value) {
+                                       if (value === old && key != inside) {
+                                               this[key] = ret;
+                                       }
+                               });
+
+                               return ret;
+                       },
+
+                       // Traverse a language definition with Depth First Search
+                       DFS: function DFS(o, callback, type, visited) {
+                               visited = visited || {};
+
+                               var objId = _.util.objId;
+
+                               for (var i in o) {
+                                       if (o.hasOwnProperty(i)) {
+                                               callback.call(o, i, o[i], type || i);
+
+                                               var property = o[i];
+                                               var propertyType = _.util.type(property);
+
+                                               if (propertyType === 'Object' && !visited[objId(property)]) {
+                                                       visited[objId(property)] = true;
+                                                       DFS(property, callback, null, visited);
+                                               } else if (propertyType === 'Array' && 
!visited[objId(property)]) {
+                                                       visited[objId(property)] = true;
+                                                       DFS(property, callback, i, visited);
+                                               }
+                                       }
                                }
-                               element = element.parentElement;
                        }
-                       return !!defaultActivation;
-               }
-       },
+               },
+
+               plugins: {},
 
-       /**
-        * This namespace contains all currently loaded languages and the some helper functions to create and 
modify languages.
-        *
-        * @namespace
-        * @memberof Prism
-        * @public
-        */
-       languages: {
                /**
-                * Creates a deep copy of the language with the given id and appends the given tokens.
+                * This is the most high-level function in Prism’s API.
+                * It fetches all the elements that have a `.language-xxxx` class and then calls {@link 
Prism.highlightElement} on
+                * each one of them.
                 *
-                * If a token in `redef` also appears in the copied language, then the existing token in the 
copied language
-                * will be overwritten at its original position.
+                * This is equivalent to `Prism.highlightAllUnder(document, async, callback)`.
                 *
-                * ## Best practices
-                *
-                * Since the position of overwriting tokens (token in `redef` that overwrite tokens in the 
copied language)
-                * doesn't matter, they can technically be in any order. However, this can be confusing to 
others that trying to
-                * understand the language definition because, normally, the order of tokens matters in Prism 
grammars.
+                * @param {boolean} [async=false] Same as in {@link Prism.highlightAllUnder}.
+                * @param {HighlightCallback} [callback] Same as in {@link Prism.highlightAllUnder}.
+                * @memberof Prism
+                * @public
+                */
+               highlightAll: function (async, callback) {
+                       _.highlightAllUnder(document, async, callback);
+               },
+
+               /**
+                * Fetches all the descendants of `container` that have a `.language-xxxx` class and then 
calls
+                * {@link Prism.highlightElement} on each one of them.
                 *
-                * Therefore, it is encouraged to order overwriting tokens according to the positions of the 
overwritten tokens.
-                * Furthermore, all non-overwriting tokens should be placed after the overwriting ones.
+                * The following hooks will be run:
+                * 1. `before-highlightall`
+                * 2. `before-all-elements-highlight`
+                * 3. All hooks of {@link Prism.highlightElement} for each element.
                 *
-                * @param {string} id The id of the language to extend. This has to be a key in 
`Prism.languages`.
-                * @param {Grammar} redef The new tokens to append.
-                * @returns {Grammar} The new language created.
+                * @param {ParentNode} container The root element, whose descendants that have a 
`.language-xxxx` class will be highlighted.
+                * @param {boolean} [async=false] Whether each element is to be highlighted asynchronously 
using Web Workers.
+                * @param {HighlightCallback} [callback] An optional callback to be invoked on each element 
after its highlighting is done.
+                * @memberof Prism
                 * @public
-                * @example
-                * Prism.languages['css-with-colors'] = Prism.languages.extend('css', {
-                *     // Prism.languages.css already has a 'comment' token, so this token will overwrite 
CSS' 'comment' token
-                *     // at its original position
-                *     'comment': { ... },
-                *     // CSS doesn't have a 'color' token, so this token will be appended
-                *     'color': /\b(?:red|green|blue)\b/
-                * });
                 */
-               extend: function (id, redef) {
-                       var lang = _.util.clone(_.languages[id]);
+               highlightAllUnder: function (container, async, callback) {
+                       var env = {
+                               callback: callback,
+                               container: container,
+                               selector: 'code[class*="language-"], [class*="language-"] code, 
code[class*="lang-"], [class*="lang-"] code'
+                       };
 
-                       for (var key in redef) {
-                               lang[key] = redef[key];
-                       }
+                       _.hooks.run('before-highlightall', env);
 
-                       return lang;
+                       env.elements = 
Array.prototype.slice.apply(env.container.querySelectorAll(env.selector));
+
+                       _.hooks.run('before-all-elements-highlight', env);
+
+                       for (var i = 0, element; (element = env.elements[i++]);) {
+                               _.highlightElement(element, async === true, env.callback);
+                       }
                },
 
                /**
-                * Inserts tokens _before_ another token in a language definition or any other grammar.
-                *
-                * ## Usage
-                *
-                * This helper method makes it easy to modify existing languages. For example, the CSS 
language definition
-                * not only defines CSS highlighting for CSS documents, but also needs to define highlighting 
for CSS embedded
-                * in HTML through `<style>` elements. To do this, it needs to modify 
`Prism.languages.markup` and add the
-                * appropriate tokens. However, `Prism.languages.markup` is a regular JavaScript object 
literal, so if you do
-                * this:
-                *
-                * ```js
-                * Prism.languages.markup.style = {
-                *     // token
-                * };
-                * ```
-                *
-                * then the `style` token will be added (and processed) at the end. `insertBefore` allows you 
to insert tokens
-                * before existing tokens. For the CSS example above, you would use it like this:
-                *
-                * ```js
-                * Prism.languages.insertBefore('markup', 'cdata', {
-                *     'style': {
-                *         // token
-                *     }
-                * });
-                * ```
-                *
-                * ## Special cases
-                *
-                * If the grammars of `inside` and `insert` have tokens with the same name, the tokens in 
`inside`'s grammar
-                * will be ignored.
-                *
-                * This behavior can be used to insert tokens after `before`:
-                *
-                * ```js
-                * Prism.languages.insertBefore('markup', 'comment', {
-                *     'comment': Prism.languages.markup.comment,
-                *     // tokens after 'comment'
-                * });
-                * ```
-                *
-                * ## Limitations
-                *
-                * The main problem `insertBefore` has to solve is iteration order. Since ES2015, the 
iteration order for object
-                * properties is guaranteed to be the insertion order (except for integer keys) but some 
browsers behave
-                * differently when keys are deleted and re-inserted. So `insertBefore` can't be implemented 
by temporarily
-                * deleting properties which is necessary to insert at arbitrary positions.
-                *
-                * To solve this problem, `insertBefore` doesn't actually insert the given tokens into the 
target object.
-                * Instead, it will create a new object and replace all references to the target object with 
the new one. This
-                * can be done without temporarily deleting properties, so the iteration order is 
well-defined.
+                * Highlights the code inside a single element.
                 *
-                * However, only references that can be reached from `Prism.languages` or `insert` will be 
replaced. I.e. if
-                * you hold the target object in a variable, then the value of the variable will not change.
+                * The following hooks will be run:
+                * 1. `before-sanity-check`
+                * 2. `before-highlight`
+                * 3. All hooks of {@link Prism.highlight}. These hooks will be run by an asynchronous worker 
if `async` is `true`.
+                * 4. `before-insert`
+                * 5. `after-highlight`
+                * 6. `complete`
                 *
-                * ```js
-                * var oldMarkup = Prism.languages.markup;
-                * var newMarkup = Prism.languages.insertBefore('markup', 'comment', { ... });
-                *
-                * assert(oldMarkup !== Prism.languages.markup);
-                * assert(newMarkup === Prism.languages.markup);
-                * ```
+                * Some the above hooks will be skipped if the element doesn't contain any text or there is 
no grammar loaded for
+                * the element's language.
                 *
-                * @param {string} inside The property of `root` (e.g. a language id in `Prism.languages`) 
that contains the
-                * object to be modified.
-                * @param {string} before The key to insert before.
-                * @param {Grammar} insert An object containing the key-value pairs to be inserted.
-                * @param {Object<string, any>} [root] The object containing `inside`, i.e. the object that 
contains the
-                * object to be modified.
+                * @param {Element} element The element containing the code.
+                * It must have a class of `language-xxxx` to be processed, where `xxxx` is a valid language 
identifier.
+                * @param {boolean} [async=false] Whether the element is to be highlighted asynchronously 
using Web Workers
+                * to improve performance and avoid blocking the UI when highlighting very large chunks of 
code. This option is
+                * [disabled by 
default](https://prismjs.com/faq.html#why-is-asynchronous-highlighting-disabled-by-default).
                 *
-                * Defaults to `Prism.languages`.
-                * @returns {Grammar} The new grammar object.
+                * Note: All language definitions required to highlight the code must be included in the main 
`prism.js` file for
+                * asynchronous highlighting to work. You can build your own bundle on the
+                * [Download page](https://prismjs.com/download.html).
+                * @param {HighlightCallback} [callback] An optional callback to be invoked after the 
highlighting is done.
+                * Mostly useful when `async` is `true`, since in that case, the highlighting is done 
asynchronously.
+                * @memberof Prism
                 * @public
                 */
-               insertBefore: function (inside, before, insert, root) {
-                       root = root || /** @type {any} */ (_.languages);
-                       var grammar = root[inside];
-                       /** @type {Grammar} */
-                       var ret = {};
-
-                       for (var token in grammar) {
-                               if (grammar.hasOwnProperty(token)) {
-
-                                       if (token == before) {
-                                               for (var newToken in insert) {
-                                                       if (insert.hasOwnProperty(newToken)) {
-                                                               ret[newToken] = insert[newToken];
-                                                       }
-                                               }
-                                       }
-
-                                       // Do not insert token which also occur in insert. See #1525
-                                       if (!insert.hasOwnProperty(token)) {
-                                               ret[token] = grammar[token];
-                                       }
-                               }
+               highlightElement: function (element, async, callback) {
+                       // Find language
+                       var language = _.util.getLanguage(element);
+                       var grammar = _.languages[language];
+
+                       // Set language on the element, if not present
+                       element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' 
language-' + language;
+
+                       // Set language on the parent, for styling
+                       var parent = element.parentElement;
+                       if (parent && parent.nodeName.toLowerCase() === 'pre') {
+                               parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + 
' language-' + language;
                        }
 
-                       var old = root[inside];
-                       root[inside] = ret;
+                       var code = element.textContent;
 
-                       // Update references in other language definitions
-                       _.languages.DFS(_.languages, function(key, value) {
-                               if (value === old && key != inside) {
-                                       this[key] = ret;
-                               }
-                       });
-
-                       return ret;
-               },
-
-               // Traverse a language definition with Depth First Search
-               DFS: function DFS(o, callback, type, visited) {
-                       visited = visited || {};
+                       var env = {
+                               element: element,
+                               language: language,
+                               grammar: grammar,
+                               code: code
+                       };
 
-                       var objId = _.util.objId;
+                       function insertHighlightedCode(highlightedCode) {
+                               env.highlightedCode = highlightedCode;
 
-                       for (var i in o) {
-                               if (o.hasOwnProperty(i)) {
-                                       callback.call(o, i, o[i], type || i);
+                               _.hooks.run('before-insert', env);
 
-                                       var property = o[i],
-                                           propertyType = _.util.type(property);
+                               env.element.innerHTML = env.highlightedCode;
 
-                                       if (propertyType === 'Object' && !visited[objId(property)]) {
-                                               visited[objId(property)] = true;
-                                               DFS(property, callback, null, visited);
-                                       }
-                                       else if (propertyType === 'Array' && !visited[objId(property)]) {
-                                               visited[objId(property)] = true;
-                                               DFS(property, callback, i, visited);
-                                       }
-                               }
+                               _.hooks.run('after-highlight', env);
+                               _.hooks.run('complete', env);
+                               callback && callback.call(env.element);
                        }
-               }
-       },
 
-       plugins: {},
+                       _.hooks.run('before-sanity-check', env);
 
-       /**
-        * This is the most high-level function in Prism’s API.
-        * It fetches all the elements that have a `.language-xxxx` class and then calls {@link 
Prism.highlightElement} on
-        * each one of them.
-        *
-        * This is equivalent to `Prism.highlightAllUnder(document, async, callback)`.
-        *
-        * @param {boolean} [async=false] Same as in {@link Prism.highlightAllUnder}.
-        * @param {HighlightCallback} [callback] Same as in {@link Prism.highlightAllUnder}.
-        * @memberof Prism
-        * @public
-        */
-       highlightAll: function(async, callback) {
-               _.highlightAllUnder(document, async, callback);
-       },
-
-       /**
-        * Fetches all the descendants of `container` that have a `.language-xxxx` class and then calls
-        * {@link Prism.highlightElement} on each one of them.
-        *
-        * The following hooks will be run:
-        * 1. `before-highlightall`
-        * 2. `before-all-elements-highlight`
-        * 3. All hooks of {@link Prism.highlightElement} for each element.
-        *
-        * @param {ParentNode} container The root element, whose descendants that have a `.language-xxxx` 
class will be highlighted.
-        * @param {boolean} [async=false] Whether each element is to be highlighted asynchronously using Web 
Workers.
-        * @param {HighlightCallback} [callback] An optional callback to be invoked on each element after its 
highlighting is done.
-        * @memberof Prism
-        * @public
-        */
-       highlightAllUnder: function(container, async, callback) {
-               var env = {
-                       callback: callback,
-                       container: container,
-                       selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], 
[class*="lang-"] code'
-               };
-
-               _.hooks.run('before-highlightall', env);
+                       // plugins may change/add the parent/element
+                       parent = env.element.parentElement;
+                       if (parent && parent.nodeName.toLowerCase() === 'pre' && 
!parent.hasAttribute('tabindex')) {
+                               parent.setAttribute('tabindex', '0');
+                       }
 
-               env.elements = Array.prototype.slice.apply(env.container.querySelectorAll(env.selector));
+                       if (!env.code) {
+                               _.hooks.run('complete', env);
+                               callback && callback.call(env.element);
+                               return;
+                       }
 
-               _.hooks.run('before-all-elements-highlight', env);
+                       _.hooks.run('before-highlight', env);
 
-               for (var i = 0, element; element = env.elements[i++];) {
-                       _.highlightElement(element, async === true, env.callback);
-               }
-       },
+                       if (!env.grammar) {
+                               insertHighlightedCode(_.util.encode(env.code));
+                               return;
+                       }
 
-       /**
-        * Highlights the code inside a single element.
-        *
-        * The following hooks will be run:
-        * 1. `before-sanity-check`
-        * 2. `before-highlight`
-        * 3. All hooks of {@link Prism.highlight}. These hooks will be run by an asynchronous worker if 
`async` is `true`.
-        * 4. `before-insert`
-        * 5. `after-highlight`
-        * 6. `complete`
-        *
-        * Some the above hooks will be skipped if the element doesn't contain any text or there is no 
grammar loaded for
-        * the element's language.
-        *
-        * @param {Element} element The element containing the code.
-        * It must have a class of `language-xxxx` to be processed, where `xxxx` is a valid language 
identifier.
-        * @param {boolean} [async=false] Whether the element is to be highlighted asynchronously using Web 
Workers
-        * to improve performance and avoid blocking the UI when highlighting very large chunks of code. This 
option is
-        * [disabled by 
default](https://prismjs.com/faq.html#why-is-asynchronous-highlighting-disabled-by-default).
-        *
-        * Note: All language definitions required to highlight the code must be included in the main 
`prism.js` file for
-        * asynchronous highlighting to work. You can build your own bundle on the
-        * [Download page](https://prismjs.com/download.html).
-        * @param {HighlightCallback} [callback] An optional callback to be invoked after the highlighting is 
done.
-        * Mostly useful when `async` is `true`, since in that case, the highlighting is done asynchronously.
-        * @memberof Prism
-        * @public
-        */
-       highlightElement: function(element, async, callback) {
-               // Find language
-               var language = _.util.getLanguage(element);
-               var grammar = _.languages[language];
-
-               // Set language on the element, if not present
-               element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + 
language;
-
-               // Set language on the parent, for styling
-               var parent = element.parentElement;
-               if (parent && parent.nodeName.toLowerCase() === 'pre') {
-                       parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' 
language-' + language;
-               }
+                       if (async && _self.Worker) {
+                               var worker = new Worker(_.filename);
 
-               var code = element.textContent;
+                               worker.onmessage = function (evt) {
+                                       insertHighlightedCode(evt.data);
+                               };
 
-               var env = {
-                       element: element,
-                       language: language,
-                       grammar: grammar,
-                       code: code
-               };
+                               worker.postMessage(JSON.stringify({
+                                       language: env.language,
+                                       code: env.code,
+                                       immediateClose: true
+                               }));
+                       } else {
+                               insertHighlightedCode(_.highlight(env.code, env.grammar, env.language));
+                       }
+               },
 
-               function insertHighlightedCode(highlightedCode) {
-                       env.highlightedCode = highlightedCode;
+               /**
+                * Low-level function, only use if you know what you’re doing. It accepts a string of text as 
input
+                * and the language definitions to use, and returns a string with the HTML produced.
+                *
+                * The following hooks will be run:
+                * 1. `before-tokenize`
+                * 2. `after-tokenize`
+                * 3. `wrap`: On each {@link Token}.
+                *
+                * @param {string} text A string with the code to be highlighted.
+                * @param {Grammar} grammar An object containing the tokens to use.
+                *
+                * Usually a language definition like `Prism.languages.markup`.
+                * @param {string} language The name of the language definition passed to `grammar`.
+                * @returns {string} The highlighted HTML.
+                * @memberof Prism
+                * @public
+                * @example
+                * Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript');
+                */
+               highlight: function (text, grammar, language) {
+                       var env = {
+                               code: text,
+                               grammar: grammar,
+                               language: language
+                       };
+                       _.hooks.run('before-tokenize', env);
+                       env.tokens = _.tokenize(env.code, env.grammar);
+                       _.hooks.run('after-tokenize', env);
+                       return Token.stringify(_.util.encode(env.tokens), env.language);
+               },
 
-                       _.hooks.run('before-insert', env);
+               /**
+                * This is the heart of Prism, and the most low-level function you can use. It accepts a 
string of text as input
+                * and the language definitions to use, and returns an array with the tokenized code.
+                *
+                * When the language definition includes nested tokens, the function is called recursively on 
each of these tokens.
+                *
+                * This method could be useful in other contexts as well, as a very crude parser.
+                *
+                * @param {string} text A string with the code to be highlighted.
+                * @param {Grammar} grammar An object containing the tokens to use.
+                *
+                * Usually a language definition like `Prism.languages.markup`.
+                * @returns {TokenStream} An array of strings and tokens, a token stream.
+                * @memberof Prism
+                * @public
+                * @example
+                * let code = `var foo = 0;`;
+                * let tokens = Prism.tokenize(code, Prism.languages.javascript);
+                * tokens.forEach(token => {
+                *     if (token instanceof Prism.Token && token.type === 'number') {
+                *         console.log(`Found numeric literal: ${token.content}`);
+                *     }
+                * });
+                */
+               tokenize: function (text, grammar) {
+                       var rest = grammar.rest;
+                       if (rest) {
+                               for (var token in rest) {
+                                       grammar[token] = rest[token];
+                               }
 
-                       env.element.innerHTML = env.highlightedCode;
+                               delete grammar.rest;
+                       }
 
-                       _.hooks.run('after-highlight', env);
-                       _.hooks.run('complete', env);
-                       callback && callback.call(env.element);
-               }
+                       var tokenList = new LinkedList();
+                       addAfter(tokenList, tokenList.head, text);
 
-               _.hooks.run('before-sanity-check', env);
+                       matchGrammar(text, tokenList, grammar, tokenList.head, 0);
 
-               if (!env.code) {
-                       _.hooks.run('complete', env);
-                       callback && callback.call(env.element);
-                       return;
-               }
+                       return toArray(tokenList);
+               },
 
-               _.hooks.run('before-highlight', env);
+               /**
+                * @namespace
+                * @memberof Prism
+                * @public
+                */
+               hooks: {
+                       all: {},
 
-               if (!env.grammar) {
-                       insertHighlightedCode(_.util.encode(env.code));
-                       return;
-               }
+                       /**
+                        * Adds the given callback to the list of callbacks for the given hook.
+                        *
+                        * The callback will be invoked when the hook it is registered for is run.
+                        * Hooks are usually directly run by a highlight function but you can also run hooks 
yourself.
+                        *
+                        * One callback function can be registered to multiple hooks and the same hook 
multiple times.
+                        *
+                        * @param {string} name The name of the hook.
+                        * @param {HookCallback} callback The callback function which is given environment 
variables.
+                        * @public
+                        */
+                       add: function (name, callback) {
+                               var hooks = _.hooks.all;
 
-               if (async && _self.Worker) {
-                       var worker = new Worker(_.filename);
+                               hooks[name] = hooks[name] || [];
 
-                       worker.onmessage = function(evt) {
-                               insertHighlightedCode(evt.data);
-                       };
+                               hooks[name].push(callback);
+                       },
 
-                       worker.postMessage(JSON.stringify({
-                               language: env.language,
-                               code: env.code,
-                               immediateClose: true
-                       }));
-               }
-               else {
-                       insertHighlightedCode(_.highlight(env.code, env.grammar, env.language));
-               }
-       },
+                       /**
+                        * Runs a hook invoking all registered callbacks with the given environment variables.
+                        *
+                        * Callbacks will be invoked synchronously and in the order in which they were 
registered.
+                        *
+                        * @param {string} name The name of the hook.
+                        * @param {Object<string, any>} env The environment variables of the hook passed to 
all callbacks registered.
+                        * @public
+                        */
+                       run: function (name, env) {
+                               var callbacks = _.hooks.all[name];
 
-       /**
-        * Low-level function, only use if you know what you’re doing. It accepts a string of text as input
-        * and the language definitions to use, and returns a string with the HTML produced.
-        *
-        * The following hooks will be run:
-        * 1. `before-tokenize`
-        * 2. `after-tokenize`
-        * 3. `wrap`: On each {@link Token}.
-        *
-        * @param {string} text A string with the code to be highlighted.
-        * @param {Grammar} grammar An object containing the tokens to use.
-        *
-        * Usually a language definition like `Prism.languages.markup`.
-        * @param {string} language The name of the language definition passed to `grammar`.
-        * @returns {string} The highlighted HTML.
-        * @memberof Prism
-        * @public
-        * @example
-        * Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript');
-        */
-       highlight: function (text, grammar, language) {
-               var env = {
-                       code: text,
-                       grammar: grammar,
-                       language: language
-               };
-               _.hooks.run('before-tokenize', env);
-               env.tokens = _.tokenize(env.code, env.grammar);
-               _.hooks.run('after-tokenize', env);
-               return Token.stringify(_.util.encode(env.tokens), env.language);
-       },
+                               if (!callbacks || !callbacks.length) {
+                                       return;
+                               }
 
-       /**
-        * This is the heart of Prism, and the most low-level function you can use. It accepts a string of 
text as input
-        * and the language definitions to use, and returns an array with the tokenized code.
-        *
-        * When the language definition includes nested tokens, the function is called recursively on each of 
these tokens.
-        *
-        * This method could be useful in other contexts as well, as a very crude parser.
-        *
-        * @param {string} text A string with the code to be highlighted.
-        * @param {Grammar} grammar An object containing the tokens to use.
-        *
-        * Usually a language definition like `Prism.languages.markup`.
-        * @returns {TokenStream} An array of strings and tokens, a token stream.
-        * @memberof Prism
-        * @public
-        * @example
-        * let code = `var foo = 0;`;
-        * let tokens = Prism.tokenize(code, Prism.languages.javascript);
-        * tokens.forEach(token => {
-        *     if (token instanceof Prism.Token && token.type === 'number') {
-        *         console.log(`Found numeric literal: ${token.content}`);
-        *     }
-        * });
-        */
-       tokenize: function(text, grammar) {
-               var rest = grammar.rest;
-               if (rest) {
-                       for (var token in rest) {
-                               grammar[token] = rest[token];
+                               for (var i = 0, callback; (callback = callbacks[i++]);) {
+                                       callback(env);
+                               }
                        }
+               },
 
-                       delete grammar.rest;
-               }
-
-               var tokenList = new LinkedList();
-               addAfter(tokenList, tokenList.head, text);
+               Token: Token
+       };
+       _self.Prism = _;
 
-               matchGrammar(text, tokenList, grammar, tokenList.head, 0);
 
-               return toArray(tokenList);
-       },
+       // Typescript note:
+       // The following can be used to import the Token type in JSDoc:
+       //
+       //   @typedef {InstanceType<import("./prism-core")["Token"]>} Token
 
        /**
-        * @namespace
-        * @memberof Prism
+        * Creates a new token.
+        *
+        * @param {string} type See {@link Token#type type}
+        * @param {string | TokenStream} content See {@link Token#content content}
+        * @param {string|string[]} [alias] The alias(es) of the token.
+        * @param {string} [matchedStr=""] A copy of the full string this token was created from.
+        * @class
+        * @global
         * @public
         */
-       hooks: {
-               all: {},
-
+       function Token(type, content, alias, matchedStr) {
                /**
-                * Adds the given callback to the list of callbacks for the given hook.
+                * The type of the token.
                 *
-                * The callback will be invoked when the hook it is registered for is run.
-                * Hooks are usually directly run by a highlight function but you can also run hooks yourself.
+                * This is usually the key of a pattern in a {@link Grammar}.
                 *
-                * One callback function can be registered to multiple hooks and the same hook multiple times.
-                *
-                * @param {string} name The name of the hook.
-                * @param {HookCallback} callback The callback function which is given environment variables.
+                * @type {string}
+                * @see GrammarToken
                 * @public
                 */
-               add: function (name, callback) {
-                       var hooks = _.hooks.all;
-
-                       hooks[name] = hooks[name] || [];
-
-                       hooks[name].push(callback);
-               },
-
+               this.type = type;
                /**
-                * Runs a hook invoking all registered callbacks with the given environment variables.
+                * The strings or tokens contained by this token.
                 *
-                * Callbacks will be invoked synchronously and in the order in which they were registered.
+                * This will be a token stream if the pattern matched also defined an `inside` grammar.
                 *
-                * @param {string} name The name of the hook.
-                * @param {Object<string, any>} env The environment variables of the hook passed to all 
callbacks registered.
+                * @type {string | TokenStream}
                 * @public
                 */
-               run: function (name, env) {
-                       var callbacks = _.hooks.all[name];
-
-                       if (!callbacks || !callbacks.length) {
-                               return;
-                       }
-
-                       for (var i=0, callback; callback = callbacks[i++];) {
-                               callback(env);
-                       }
-               }
-       },
-
-       Token: Token
-};
-_self.Prism = _;
-
-
-// Typescript note:
-// The following can be used to import the Token type in JSDoc:
-//
-//   @typedef {InstanceType<import("./prism-core")["Token"]>} Token
+               this.content = content;
+               /**
+                * The alias(es) of the token.
+                *
+                * @type {string|string[]}
+                * @see GrammarToken
+                * @public
+                */
+               this.alias = alias;
+               // Copy of the full string this token was created from
+               this.length = (matchedStr || '').length | 0;
+       }
 
-/**
- * Creates a new token.
- *
- * @param {string} type See {@link Token#type type}
- * @param {string | TokenStream} content See {@link Token#content content}
- * @param {string|string[]} [alias] The alias(es) of the token.
- * @param {string} [matchedStr=""] A copy of the full string this token was created from.
- * @class
- * @global
- * @public
- */
-function Token(type, content, alias, matchedStr) {
        /**
-        * The type of the token.
+        * A token stream is an array of strings and {@link Token Token} objects.
         *
-        * This is usually the key of a pattern in a {@link Grammar}.
+        * Token streams have to fulfill a few properties that are assumed by most functions (mostly internal 
ones) that process
+        * them.
         *
-        * @type {string}
-        * @see GrammarToken
-        * @public
-        */
-       this.type = type;
-       /**
-        * The strings or tokens contained by this token.
+        * 1. No adjacent strings.
+        * 2. No empty strings.
         *
-        * This will be a token stream if the pattern matched also defined an `inside` grammar.
+        *    The only exception here is the token stream that only contains the empty string and nothing 
else.
         *
-        * @type {string | TokenStream}
+        * @typedef {Array<string | Token>} TokenStream
+        * @global
         * @public
         */
-       this.content = content;
+
        /**
-        * The alias(es) of the token.
+        * Converts the given token or token stream to an HTML representation.
         *
-        * @type {string|string[]}
-        * @see GrammarToken
-        * @public
+        * The following hooks will be run:
+        * 1. `wrap`: On each {@link Token}.
+        *
+        * @param {string | Token | TokenStream} o The token or token stream to be converted.
+        * @param {string} language The name of current language.
+        * @returns {string} The HTML representation of the token or token stream.
+        * @memberof Token
+        * @static
         */
-       this.alias = alias;
-       // Copy of the full string this token was created from
-       this.length = (matchedStr || '').length | 0;
-}
-
-/**
- * A token stream is an array of strings and {@link Token Token} objects.
- *
- * Token streams have to fulfill a few properties that are assumed by most functions (mostly internal ones) 
that process
- * them.
- *
- * 1. No adjacent strings.
- * 2. No empty strings.
- *
- *    The only exception here is the token stream that only contains the empty string and nothing else.
- *
- * @typedef {Array<string | Token>} TokenStream
- * @global
- * @public
- */
-
-/**
- * Converts the given token or token stream to an HTML representation.
- *
- * The following hooks will be run:
- * 1. `wrap`: On each {@link Token}.
- *
- * @param {string | Token | TokenStream} o The token or token stream to be converted.
- * @param {string} language The name of current language.
- * @returns {string} The HTML representation of the token or token stream.
- * @memberof Token
- * @static
- */
-Token.stringify = function stringify(o, language) {
-       if (typeof o == 'string') {
-               return o;
-       }
-       if (Array.isArray(o)) {
-               var s = '';
-               o.forEach(function (e) {
-                       s += stringify(e, language);
-               });
-               return s;
-       }
-
-       var env = {
-               type: o.type,
-               content: stringify(o.content, language),
-               tag: 'span',
-               classes: ['token', o.type],
-               attributes: {},
-               language: language
-       };
-
-       var aliases = o.alias;
-       if (aliases) {
-               if (Array.isArray(aliases)) {
-                       Array.prototype.push.apply(env.classes, aliases);
-               } else {
-                       env.classes.push(aliases);
+       Token.stringify = function stringify(o, language) {
+               if (typeof o == 'string') {
+                       return o;
+               }
+               if (Array.isArray(o)) {
+                       var s = '';
+                       o.forEach(function (e) {
+                               s += stringify(e, language);
+                       });
+                       return s;
                }
-       }
 
-       _.hooks.run('wrap', env);
+               var env = {
+                       type: o.type,
+                       content: stringify(o.content, language),
+                       tag: 'span',
+                       classes: ['token', o.type],
+                       attributes: {},
+                       language: language
+               };
 
-       var attributes = '';
-       for (var name in env.attributes) {
-               attributes += ' ' + name + '="' + (env.attributes[name] || '').replace(/"/g, '&quot;') + '"';
-       }
+               var aliases = o.alias;
+               if (aliases) {
+                       if (Array.isArray(aliases)) {
+                               Array.prototype.push.apply(env.classes, aliases);
+                       } else {
+                               env.classes.push(aliases);
+                       }
+               }
 
-       return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + attributes + '>' + env.content + 
'</' + env.tag + '>';
-};
+               _.hooks.run('wrap', env);
 
-/**
- * @param {RegExp} pattern
- * @param {number} pos
- * @param {string} text
- * @param {boolean} lookbehind
- * @returns {RegExpExecArray | null}
- */
-function matchPattern(pattern, pos, text, lookbehind) {
-       pattern.lastIndex = pos;
-       var match = pattern.exec(text);
-       if (match && lookbehind && match[1]) {
-               // change the match to remove the text matched by the Prism lookbehind group
-               var lookbehindLength = match[1].length;
-               match.index += lookbehindLength;
-               match[0] = match[0].slice(lookbehindLength);
-       }
-       return match;
-}
-
-/**
- * @param {string} text
- * @param {LinkedList<string | Token>} tokenList
- * @param {any} grammar
- * @param {LinkedListNode<string | Token>} startNode
- * @param {number} startPos
- * @param {RematchOptions} [rematch]
- * @returns {void}
- * @private
- *
- * @typedef RematchOptions
- * @property {string} cause
- * @property {number} reach
- */
-function matchGrammar(text, tokenList, grammar, startNode, startPos, rematch) {
-       for (var token in grammar) {
-               if (!grammar.hasOwnProperty(token) || !grammar[token]) {
-                       continue;
+               var attributes = '';
+               for (var name in env.attributes) {
+                       attributes += ' ' + name + '="' + (env.attributes[name] || '').replace(/"/g, 
'&quot;') + '"';
                }
 
-               var patterns = grammar[token];
-               patterns = Array.isArray(patterns) ? patterns : [patterns];
-
-               for (var j = 0; j < patterns.length; ++j) {
-                       if (rematch && rematch.cause == token + ',' + j) {
-                               return;
-                       }
+               return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + attributes + '>' + 
env.content + '</' + env.tag + '>';
+       };
 
-                       var patternObj = patterns[j],
-                               inside = patternObj.inside,
-                               lookbehind = !!patternObj.lookbehind,
-                               greedy = !!patternObj.greedy,
-                               alias = patternObj.alias;
+       /**
+        * @param {RegExp} pattern
+        * @param {number} pos
+        * @param {string} text
+        * @param {boolean} lookbehind
+        * @returns {RegExpExecArray | null}
+        */
+       function matchPattern(pattern, pos, text, lookbehind) {
+               pattern.lastIndex = pos;
+               var match = pattern.exec(text);
+               if (match && lookbehind && match[1]) {
+                       // change the match to remove the text matched by the Prism lookbehind group
+                       var lookbehindLength = match[1].length;
+                       match.index += lookbehindLength;
+                       match[0] = match[0].slice(lookbehindLength);
+               }
+               return match;
+       }
 
-                       if (greedy && !patternObj.pattern.global) {
-                               // Without the global flag, lastIndex won't work
-                               var flags = patternObj.pattern.toString().match(/[imsuy]*$/)[0];
-                               patternObj.pattern = RegExp(patternObj.pattern.source, flags + 'g');
+       /**
+        * @param {string} text
+        * @param {LinkedList<string | Token>} tokenList
+        * @param {any} grammar
+        * @param {LinkedListNode<string | Token>} startNode
+        * @param {number} startPos
+        * @param {RematchOptions} [rematch]
+        * @returns {void}
+        * @private
+        *
+        * @typedef RematchOptions
+        * @property {string} cause
+        * @property {number} reach
+        */
+       function matchGrammar(text, tokenList, grammar, startNode, startPos, rematch) {
+               for (var token in grammar) {
+                       if (!grammar.hasOwnProperty(token) || !grammar[token]) {
+                               continue;
                        }
 
-                       /** @type {RegExp} */
-                       var pattern = patternObj.pattern || patternObj;
-
-                       for ( // iterate the token list and keep track of the current token/string position
-                               var currentNode = startNode.next, pos = startPos;
-                               currentNode !== tokenList.tail;
-                               pos += currentNode.value.length, currentNode = currentNode.next
-                       ) {
+                       var patterns = grammar[token];
+                       patterns = Array.isArray(patterns) ? patterns : [patterns];
 
-                               if (rematch && pos >= rematch.reach) {
-                                       break;
+                       for (var j = 0; j < patterns.length; ++j) {
+                               if (rematch && rematch.cause == token + ',' + j) {
+                                       return;
                                }
 
-                               var str = currentNode.value;
+                               var patternObj = patterns[j];
+                               var inside = patternObj.inside;
+                               var lookbehind = !!patternObj.lookbehind;
+                               var greedy = !!patternObj.greedy;
+                               var alias = patternObj.alias;
 
-                               if (tokenList.length > text.length) {
-                                       // Something went terribly wrong, ABORT, ABORT!
-                                       return;
+                               if (greedy && !patternObj.pattern.global) {
+                                       // Without the global flag, lastIndex won't work
+                                       var flags = patternObj.pattern.toString().match(/[imsuy]*$/)[0];
+                                       patternObj.pattern = RegExp(patternObj.pattern.source, flags + 'g');
                                }
 
-                               if (str instanceof Token) {
-                                       continue;
-                               }
+                               /** @type {RegExp} */
+                               var pattern = patternObj.pattern || patternObj;
 
-                               var removeCount = 1; // this is the to parameter of removeBetween
-                               var match;
+                               for ( // iterate the token list and keep track of the current token/string 
position
+                                       var currentNode = startNode.next, pos = startPos;
+                                       currentNode !== tokenList.tail;
+                                       pos += currentNode.value.length, currentNode = currentNode.next
+                               ) {
 
-                               if (greedy) {
-                                       match = matchPattern(pattern, pos, text, lookbehind);
-                                       if (!match) {
+                                       if (rematch && pos >= rematch.reach) {
                                                break;
                                        }
 
-                                       var from = match.index;
-                                       var to = match.index + match[0].length;
-                                       var p = pos;
+                                       var str = currentNode.value;
 
-                                       // find the node that contains the match
-                                       p += currentNode.value.length;
-                                       while (from >= p) {
-                                               currentNode = currentNode.next;
-                                               p += currentNode.value.length;
+                                       if (tokenList.length > text.length) {
+                                               // Something went terribly wrong, ABORT, ABORT!
+                                               return;
                                        }
-                                       // adjust pos (and p)
-                                       p -= currentNode.value.length;
-                                       pos = p;
 
-                                       // the current node is a Token, then the match starts inside another 
Token, which is invalid
-                                       if (currentNode.value instanceof Token) {
+                                       if (str instanceof Token) {
                                                continue;
                                        }
 
-                                       // find the last node which is affected by this match
-                                       for (
-                                               var k = currentNode;
-                                               k !== tokenList.tail && (p < to || typeof k.value === 
'string');
-                                               k = k.next
-                                       ) {
-                                               removeCount++;
-                                               p += k.value.length;
-                                       }
-                                       removeCount--;
+                                       var removeCount = 1; // this is the to parameter of removeBetween
+                                       var match;
 
-                                       // replace with the new match
-                                       str = text.slice(pos, p);
-                                       match.index -= pos;
-                               } else {
-                                       match = matchPattern(pattern, 0, str, lookbehind);
-                                       if (!match) {
-                                               continue;
+                                       if (greedy) {
+                                               match = matchPattern(pattern, pos, text, lookbehind);
+                                               if (!match) {
+                                                       break;
+                                               }
+
+                                               var from = match.index;
+                                               var to = match.index + match[0].length;
+                                               var p = pos;
+
+                                               // find the node that contains the match
+                                               p += currentNode.value.length;
+                                               while (from >= p) {
+                                                       currentNode = currentNode.next;
+                                                       p += currentNode.value.length;
+                                               }
+                                               // adjust pos (and p)
+                                               p -= currentNode.value.length;
+                                               pos = p;
+
+                                               // the current node is a Token, then the match starts inside 
another Token, which is invalid
+                                               if (currentNode.value instanceof Token) {
+                                                       continue;
+                                               }
+
+                                               // find the last node which is affected by this match
+                                               for (
+                                                       var k = currentNode;
+                                                       k !== tokenList.tail && (p < to || typeof k.value === 
'string');
+                                                       k = k.next
+                                               ) {
+                                                       removeCount++;
+                                                       p += k.value.length;
+                                               }
+                                               removeCount--;
+
+                                               // replace with the new match
+                                               str = text.slice(pos, p);
+                                               match.index -= pos;
+                                       } else {
+                                               match = matchPattern(pattern, 0, str, lookbehind);
+                                               if (!match) {
+                                                       continue;
+                                               }
                                        }
-                               }
 
-                               var from = match.index,
-                                       matchStr = match[0],
-                                       before = str.slice(0, from),
-                                       after = str.slice(from + matchStr.length);
+                                       // eslint-disable-next-line no-redeclare
+                                       var from = match.index;
+                                       var matchStr = match[0];
+                                       var before = str.slice(0, from);
+                                       var after = str.slice(from + matchStr.length);
 
-                               var reach = pos + str.length;
-                               if (rematch && reach > rematch.reach) {
-                                       rematch.reach = reach;
-                               }
+                                       var reach = pos + str.length;
+                                       if (rematch && reach > rematch.reach) {
+                                               rematch.reach = reach;
+                                       }
 
-                               var removeFrom = currentNode.prev;
+                                       var removeFrom = currentNode.prev;
 
-                               if (before) {
-                                       removeFrom = addAfter(tokenList, removeFrom, before);
-                                       pos += before.length;
-                               }
+                                       if (before) {
+                                               removeFrom = addAfter(tokenList, removeFrom, before);
+                                               pos += before.length;
+                                       }
 
-                               removeRange(tokenList, removeFrom, removeCount);
+                                       removeRange(tokenList, removeFrom, removeCount);
 
-                               var wrapped = new Token(token, inside ? _.tokenize(matchStr, inside) : 
matchStr, alias, matchStr);
-                               currentNode = addAfter(tokenList, removeFrom, wrapped);
+                                       var wrapped = new Token(token, inside ? _.tokenize(matchStr, inside) 
: matchStr, alias, matchStr);
+                                       currentNode = addAfter(tokenList, removeFrom, wrapped);
 
-                               if (after) {
-                                       addAfter(tokenList, currentNode, after);
-                               }
+                                       if (after) {
+                                               addAfter(tokenList, currentNode, after);
+                                       }
 
-                               if (removeCount > 1) {
-                                       // at least one Token object was removed, so we have to do some 
rematching
-                                       // this can only happen if the current pattern is greedy
+                                       if (removeCount > 1) {
+                                               // at least one Token object was removed, so we have to do 
some rematching
+                                               // this can only happen if the current pattern is greedy
 
-                                       /** @type {RematchOptions} */
-                                       var nestedRematch = {
-                                               cause: token + ',' + j,
-                                               reach: reach
-                                       };
-                                       matchGrammar(text, tokenList, grammar, currentNode.prev, pos, 
nestedRematch);
+                                               /** @type {RematchOptions} */
+                                               var nestedRematch = {
+                                                       cause: token + ',' + j,
+                                                       reach: reach
+                                               };
+                                               matchGrammar(text, tokenList, grammar, currentNode.prev, pos, 
nestedRematch);
 
-                                       // the reach might have been extended because of the rematching
-                                       if (rematch && nestedRematch.reach > rematch.reach) {
-                                               rematch.reach = nestedRematch.reach;
+                                               // the reach might have been extended because of the 
rematching
+                                               if (rematch && nestedRematch.reach > rematch.reach) {
+                                                       rematch.reach = nestedRematch.reach;
+                                               }
                                        }
                                }
                        }
                }
        }
-}
 
-/**
- * @typedef LinkedListNode
- * @property {T} value
- * @property {LinkedListNode<T> | null} prev The previous node.
- * @property {LinkedListNode<T> | null} next The next node.
- * @template T
- * @private
- */
+       /**
+        * @typedef LinkedListNode
+        * @property {T} value
+        * @property {LinkedListNode<T> | null} prev The previous node.
+        * @property {LinkedListNode<T> | null} next The next node.
+        * @template T
+        * @private
+        */
 
-/**
- * @template T
- * @private
- */
-function LinkedList() {
-       /** @type {LinkedListNode<T>} */
-       var head = { value: null, prev: null, next: null };
-       /** @type {LinkedListNode<T>} */
-       var tail = { value: null, prev: head, next: null };
-       head.next = tail;
-
-       /** @type {LinkedListNode<T>} */
-       this.head = head;
-       /** @type {LinkedListNode<T>} */
-       this.tail = tail;
-       this.length = 0;
-}
+       /**
+        * @template T
+        * @private
+        */
+       function LinkedList() {
+               /** @type {LinkedListNode<T>} */
+               var head = { value: null, prev: null, next: null };
+               /** @type {LinkedListNode<T>} */
+               var tail = { value: null, prev: head, next: null };
+               head.next = tail;
+
+               /** @type {LinkedListNode<T>} */
+               this.head = head;
+               /** @type {LinkedListNode<T>} */
+               this.tail = tail;
+               this.length = 0;
+       }
 
-/**
- * Adds a new node with the given value to the list.
- * @param {LinkedList<T>} list
- * @param {LinkedListNode<T>} node
- * @param {T} value
- * @returns {LinkedListNode<T>} The added node.
- * @template T
- */
-function addAfter(list, node, value) {
-       // assumes that node != list.tail && values.length >= 0
-       var next = node.next;
+       /**
+        * Adds a new node with the given value to the list.
+        *
+        * @param {LinkedList<T>} list
+        * @param {LinkedListNode<T>} node
+        * @param {T} value
+        * @returns {LinkedListNode<T>} The added node.
+        * @template T
+        */
+       function addAfter(list, node, value) {
+               // assumes that node != list.tail && values.length >= 0
+               var next = node.next;
 
-       var newNode = { value: value, prev: node, next: next };
-       node.next = newNode;
-       next.prev = newNode;
-       list.length++;
+               var newNode = { value: value, prev: node, next: next };
+               node.next = newNode;
+               next.prev = newNode;
+               list.length++;
 
-       return newNode;
-}
-/**
- * Removes `count` nodes after the given node. The given node will not be removed.
- * @param {LinkedList<T>} list
- * @param {LinkedListNode<T>} node
- * @param {number} count
- * @template T
- */
-function removeRange(list, node, count) {
-       var next = node.next;
-       for (var i = 0; i < count && next !== list.tail; i++) {
-               next = next.next;
+               return newNode;
        }
-       node.next = next;
-       next.prev = node;
-       list.length -= i;
-}
-/**
- * @param {LinkedList<T>} list
- * @returns {T[]}
- * @template T
- */
-function toArray(list) {
-       var array = [];
-       var node = list.head.next;
-       while (node !== list.tail) {
-               array.push(node.value);
-               node = node.next;
+       /**
+        * Removes `count` nodes after the given node. The given node will not be removed.
+        *
+        * @param {LinkedList<T>} list
+        * @param {LinkedListNode<T>} node
+        * @param {number} count
+        * @template T
+        */
+       function removeRange(list, node, count) {
+               var next = node.next;
+               for (var i = 0; i < count && next !== list.tail; i++) {
+                       next = next.next;
+               }
+               node.next = next;
+               next.prev = node;
+               list.length -= i;
+       }
+       /**
+        * @param {LinkedList<T>} list
+        * @returns {T[]}
+        * @template T
+        */
+       function toArray(list) {
+               var array = [];
+               var node = list.head.next;
+               while (node !== list.tail) {
+                       array.push(node.value);
+                       node = node.next;
+               }
+               return array;
        }
-       return array;
-}
 
 
-if (!_self.document) {
-       if (!_self.addEventListener) {
-               // in Node.js
-               return _;
-       }
+       if (!_self.document) {
+               if (!_self.addEventListener) {
+                       // in Node.js
+                       return _;
+               }
 
-       if (!_.disableWorkerMessageHandler) {
-               // In worker
-               _self.addEventListener('message', function (evt) {
-                       var message = JSON.parse(evt.data),
-                               lang = message.language,
-                               code = message.code,
-                               immediateClose = message.immediateClose;
-
-                       _self.postMessage(_.highlight(code, _.languages[lang], lang));
-                       if (immediateClose) {
-                               _self.close();
-                       }
-               }, false);
-       }
+               if (!_.disableWorkerMessageHandler) {
+                       // In worker
+                       _self.addEventListener('message', function (evt) {
+                               var message = JSON.parse(evt.data);
+                               var lang = message.language;
+                               var code = message.code;
+                               var immediateClose = message.immediateClose;
+
+                               _self.postMessage(_.highlight(code, _.languages[lang], lang));
+                               if (immediateClose) {
+                                       _self.close();
+                               }
+                       }, false);
+               }
 
-       return _;
-}
+               return _;
+       }
 
-// Get current script and highlight
-var script = _.util.currentScript();
+       // Get current script and highlight
+       var script = _.util.currentScript();
 
-if (script) {
-       _.filename = script.src;
+       if (script) {
+               _.filename = script.src;
 
-       if (script.hasAttribute('data-manual')) {
-               _.manual = true;
+               if (script.hasAttribute('data-manual')) {
+                       _.manual = true;
+               }
        }
-}
 
-function highlightAutomaticallyCallback() {
-       if (!_.manual) {
-               _.highlightAll();
+       function highlightAutomaticallyCallback() {
+               if (!_.manual) {
+                       _.highlightAll();
+               }
        }
-}
 
-if (!_.manual) {
-       // If the document state is "loading", then we'll use DOMContentLoaded.
-       // If the document state is "interactive" and the prism.js script is deferred, then we'll also use the
-       // DOMContentLoaded event because there might be some plugins or languages which have also been 
deferred and they
-       // might take longer one animation frame to execute which can create a race condition where only some 
plugins have
-       // been loaded when Prism.highlightAll() is executed, depending on how fast resources are loaded.
-       // See https://github.com/PrismJS/prism/issues/2102
-       var readyState = document.readyState;
-       if (readyState === 'loading' || readyState === 'interactive' && script && script.defer) {
-               document.addEventListener('DOMContentLoaded', highlightAutomaticallyCallback);
-       } else {
-               if (window.requestAnimationFrame) {
-                       window.requestAnimationFrame(highlightAutomaticallyCallback);
+       if (!_.manual) {
+               // If the document state is "loading", then we'll use DOMContentLoaded.
+               // If the document state is "interactive" and the prism.js script is deferred, then we'll 
also use the
+               // DOMContentLoaded event because there might be some plugins or languages which have also 
been deferred and they
+               // might take longer one animation frame to execute which can create a race condition where 
only some plugins have
+               // been loaded when Prism.highlightAll() is executed, depending on how fast resources are 
loaded.
+               // See https://github.com/PrismJS/prism/issues/2102
+               var readyState = document.readyState;
+               if (readyState === 'loading' || readyState === 'interactive' && script && script.defer) {
+                       document.addEventListener('DOMContentLoaded', highlightAutomaticallyCallback);
                } else {
-                       window.setTimeout(highlightAutomaticallyCallback, 16);
+                       if (window.requestAnimationFrame) {
+                               window.requestAnimationFrame(highlightAutomaticallyCallback);
+                       } else {
+                               window.setTimeout(highlightAutomaticallyCallback, 16);
+                       }
                }
        }
-}
 
-return _;
+       return _;
 
-})(_self);
+}(_self));
 
 if (typeof module !== 'undefined' && module.exports) {
        module.exports = Prism;
@@ -1176,7 +1194,7 @@ if (typeof global !== 'undefined') {
  * each another.
  * @global
  * @public
-*/
+ */
 
 /**
  * @typedef Grammar
@@ -1194,7 +1212,7 @@ if (typeof global !== 'undefined') {
  * @returns {void}
  * @global
  * @public
-*/
+ */
 
 /**
  * @callback HookCallback
@@ -1239,6 +1257,7 @@ Prism.languages.markup = {
                                        'namespace': /^[^\s>\/:]+:/
                                }
                        },
+                       'special-attr': [],
                        'attr-value': {
                                pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,
                                inside: {
@@ -1325,6 +1344,49 @@ Object.defineProperty(Prism.languages.markup.tag, 'addInlined', {
                Prism.languages.insertBefore('markup', 'cdata', def);
        }
 });
+Object.defineProperty(Prism.languages.markup.tag, 'addAttribute', {
+       /**
+        * Adds an pattern to highlight languages embedded in HTML attributes.
+        *
+        * An example of an inlined language is CSS with `style` attributes.
+        *
+        * @param {string} attrName The name of the tag that contains the inlined language. This name will be 
treated as
+        * case insensitive.
+        * @param {string} lang The language key.
+        * @example
+        * addAttribute('style', 'css');
+        */
+       value: function (attrName, lang) {
+               Prism.languages.markup.tag.inside['special-attr'].push({
+                       pattern: RegExp(
+                               /(^|["'\s])/.source + '(?:' + attrName + ')' + 
/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,
+                               'i'
+                       ),
+                       lookbehind: true,
+                       inside: {
+                               'attr-name': /^[^\s=]+/,
+                               'attr-value': {
+                                       pattern: /=[\s\S]+/,
+                                       inside: {
+                                               'value': {
+                                                       pattern: /(=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,
+                                                       lookbehind: true,
+                                                       alias: [lang, 'language-' + lang],
+                                                       inside: Prism.languages[lang]
+                                               },
+                                               'punctuation': [
+                                                       {
+                                                               pattern: /^=/,
+                                                               alias: 'attr-equals'
+                                                       },
+                                                       /"|'/
+                                               ]
+                                       }
+                               }
+                       }
+               });
+       }
+});
 
 Prism.languages.html = Prism.languages.markup;
 Prism.languages.mathml = Prism.languages.markup;
@@ -1386,34 +1448,7 @@ Prism.languages.rss = Prism.languages.xml;
        var markup = Prism.languages.markup;
        if (markup) {
                markup.tag.addInlined('style', 'css');
-
-               Prism.languages.insertBefore('inside', 'attr-value', {
-                       'style-attr': {
-                               pattern: /(^|["'\s])style\s*=\s*(?:"[^"]*"|'[^']*')/i,
-                               lookbehind: true,
-                               inside: {
-                                       'attr-value': {
-                                               pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,
-                                               inside: {
-                                                       'style': {
-                                                               pattern: /(["'])[\s\S]+(?=["']$)/,
-                                                               lookbehind: true,
-                                                               alias: 'language-css',
-                                                               inside: Prism.languages.css
-                                                       },
-                                                       'punctuation': [
-                                                               {
-                                                                       pattern: /^=/,
-                                                                       alias: 'attr-equals'
-                                                               },
-                                                               /"|'/
-                                                       ]
-                                               }
-                                       },
-                                       'attr-name': /^style/i
-                               }
-                       }
-               }, markup.tag);
+               markup.tag.addAttribute('style', 'css');
        }
 
 }(Prism));
@@ -1464,7 +1499,7 @@ Prism.languages.javascript = Prism.languages.extend('clike', {
                        lookbehind: true
                },
                {
-                       pattern: 
/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,
+                       pattern: 
/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,
                        lookbehind: true
                },
        ],
@@ -1478,7 +1513,7 @@ Prism.languages.javascript['class-name'][0].pattern = /(\b(?:class|interface|ext
 
 Prism.languages.insertBefore('javascript', 'keyword', {
        'regex': {
-               pattern: 
/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,
+               pattern: 
/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,
                lookbehind: true,
                greedy: true,
                inside: {
@@ -1522,6 +1557,11 @@ Prism.languages.insertBefore('javascript', 'keyword', {
 });
 
 Prism.languages.insertBefore('javascript', 'string', {
+       'hashbang': {
+               pattern: /^#!.*/,
+               greedy: true,
+               alias: 'comment'
+       },
        'template-string': {
                pattern: /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,
                greedy: true,
@@ -1548,11 +1588,18 @@ Prism.languages.insertBefore('javascript', 'string', {
 
 if (Prism.languages.markup) {
        Prism.languages.markup.tag.addInlined('script', 'javascript');
+
+       // add attribute support for all DOM events.
+       // https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events
+       Prism.languages.markup.tag.addAttribute(
+               
/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,
+               'javascript'
+       );
 }
 
 Prism.languages.js = Prism.languages.javascript;
 
-(function(Prism) {
+(function (Prism) {
        // $ set | grep '^[A-Z][^[:space:]]*=' | cut -d= -f1 | tr '\n' '|'
        // + LC_ALL, RANDOM, REPLY, SECONDS.
        // + make sure PS1..4 are here as they are not always set,
@@ -1569,7 +1616,7 @@ Prism.languages.js = Prism.languages.javascript;
        var insideString = {
                'bash': commandAfterHeredoc,
                'environment': {
-                       pattern: RegExp("\\$" + envVars),
+                       pattern: RegExp('\\$' + envVars),
                        alias: 'constant'
                },
                'variable': [
@@ -1609,7 +1656,7 @@ Prism.languages.js = Prism.languages.javascript;
                                        'operator': /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,
                                        'punctuation': /[\[\]]/,
                                        'environment': {
-                                               pattern: RegExp("(\\{)" + envVars),
+                                               pattern: RegExp('(\\{)' + envVars),
                                                lookbehind: true,
                                                alias: 'constant'
                                        }
@@ -1637,13 +1684,13 @@ Prism.languages.js = Prism.languages.javascript;
                        // but not “foo {”
                        {
                                // a) and c)
-                               pattern: /(\bfunction\s+)\w+(?=(?:\s*\(?:\s*\))?\s*\{)/,
+                               pattern: /(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,
                                lookbehind: true,
                                alias: 'function'
                        },
                        {
                                // b)
-                               pattern: /\b\w+(?=\s*\(\s*\)\s*\{)/,
+                               pattern: /\b[\w-]+(?=\s*\(\s*\)\s*\{)/,
                                alias: 'function'
                        }
                ],
@@ -1659,7 +1706,7 @@ Prism.languages.js = Prism.languages.javascript;
                        pattern: /(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,
                        inside: {
                                'environment': {
-                                       pattern: RegExp("(^|[\\s;|&]|[<>]\\()" + envVars),
+                                       pattern: RegExp('(^|[\\s;|&]|[<>]\\()' + envVars),
                                        lookbehind: true,
                                        alias: 'constant'
                                }
@@ -1687,14 +1734,29 @@ Prism.languages.js = Prism.languages.javascript;
                        },
                        // “Normal” string
                        {
-                               pattern: 
/(^|[^\\](?:\\\\)*)(["'])(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|(?!\2)[^\\`$])*\2/,
+                               // https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
+                               pattern: 
/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,
                                lookbehind: true,
                                greedy: true,
                                inside: insideString
+                       },
+                       {
+                               // https://www.gnu.org/software/bash/manual/html_node/Single-Quotes.html
+                               pattern: /(^|[^$\\])'[^']*'/,
+                               lookbehind: true,
+                               greedy: true
+                       },
+                       {
+                               // https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html
+                               pattern: /\$'(?:[^'\\]|\\[\s\S])*'/,
+                               greedy: true,
+                               inside: {
+                                       'entity': insideString.entity
+                               }
                        }
                ],
                'environment': {
-                       pattern: RegExp("\\$?" + envVars),
+                       pattern: RegExp('\\$?' + envVars),
                        alias: 'constant'
                },
                'variable': insideString.variable,
@@ -1758,12 +1820,12 @@ Prism.languages.js = Prism.languages.javascript;
                'number'
        ];
        var inside = insideString.variable[1].inside;
-       for(var i = 0; i < toBeCopied.length; i++) {
+       for (var i = 0; i < toBeCopied.length; i++) {
                inside[toBeCopied[i]] = Prism.languages.bash[toBeCopied[i]];
        }
 
        Prism.languages.shell = Prism.languages.bash;
-})(Prism);
+}(Prism));
 
 Prism.languages.c = Prism.languages.extend('clike', {
        'comment': {
@@ -1831,7 +1893,8 @@ delete Prism.languages.c['boolean'];
 
 (function (Prism) {
 
-       var keyword = 
/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char8_t|char16_t|char32_t|class|compl|concept|const|consteval|constexpr|constinit|const_cast|continue|co_await|co_return|co_yield|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/;
+       var keyword = 
/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char8_t|char16_t|char32_t|class|compl|concept|const|consteval|constexpr|constinit|const_cast|continue|co_await|co_return|co_yield|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/;
+       var modName = /\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace(/<keyword>/g, function () { 
return keyword.source; });
 
        Prism.languages.cpp = Prism.languages.extend('c', {
                'class-name': [
@@ -1857,11 +1920,31 @@ delete Prism.languages.c['boolean'];
                        pattern: 
/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,
                        greedy: true
                },
-               'operator': 
/>>=?|<<=?|->|([-+&|:])\1|[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,
+               'operator': 
/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,
                'boolean': /\b(?:true|false)\b/
        });
 
        Prism.languages.insertBefore('cpp', 'string', {
+               'module': {
+                       // https://en.cppreference.com/w/cpp/language/modules
+                       pattern: RegExp(
+                               /(\b(?:module|import)\s+)/.source +
+                               '(?:' +
+                               // header-name
+                               /"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source +
+                               '|' +
+                               // module name or partition or both
+                               
/<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace(/<mod-name>/g, function () { return modName; 
}) +
+                               ')'
+                       ),
+                       lookbehind: true,
+                       greedy: true,
+                       inside: {
+                               'string': /^[<"][\s\S]+/,
+                               'operator': /:/,
+                               'punctuation': /\./
+                       }
+               },
                'raw-string': {
                        pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,
                        alias: 'string',
@@ -1869,6 +1952,27 @@ delete Prism.languages.c['boolean'];
                }
        });
 
+       Prism.languages.insertBefore('cpp', 'keyword', {
+               'generic-function': {
+                       pattern: /\b[a-z_]\w*\s*<(?:[^<>]|<(?:[^<>])*>)*>(?=\s*\()/i,
+                       inside: {
+                               'function': /^\w+/,
+                               'generic': {
+                                       pattern: /<[\s\S]+/,
+                                       alias: 'class-name',
+                                       inside: Prism.languages.cpp
+                               }
+                       }
+               }
+       });
+
+       Prism.languages.insertBefore('cpp', 'operator', {
+               'double-colon': {
+                       pattern: /::/,
+                       alias: 'punctuation'
+               }
+       });
+
        Prism.languages.insertBefore('cpp', 'class-name', {
                // the base clause is an optional list of parent classes
                // https://en.cppreference.com/w/cpp/language/class
@@ -1879,108 +1983,109 @@ delete Prism.languages.c['boolean'];
                        inside: Prism.languages.extend('cpp', {})
                }
        });
-       Prism.languages.insertBefore('inside', 'operator', {
+
+       Prism.languages.insertBefore('inside', 'double-colon', {
                // All untokenized words that are not namespaces should be class names
                'class-name': /\b[a-z_]\w*\b(?!\s*::)/i
        }, Prism.languages.cpp['base-clause']);
 
 }(Prism));
 
-(function(Prism) {
-
-// Ignore comments starting with { to privilege string interpolation highlighting
-var comment = /#(?!\{).+/,
-    interpolation = {
-       pattern: /#\{[^}]+\}/,
-       alias: 'variable'
-    };
+(function (Prism) {
 
-Prism.languages.coffeescript = Prism.languages.extend('javascript', {
-       'comment': comment,
-       'string': [
+       // Ignore comments starting with { to privilege string interpolation highlighting
+       var comment = /#(?!\{).+/;
+       var interpolation = {
+               pattern: /#\{[^}]+\}/,
+               alias: 'variable'
+       };
 
-               // Strings are multiline
-               {
-                       pattern: /'(?:\\[\s\S]|[^\\'])*'/,
-                       greedy: true
-               },
+       Prism.languages.coffeescript = Prism.languages.extend('javascript', {
+               'comment': comment,
+               'string': [
 
-               {
                        // Strings are multiline
-                       pattern: /"(?:\\[\s\S]|[^\\"])*"/,
-                       greedy: true,
-                       inside: {
-                               'interpolation': interpolation
+                       {
+                               pattern: /'(?:\\[\s\S]|[^\\'])*'/,
+                               greedy: true
+                       },
+
+                       {
+                               // Strings are multiline
+                               pattern: /"(?:\\[\s\S]|[^\\"])*"/,
+                               greedy: true,
+                               inside: {
+                                       'interpolation': interpolation
+                               }
                        }
+               ],
+               'keyword': 
/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,
+               'class-member': {
+                       pattern: /@(?!\d)\w+/,
+                       alias: 'variable'
                }
-       ],
-       'keyword': 
/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,
-       'class-member': {
-               pattern: /@(?!\d)\w+/,
-               alias: 'variable'
-       }
-});
-
-Prism.languages.insertBefore('coffeescript', 'comment', {
-       'multiline-comment': {
-               pattern: /###[\s\S]+?###/,
-               alias: 'comment'
-       },
+       });
 
-       // Block regexp can contain comments and interpolation
-       'block-regex': {
-               pattern: /\/{3}[\s\S]*?\/{3}/,
-               alias: 'regex',
-               inside: {
-                       'comment': comment,
-                       'interpolation': interpolation
-               }
-       }
-});
+       Prism.languages.insertBefore('coffeescript', 'comment', {
+               'multiline-comment': {
+                       pattern: /###[\s\S]+?###/,
+                       alias: 'comment'
+               },
 
-Prism.languages.insertBefore('coffeescript', 'string', {
-       'inline-javascript': {
-               pattern: /`(?:\\[\s\S]|[^\\`])*`/,
-               inside: {
-                       'delimiter': {
-                               pattern: /^`|`$/,
-                               alias: 'punctuation'
-                       },
-                       'script': {
-                               pattern: /[\s\S]+/,
-                               alias: 'language-javascript',
-                               inside: Prism.languages.javascript
+               // Block regexp can contain comments and interpolation
+               'block-regex': {
+                       pattern: /\/{3}[\s\S]*?\/{3}/,
+                       alias: 'regex',
+                       inside: {
+                               'comment': comment,
+                               'interpolation': interpolation
                        }
                }
-       },
+       });
 
-       // Block strings
-       'multiline-string': [
-               {
-                       pattern: /'''[\s\S]*?'''/,
-                       greedy: true,
-                       alias: 'string'
-               },
-               {
-                       pattern: /"""[\s\S]*?"""/,
-                       greedy: true,
-                       alias: 'string',
+       Prism.languages.insertBefore('coffeescript', 'string', {
+               'inline-javascript': {
+                       pattern: /`(?:\\[\s\S]|[^\\`])*`/,
                        inside: {
-                               interpolation: interpolation
+                               'delimiter': {
+                                       pattern: /^`|`$/,
+                                       alias: 'punctuation'
+                               },
+                               'script': {
+                                       pattern: /[\s\S]+/,
+                                       alias: 'language-javascript',
+                                       inside: Prism.languages.javascript
+                               }
                        }
-               }
-       ]
+               },
 
-});
+               // Block strings
+               'multiline-string': [
+                       {
+                               pattern: /'''[\s\S]*?'''/,
+                               greedy: true,
+                               alias: 'string'
+                       },
+                       {
+                               pattern: /"""[\s\S]*?"""/,
+                               greedy: true,
+                               alias: 'string',
+                               inside: {
+                                       interpolation: interpolation
+                               }
+                       }
+               ]
 
-Prism.languages.insertBefore('coffeescript', 'keyword', {
-       // Object property
-       'property': /(?!\d)\w+(?=\s*:(?!:))/
-});
+       });
+
+       Prism.languages.insertBefore('coffeescript', 'keyword', {
+               // Object property
+               'property': /(?!\d)\w+(?=\s*:(?!:))/
+       });
 
-delete Prism.languages.coffeescript['template-string'];
+       delete Prism.languages.coffeescript['template-string'];
 
-Prism.languages.coffee = Prism.languages.coffeescript;
+       Prism.languages.coffee = Prism.languages.coffeescript;
 }(Prism));
 
 /**
@@ -2025,22 +2130,25 @@ Prism.languages.coffee = Prism.languages.coffeescript;
                'regex': [
                        {
                                pattern: RegExp(/%r/.source + '(?:' + [
-                                       /([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[gim]{0,3}/.source,
-                                       /\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/.source,
+                                       /([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,
+                                       /\((?:[^()\\]|\\[\s\S])*\)/.source,
                                        // Here we need to specifically allow interpolation
-                                       /\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/.source,
-                                       /\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/.source,
-                                       /<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/.source
-                               ].join('|') + ')'),
+                                       /\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/.source,
+                                       /\[(?:[^\[\]\\]|\\[\s\S])*\]/.source,
+                                       /<(?:[^<>\\]|\\[\s\S])*>/.source
+                               ].join('|') + ')' + /[egimnosux]{0,6}/.source),
                                greedy: true,
                                inside: {
                                        'interpolation': interpolation
                                }
                        },
                        {
-                               pattern: 
/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[gim]{0,3}(?=\s*(?:$|[\r\n,.;})]))/,
+                               pattern: 
/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,
                                lookbehind: true,
-                               greedy: true
+                               greedy: true,
+                               inside: {
+                                       'interpolation': interpolation
+                               }
                        }
                ],
                'variable': /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,
@@ -2084,13 +2192,42 @@ Prism.languages.coffee = Prism.languages.coffeescript;
                        inside: {
                                'interpolation': interpolation
                        }
+               },
+               {
+                       pattern: /<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,
+                       alias: 'heredoc-string',
+                       greedy: true,
+                       inside: {
+                               'delimiter': {
+                                       pattern: /^<<[-~]?[a-z_]\w*|[a-z_]\w*$/i,
+                                       alias: 'symbol',
+                                       inside: {
+                                               'punctuation': /^<<[-~]?/
+                                       }
+                               },
+                               'interpolation': interpolation
+                       }
+               },
+               {
+                       pattern: /<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,
+                       alias: 'heredoc-string',
+                       greedy: true,
+                       inside: {
+                               'delimiter': {
+                                       pattern: /^<<[-~]?'[a-z_]\w*'|[a-z_]\w*$/i,
+                                       alias: 'symbol',
+                                       inside: {
+                                               'punctuation': /^<<[-~]?'|'$/,
+                                       }
+                               }
+                       }
                }
        ];
 
        Prism.languages.rb = Prism.languages.ruby;
 }(Prism));
 
-(function(Prism) {
+(function (Prism) {
        Prism.languages.crystal = Prism.languages.extend('ruby', {
                keyword: [
                        
/\b(?:abstract|alias|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|rescue|return|require|select|self|sizeof|struct|super|then|type|typeof|uninitialized|union|unless|until|when|while|with|yield|__DIR__|__END_LINE__|__FILE__|__LINE__)\b/,
@@ -2117,26 +2254,26 @@ Prism.languages.coffee = Prism.languages.coffeescript;
                },
 
                expansion: [
-               {
-                       pattern: /\{\{.+?\}\}/,
-                       inside: {
-                               delimiter: {
-                                       pattern: /^\{\{|\}\}$/,
-                                       alias: 'tag'
-                               },
-                               rest: Prism.languages.crystal
-                       }
-               },
-               {
-                       pattern: /\{%.+?%\}/,
-                       inside: {
-                               delimiter: {
-                                       pattern: /^\{%|%\}$/,
-                                       alias: 'tag'
-                               },
-                               rest: Prism.languages.crystal
+                       {
+                               pattern: /\{\{.+?\}\}/,
+                               inside: {
+                                       delimiter: {
+                                               pattern: /^\{\{|\}\}$/,
+                                               alias: 'tag'
+                                       },
+                                       rest: Prism.languages.crystal
+                               }
+                       },
+                       {
+                               pattern: /\{%.+?%\}/,
+                               inside: {
+                                       delimiter: {
+                                               pattern: /^\{%|%\}$/,
+                                               alias: 'tag'
+                                       },
+                                       rest: Prism.languages.crystal
+                               }
                        }
-               }
                ]
        });
 
@@ -2223,30 +2360,73 @@ Prism.languages.insertBefore('d', 'function', {
        }
 });
 
-Prism.languages.dart = Prism.languages.extend('clike', {
-       'string': [
-               {
-                       pattern: /r?("""|''')[\s\S]*?\1/,
-                       greedy: true
-               },
-               {
-                       pattern: /r?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,
-                       greedy: true
-               }
-       ],
-       'keyword': [
+(function (Prism) {
+       var keywords = [
                /\b(?:async|sync|yield)\*/,
-               
/\b(?:abstract|assert|async|await|break|case|catch|class|const|continue|covariant|default|deferred|do|dynamic|else|enum|export|extension|external|extends|factory|final|finally|for|Function|get|hide|if|implements|interface|import|in|library|mixin|new|null|on|operator|part|rethrow|return|set|show|static|super|switch|sync|this|throw|try|typedef|var|void|while|with|yield)\b/
-       ],
-       'operator': /\bis!|\b(?:as|is)\b|\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?/
-});
+               
/\b(?:abstract|assert|async|await|break|case|catch|class|const|continue|covariant|default|deferred|do|dynamic|else|enum|export|extension|external|extends|factory|final|finally|for|get|hide|if|implements|interface|import|in|library|mixin|new|null|on|operator|part|rethrow|return|set|show|static|super|switch|sync|this|throw|try|typedef|var|void|while|with|yield)\b/
+       ];
 
-Prism.languages.insertBefore('dart','function',{
-       'metadata': {
-               pattern: /@\w+/,
-               alias: 'symbol'
-       }
-});
+       // Handles named imports, such as http.Client
+       var packagePrefix = /(^|[^\w.])(?:[a-z]\w*\s*\.\s*)*(?:[A-Z]\w*\s*\.\s*)*/.source;
+
+       // based on the dart naming conventions
+       var className = {
+               pattern: RegExp(packagePrefix + /[A-Z](?:[\d_A-Z]*[a-z]\w*)?\b/.source),
+               lookbehind: true,
+               inside: {
+                       'namespace': {
+                               pattern: /^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,
+                               inside: {
+                                       'punctuation': /\./
+                               }
+                       },
+               }
+       };
+
+       Prism.languages.dart = Prism.languages.extend('clike', {
+               'string': [
+                       {
+                               pattern: /r?("""|''')[\s\S]*?\1/,
+                               greedy: true
+                       },
+                       {
+                               pattern: /r?(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,
+                               greedy: true
+                       }
+               ],
+               'class-name': [
+                       className,
+                       {
+                               // variables and parameters
+                               // this to support class names (or generic parameters) which do not contain a 
lower case letter (also works for methods)
+                               pattern: RegExp(packagePrefix + /[A-Z]\w*(?=\s+\w+\s*[;,=()])/.source),
+                               lookbehind: true,
+                               inside: className.inside
+                       }
+               ],
+               'keyword': keywords,
+               'operator': /\bis!|\b(?:as|is)\b|\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?/
+       });
+
+       Prism.languages.insertBefore('dart', 'function', {
+               'metadata': {
+                       pattern: /@\w+/,
+                       alias: 'symbol'
+               }
+       });
+
+       Prism.languages.insertBefore('dart', 'class-name', {
+               'generics': {
+                       pattern: /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/,
+                       inside: {
+                               'class-name': className,
+                               'keyword': keywords,
+                               'punctuation': /[<>(),.:]/,
+                               'operator': /[?&|]/
+                       }
+               },
+       });
+}(Prism));
 
 (function (Prism) {
 
@@ -2285,8 +2465,8 @@ Prism.languages.insertBefore('dart','function',{
                if (!/^\w+$/.test(name)) { // "deleted-sign" -> "deleted"
                        alias.push(/\w+/.exec(name)[0]);
                }
-               if (name === "diff") {
-                       alias.push("bold");
+               if (name === 'diff') {
+                       alias.push('bold');
                }
 
                Prism.languages.diff[name] = {
@@ -2354,8 +2534,9 @@ Prism.languages.insertBefore('dart','function',{
                                        var placeholder;
 
                                        // Check for existing strings
-                                       while (env.code.indexOf(placeholder = getPlaceholder(language, i)) 
!== -1)
+                                       while (env.code.indexOf(placeholder = getPlaceholder(language, i)) 
!== -1) {
                                                ++i;
+                                       }
 
                                        // Create a sparse array
                                        tokenStack[i] = match;
@@ -2496,10 +2677,20 @@ Prism.languages.insertBefore('dart','function',{
                markupTemplating.tokenizePlaceholders(env, 'jinja2');
        });
 
-})(Prism);
+}(Prism));
 
 Prism.languages.elixir = {
-       'comment': /#.*/m,
+       'doc': {
+               pattern: 
/@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/,
+               inside: {
+                       'attribute': /^@\w+/,
+                       'string': /['"][\s\S]+/
+               }
+       },
+       'comment': {
+               pattern: /#.*/m,
+               greedy: true
+       },
        // ~r"""foo""" (multi-line), ~r'''foo''' (multi-line), ~r/foo/, ~r|foo|, ~r"foo", ~r'foo', ~r(foo), 
~r[foo], ~r{foo}, ~r<foo>
        'regex': {
                pattern: 
/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,
@@ -2536,14 +2727,12 @@ Prism.languages.elixir = {
                lookbehind: true,
                alias: 'symbol'
        },
+       'module': {
+               pattern: /\b[A-Z]\w*\b/,
+               alias: 'class-name'
+       },
        // Look-ahead prevents bad highlighting of the :: operator
        'attr-name': /\w+\??:(?!:)/,
-       'capture': {
-               // Look-behind prevents bad highlighting of the && operator
-               pattern: /(^|[^&])&(?:[^&\s\d()][^\s()]*|(?=\())/,
-               lookbehind: true,
-               alias: 'function'
-       },
        'argument': {
                // Look-behind prevents bad highlighting of the && operator
                pattern: /(^|[^&])&\d+/,
@@ -2554,8 +2743,9 @@ Prism.languages.elixir = {
                pattern: /@\w+/,
                alias: 'variable'
        },
+       'function': /\b[_a-zA-Z]\w*[?!]?(?:(?=\s*(?:\.\s*)?\()|(?=\/\d+))/,
        'number': /\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,
-       'keyword': 
/\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct|delegate)?|do|else|end|fn|for|if|import|not|or|require|rescue|try|unless|use|when)\b/,
+       'keyword': 
/\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct|delegate)?|do|else|end|fn|for|if|import|not|or|raise|require|rescue|try|unless|use|when)\b/,
        'boolean': /\b(?:true|false|nil)\b/,
        'operator': [
                /\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,
@@ -2573,19 +2763,7 @@ Prism.languages.elixir = {
        'punctuation': /<<|>>|[.,%\[\]{}()]/
 };
 
-Prism.languages.insertBefore('elixir', 'keyword', {
-       'module': {
-               pattern: /\b(defmodule\s)[A-Z][\w.\\]+/,
-               lookbehind: true,
-               alias: 'class-name'
-       },
-       'function': {
-               pattern: /\b(defp?\s)[\w.\\]+/,
-               lookbehind: true
-       }
-});
-
-Prism.languages.elixir.string.forEach(function(o) {
+Prism.languages.elixir.string.forEach(function (o) {
        o.inside = {
                'interpolation': {
                        pattern: /#\{[^}]+\}/,
@@ -2701,7 +2879,7 @@ Prism.languages.insertBefore('groovy', 'function', {
 });
 
 // Handle string interpolation
-Prism.hooks.add('wrap', function(env) {
+Prism.hooks.add('wrap', function (env) {
        if (env.language === 'groovy' && env.type === 'string') {
                var delimiter = env.content[0];
 
@@ -2791,7 +2969,7 @@ Prism.hooks.add('wrap', function(env) {
                        alias: 'punctuation'
                },
                'generics': {
-                       pattern: /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/,
+                       pattern: 
/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,
                        inside: {
                                'class-name': className,
                                'keyword': keywords,
@@ -2893,7 +3071,7 @@ Prism.languages.julia = {
                'operator': 
/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/
        });
 
-       delete Prism.languages.kotlin["class-name"];
+       delete Prism.languages.kotlin['class-name'];
 
        Prism.languages.insertBefore('kotlin', 'string', {
                'raw-string': {
@@ -2940,6 +3118,71 @@ Prism.languages.julia = {
        Prism.languages.kts = Prism.languages.kotlin;
 }(Prism));
 
+(function (Prism) {
+       var funcPattern = /\\(?:[^a-z()[\]]|[a-z*]+)/i;
+       var insideEqu = {
+               'equation-command': {
+                       pattern: funcPattern,
+                       alias: 'regex'
+               }
+       };
+
+       Prism.languages.latex = {
+               'comment': /%.*/m,
+               // the verbatim environment prints whitespace to the document
+               'cdata': {
+                       pattern: /(\\begin\{((?:verbatim|lstlisting)\*?)\})[\s\S]*?(?=\\end\{\2\})/,
+                       lookbehind: true
+               },
+               /*
+                * equations can be between $$ $$ or $ $ or \( \) or \[ \]
+                * (all are multiline)
+                */
+               'equation': [
+                       {
+                               pattern: 
/\$\$(?:\\[\s\S]|[^\\$])+\$\$|\$(?:\\[\s\S]|[^\\$])+\$|\\\([\s\S]*?\\\)|\\\[[\s\S]*?\\\]/,
+                               inside: insideEqu,
+                               alias: 'string'
+                       },
+                       {
+                               pattern: 
/(\\begin\{((?:equation|math|eqnarray|align|multline|gather)\*?)\})[\s\S]*?(?=\\end\{\2\})/,
+                               lookbehind: true,
+                               inside: insideEqu,
+                               alias: 'string'
+                       }
+               ],
+               /*
+                * arguments which are keywords or references are highlighted
+                * as keywords
+                */
+               'keyword': {
+                       pattern: 
/(\\(?:begin|end|ref|cite|label|usepackage|documentclass)(?:\[[^\]]+\])?\{)[^}]+(?=\})/,
+                       lookbehind: true
+               },
+               'url': {
+                       pattern: /(\\url\{)[^}]+(?=\})/,
+                       lookbehind: true
+               },
+               /*
+                * section or chapter headlines are highlighted as bold so that
+                * they stand out more
+                */
+               'headline': {
+                       pattern: 
/(\\(?:part|chapter|section|subsection|frametitle|subsubsection|paragraph|subparagraph|subsubparagraph|subsubsubparagraph)\*?(?:\[[^\]]+\])?\{)[^}]+(?=\}(?:\[[^\]]+\])?)/,
+                       lookbehind: true,
+                       alias: 'class-name'
+               },
+               'function': {
+                       pattern: funcPattern,
+                       alias: 'selector'
+               },
+               'punctuation': /[[\]{}&]/
+       };
+
+       Prism.languages.tex = Prism.languages.latex;
+       Prism.languages.context = Prism.languages.latex;
+}(Prism));
+
 Prism.languages.lua = {
        'comment': /^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m,
        // \z may be used to skip the following space
@@ -2978,17 +3221,58 @@ Prism.languages.matlab = {
        'punctuation': /\.{3}|[.,;\[\](){}!]/
 };
 
-Prism.languages.nginx = Prism.languages.extend('clike', {
-       'comment': {
-               pattern: /(^|[^"{\\])#.*/,
-               lookbehind: true
-       },
-       'keyword': 
/\b(?:CONTENT_|DOCUMENT_|GATEWAY_|HTTP_|HTTPS|if_not_empty|PATH_|QUERY_|REDIRECT_|REMOTE_|REQUEST_|SCGI|SCRIPT_|SERVER_|http|events|accept_mutex|accept_mutex_delay|access_log|add_after_body|add_before_body|add_header|addition_types|aio|alias|allow|ancient_browser|ancient_browser_value|auth|auth_basic|auth_basic_user_file|auth_http|auth_http_header|auth_http_timeout|autoindex|autoindex_exact_size|autoindex_localtime|break|charset|charset_map|charset_types|chunked_transfer_encoding|client_body_buffer_size|client_body_in_file_only|client_body_in_single_buffer|client_body_temp_path|client_body_timeout|client_header_buffer_size|client_header_timeout|client_max_body_size|connection_pool_size|create_full_put_path|daemon|dav_access|dav_methods|debug_connection|debug_points|default_type|deny|devpoll_changes|devpoll_events|directio|directio_alignment|disable_symlinks|empty_gif|env|epoll_events|error_log|error_page|expires|fastcgi_buffer_size|fastcgi_buffers|fastcgi_busy_buffers_si
 
ze|fastcgi_cache|fastcgi_cache_bypass|fastcgi_cache_key|fastcgi_cache_lock|fastcgi_cache_lock_timeout|fastcgi_cache_methods|fastcgi_cache_min_uses|fastcgi_cache_path|fastcgi_cache_purge|fastcgi_cache_use_stale|fastcgi_cache_valid|fastcgi_connect_timeout|fastcgi_hide_header|fastcgi_ignore_client_abort|fastcgi_ignore_headers|fastcgi_index|fastcgi_intercept_errors|fastcgi_keep_conn|fastcgi_max_temp_file_size|fastcgi_next_upstream|fastcgi_no_cache|fastcgi_param|fastcgi_pass|fastcgi_pass_header|fastcgi_read_timeout|fastcgi_redirect_errors|fastcgi_send_timeout|fastcgi_split_path_info|fastcgi_store|fastcgi_store_access|fastcgi_temp_file_write_size|fastcgi_temp_path|flv|geo|geoip_city|geoip_country|google_perftools_profiles|gzip|gzip_buffers|gzip_comp_level|gzip_disable|gzip_http_version|gzip_min_length|gzip_proxied|gzip_static|gzip_types|gzip_vary|if|if_modified_since|ignore_invalid_headers|image_filter|image_filter_buffer|image_filter_jpeg_quality|image_filter_sharpen|image_filter_transpa
 
rency|imap_capabilities|imap_client_buffer|include|index|internal|ip_hash|keepalive|keepalive_disable|keepalive_requests|keepalive_timeout|kqueue_changes|kqueue_events|large_client_header_buffers|limit_conn|limit_conn_log_level|limit_conn_zone|limit_except|limit_rate|limit_rate_after|limit_req|limit_req_log_level|limit_req_zone|limit_zone|lingering_close|lingering_time|lingering_timeout|listen|location|lock_file|log_format|log_format_combined|log_not_found|log_subrequest|map|map_hash_bucket_size|map_hash_max_size|master_process|max_ranges|memcached_buffer_size|memcached_connect_timeout|memcached_next_upstream|memcached_pass|memcached_read_timeout|memcached_send_timeout|merge_slashes|min_delete_depth|modern_browser|modern_browser_value|mp4|mp4_buffer_size|mp4_max_buffer_size|msie_padding|msie_refresh|multi_accept|open_file_cache|open_file_cache_errors|open_file_cache_min_uses|open_file_cache_valid|open_log_file_cache|optimize_server_names|override_charset|pcre_jit|perl|perl_modules|p
 
erl_require|perl_set|pid|pop3_auth|pop3_capabilities|port_in_redirect|post_action|postpone_output|protocol|proxy|proxy_buffer|proxy_buffer_size|proxy_buffering|proxy_buffers|proxy_busy_buffers_size|proxy_cache|proxy_cache_bypass|proxy_cache_key|proxy_cache_lock|proxy_cache_lock_timeout|proxy_cache_methods|proxy_cache_min_uses|proxy_cache_path|proxy_cache_use_stale|proxy_cache_valid|proxy_connect_timeout|proxy_cookie_domain|proxy_cookie_path|proxy_headers_hash_bucket_size|proxy_headers_hash_max_size|proxy_hide_header|proxy_http_version|proxy_ignore_client_abort|proxy_ignore_headers|proxy_intercept_errors|proxy_max_temp_file_size|proxy_method|proxy_next_upstream|proxy_no_cache|proxy_pass|proxy_pass_error_message|proxy_pass_header|proxy_pass_request_body|proxy_pass_request_headers|proxy_read_timeout|proxy_redirect|proxy_redirect_errors|proxy_send_lowat|proxy_send_timeout|proxy_set_body|proxy_set_header|proxy_ssl_session_reuse|proxy_store|proxy_store_access|proxy_temp_file_write_size|pr
 
oxy_temp_path|proxy_timeout|proxy_upstream_fail_timeout|proxy_upstream_max_fails|random_index|read_ahead|real_ip_header|recursive_error_pages|request_pool_size|reset_timedout_connection|resolver|resolver_timeout|return|rewrite|root|rtsig_overflow_events|rtsig_overflow_test|rtsig_overflow_threshold|rtsig_signo|satisfy|satisfy_any|secure_link_secret|send_lowat|send_timeout|sendfile|sendfile_max_chunk|server|server_name|server_name_in_redirect|server_names_hash_bucket_size|server_names_hash_max_size|server_tokens|set|set_real_ip_from|smtp_auth|smtp_capabilities|so_keepalive|source_charset|split_clients|ssi|ssi_silent_errors|ssi_types|ssi_value_length|ssl|ssl_certificate|ssl_certificate_key|ssl_ciphers|ssl_client_certificate|ssl_crl|ssl_dhparam|ssl_engine|ssl_prefer_server_ciphers|ssl_protocols|ssl_session_cache|ssl_session_timeout|ssl_verify_client|ssl_verify_depth|starttls|stub_status|sub_filter|sub_filter_once|sub_filter_types|tcp_nodelay|tcp_nopush|timeout|timer_resolution|try_files
 
|types|types_hash_bucket_size|types_hash_max_size|underscores_in_headers|uninitialized_variable_warn|upstream|use|user|userid|userid_domain|userid_expires|userid_name|userid_p3p|userid_path|userid_service|valid_referers|variables_hash_bucket_size|variables_hash_max_size|worker_connections|worker_cpu_affinity|worker_priority|worker_processes|worker_rlimit_core|worker_rlimit_nofile|worker_rlimit_sigpending|working_directory|xclient|xml_entities|xslt_entities|xslt_stylesheet|xslt_types|ssl_session_tickets|ssl_stapling|ssl_stapling_verify|ssl_ecdh_curve|ssl_trusted_certificate|more_set_headers|ssl_early_data)\b/i
-});
+(function (Prism) {
 
-Prism.languages.insertBefore('nginx', 'keyword', {
-       'variable': /\$[a-z_]+/i
-});
+       var variable = /\$(?:\w[a-z\d]*(?:_[^\x00-\x1F\s"'\\()$]*)?|\{[^}\s"'\\]+\})/i;
+
+       Prism.languages.nginx = {
+               'comment': {
+                       pattern: /(^|[\s{};])#.*/,
+                       lookbehind: true
+               },
+               'directive': {
+                       pattern: 
/(^|\s)\w(?:[^;{}"'\\\s]|\\.|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\s+(?:#.*(?!.)|(?![#\s])))*?(?=\s*[;{])/,
+                       lookbehind: true,
+                       greedy: true,
+                       inside: {
+                               'string': {
+                                       pattern: 
/((?:^|[^\\])(?:\\\\)*)(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,
+                                       lookbehind: true,
+                                       inside: {
+                                               'escape': {
+                                                       pattern: /\\["'\\nrt]/,
+                                                       alias: 'entity'
+                                               },
+                                               'variable': variable
+                                       }
+                               },
+                               'comment': {
+                                       pattern: /(\s)#.*/,
+                                       lookbehind: true,
+                                       greedy: true
+                               },
+                               'keyword': {
+                                       pattern: /^\S+/,
+                                       greedy: true
+                               },
+
+                               // other patterns
+
+                               'boolean': {
+                                       pattern: /(\s)(?:off|on)(?!\S)/,
+                                       lookbehind: true
+                               },
+                               'number': {
+                                       pattern: /(\s)\d+[a-z]*(?!\S)/i,
+                                       lookbehind: true
+                               },
+                               'variable': variable
+                       }
+               },
+               'punctuation': /[{};]/
+       };
+
+}(Prism));
 
 Prism.languages.nim = {
        'comment': /#.*/,
@@ -3023,6 +3307,7 @@ Prism.languages.nim = {
        },
        'punctuation': /[({\[]\.|\.[)}\]]|[`(){}\[\],:]/
 };
+
 Prism.languages.ocaml = {
        'comment': /\(\*[\s\S]*?\*\)/,
        'string': [
@@ -3228,7 +3513,7 @@ Prism.languages.perl = {
                // ${...}
                /[&*$@%]#?(?=\{)/,
                // $foo
-               /[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+)+(?:::)*/i,
+               /[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+(?![\w$]))+(?:::)*/i,
                // $1
                /[&*$@%]\d+/,
                // $_, @_, %!
@@ -3271,8 +3556,18 @@ Prism.languages.perl = {
                        pattern: /\b(?:false|true)\b/i,
                        alias: 'boolean'
                },
-               /\b[A-Z_][A-Z0-9_]*\b(?!\s*\()/,
+               {
+                       pattern: /(::\s*)\b[a-z_]\w*\b(?!\s*\()/i,
+                       greedy: true,
+                       lookbehind: true,
+               },
+               {
+                       pattern: /(\b(?:case|const)\s+)\b[a-z_]\w*(?=\s*[;=])/i,
+                       greedy: true,
+                       lookbehind: true,
+               },
                /\b(?:null)\b/i,
+               /\b[A-Z_][A-Z0-9_]*\b(?!\s*\()/,
        ];
        var number = 
/\b0b[01]+(?:_[01]+)*\b|\b0o[0-7]+(?:_[0-7]+)*\b|\b0x[\da-f]+(?:_[\da-f]+)*\b|(?:\b\d+(?:_\d+)*\.?(?:\d+(?:_\d+)*)?|\B\.\d+)(?:e[+-]?\d+)?/i;
        var operator = 
/<?=>|\?\?=?|\.{3}|\??->|[!=]=?=?|::|\*\*=?|--|\+\+|&&|\|\||<<|>>|[?~]|[/^|%*&<>.+-]=?/;
@@ -3297,6 +3592,11 @@ Prism.languages.perl = {
                        lookbehind: true,
                        alias: 'class-name'
                },
+               'function-definition': {
+                       pattern: /(\bfunction\s+)[a-z_]\w*(?=\s*\()/i,
+                       lookbehind: true,
+                       alias: 'function'
+               },
                'keyword': [
                        {
                                pattern: 
/(\(\s*)\b(?:bool|boolean|int|integer|float|string|object|array)\b(?=\s*\))/i,
@@ -3344,9 +3644,26 @@ Prism.languages.perl = {
                                alias: 'static-context',
                                greedy: true
                        },
-                       
/\b(?:__halt_compiler|abstract|and|array|as|break|callable|case|catch|class|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|namespace|match|new|or|parent|print|private|protected|public|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield)\b/i
+                       {
+                               // yield from
+                               pattern: /(\byield\s+)from\b/i,
+                               lookbehind: true
+                       },
+                       // `class` is always a keyword unlike other keywords
+                       /\bclass\b/i,
+                       {
+                               // https://www.php.net/manual/en/reserved.keywords.php
+                               //
+                               // keywords cannot be preceded by "->"
+                               // the complex lookbehind means `(?<!(?:->|::)\s*)`
+                               pattern: 
/((?:^|[^\s>:]|(?:^|[^-])>|(?:^|[^:]):)\s*)\b(?:__halt_compiler|abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|namespace|match|new|or|parent|print|private|protected|public|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield)\b/i,
+                               lookbehind: true
+                       }
                ],
-               'argument-name': /\b[a-z_]\w*(?=\s*:(?!:))/i,
+               'argument-name': {
+                       pattern: /([(,]\s+)\b[a-z_]\w*(?=\s*:(?!:))/i,
+                       lookbehind: true
+               },
                'class-name': [
                        {
                                pattern: 
/(\b(?:extends|implements|instanceof|new(?!\s+self|\s+static))\s+|\bcatch\s*\()\b[a-z_]\w*(?!\\)\b/i,
@@ -3446,9 +3763,15 @@ Prism.languages.perl = {
                        }
                ],
                'constant': constant,
-               'function': /\w+\s*(?=\()/,
+               'function': {
+                       pattern: /(^|[^\\\w])\\?[a-z_](?:[\w\\]*\w)?(?=\s*\()/i,
+                       lookbehind: true,
+                       inside: {
+                               'punctuation': /\\/
+                       }
+               },
                'property': {
-                       pattern: /(->)[\w]+/,
+                       pattern: /(->\s*)\w+/,
                        lookbehind: true
                },
                'number': number,
@@ -3457,7 +3780,7 @@ Prism.languages.perl = {
        };
 
        var string_interpolation = {
-               pattern: 
/{\$(?:{(?:{[^{}]+}|[^{}]+)}|[^{}])+}|(^|[^\\{])\$+(?:\w+(?:\[[^\r\n\[\]]+\]|->\w+)*)/,
+               pattern: 
/{\$(?:{(?:{[^{}]+}|[^{}]+)}|[^{}])+}|(^|[^\\{])\$+(?:\w+(?:\[[^\r\n\[\]]+\]|->\w+)?)/,
                lookbehind: true,
                inside: Prism.languages.php
        };
@@ -3489,7 +3812,7 @@ Prism.languages.perl = {
                                                'punctuation': /^<<<"?|[";]$/
                                        }
                                },
-                               'interpolation': string_interpolation // See below
+                               'interpolation': string_interpolation
                        }
                },
                {
@@ -3507,16 +3830,13 @@ Prism.languages.perl = {
                        alias: 'double-quoted-string',
                        greedy: true,
                        inside: {
-                               'interpolation': string_interpolation // See below
+                               'interpolation': string_interpolation
                        }
                }
        ];
 
        Prism.languages.insertBefore('php', 'variable', {
                'string': string,
-       });
-
-       Prism.languages.insertBefore('php', 'variable', {
                'attribute': {
                        pattern: 
/#\[(?:[^"'\/#]|\/(?![*/])|\/\/.*$|#(?!\[).*$|\/\*(?:[^*]|\*(?!\/))*\*\/|"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*')+\](?=\s*[a-z$#])/mi,
                        greedy: true,
@@ -3562,7 +3882,7 @@ Prism.languages.perl = {
                },
        });
 
-       Prism.hooks.add('before-tokenize', function(env) {
+       Prism.hooks.add('before-tokenize', function (env) {
                if (!/<\?/.test(env.code)) {
                        return;
                }
@@ -3571,7 +3891,7 @@ Prism.languages.perl = {
                Prism.languages['markup-templating'].buildPlaceholders(env, 'php', phpPattern);
        });
 
-       Prism.hooks.add('after-tokenize', function(env) {
+       Prism.hooks.add('after-tokenize', function (env) {
                Prism.languages['markup-templating'].tokenizePlaceholders(env, 'php');
        });
 
@@ -3642,149 +3962,149 @@ Prism.languages.python['string-interpolation'].inside['interpolation'].inside.re
 
 Prism.languages.py = Prism.languages.python;
 
-(function(Prism) {
+(function (Prism) {
 
-var javascript = Prism.util.clone(Prism.languages.javascript);
+       var javascript = Prism.util.clone(Prism.languages.javascript);
 
-var space = /(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source;
-var braces = /(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source;
-var spread = /(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;
+       var space = /(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source;
+       var braces = /(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source;
+       var spread = /(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;
 
-/**
- * @param {string} source
- * @param {string} [flags]
- */
-function re(source, flags) {
-       source = source
-               .replace(/<S>/g, function () { return space; })
-               .replace(/<BRACES>/g, function () { return braces; })
-               .replace(/<SPREAD>/g, function () { return spread; });
-       return RegExp(source, flags);
-}
+       /**
+        * @param {string} source
+        * @param {string} [flags]
+        */
+       function re(source, flags) {
+               source = source
+                       .replace(/<S>/g, function () { return space; })
+                       .replace(/<BRACES>/g, function () { return braces; })
+                       .replace(/<SPREAD>/g, function () { return spread; });
+               return RegExp(source, flags);
+       }
 
-spread = re(spread).source;
+       spread = re(spread).source;
 
 
-Prism.languages.jsx = Prism.languages.extend('markup', javascript);
-Prism.languages.jsx.tag.pattern = re(
-       
/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source
-);
+       Prism.languages.jsx = Prism.languages.extend('markup', javascript);
+       Prism.languages.jsx.tag.pattern = re(
+               
/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source
+       );
 
-Prism.languages.jsx.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i;
-Prism.languages.jsx.tag.inside['attr-value'].pattern = 
/=(?!\{)(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s'">]+)/i;
-Prism.languages.jsx.tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/;
-Prism.languages.jsx.tag.inside['comment'] = javascript['comment'];
+       Prism.languages.jsx.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i;
+       Prism.languages.jsx.tag.inside['attr-value'].pattern = 
/=(?!\{)(?:"(?:\\[^]|[^\\"])*"|'(?:\\[^]|[^\\'])*'|[^\s'">]+)/i;
+       Prism.languages.jsx.tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/;
+       Prism.languages.jsx.tag.inside['comment'] = javascript['comment'];
 
-Prism.languages.insertBefore('inside', 'attr-name', {
-       'spread': {
-               pattern: re(/<SPREAD>/.source),
-               inside: Prism.languages.jsx
-       }
-}, Prism.languages.jsx.tag);
+       Prism.languages.insertBefore('inside', 'attr-name', {
+               'spread': {
+                       pattern: re(/<SPREAD>/.source),
+                       inside: Prism.languages.jsx
+               }
+       }, Prism.languages.jsx.tag);
 
-Prism.languages.insertBefore('inside', 'attr-value',{
-       'script': {
-               // Allow for two levels of nesting
-               pattern: re(/=<BRACES>/.source),
-               inside: {
-                       'script-punctuation': {
-                               pattern: /^=(?={)/,
-                               alias: 'punctuation'
+       Prism.languages.insertBefore('inside', 'special-attr', {
+               'script': {
+                       // Allow for two levels of nesting
+                       pattern: re(/=<BRACES>/.source),
+                       inside: {
+                               'script-punctuation': {
+                                       pattern: /^=(?={)/,
+                                       alias: 'punctuation'
+                               },
+                               rest: Prism.languages.jsx
                        },
-                       rest: Prism.languages.jsx
-               },
-               'alias': 'language-javascript'
-       }
-}, Prism.languages.jsx.tag);
+                       'alias': 'language-javascript'
+               }
+       }, Prism.languages.jsx.tag);
 
-// The following will handle plain text inside tags
-var stringifyToken = function (token) {
-       if (!token) {
-               return '';
-       }
-       if (typeof token === 'string') {
-               return token;
-       }
-       if (typeof token.content === 'string') {
-               return token.content;
-       }
-       return token.content.map(stringifyToken).join('');
-};
+       // The following will handle plain text inside tags
+       var stringifyToken = function (token) {
+               if (!token) {
+                       return '';
+               }
+               if (typeof token === 'string') {
+                       return token;
+               }
+               if (typeof token.content === 'string') {
+                       return token.content;
+               }
+               return token.content.map(stringifyToken).join('');
+       };
 
-var walkTokens = function (tokens) {
-       var openedTags = [];
-       for (var i = 0; i < tokens.length; i++) {
-               var token = tokens[i];
-               var notTagNorBrace = false;
-
-               if (typeof token !== 'string') {
-                       if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') {
-                               // We found a tag, now find its kind
-
-                               if (token.content[0].content[0].content === '</') {
-                                       // Closing tag
-                                       if (openedTags.length > 0 && openedTags[openedTags.length - 
1].tagName === stringifyToken(token.content[0].content[1])) {
-                                               // Pop matching opening tag
-                                               openedTags.pop();
-                                       }
-                               } else {
-                                       if (token.content[token.content.length - 1].content === '/>') {
-                                               // Autoclosed tag, ignore
+       var walkTokens = function (tokens) {
+               var openedTags = [];
+               for (var i = 0; i < tokens.length; i++) {
+                       var token = tokens[i];
+                       var notTagNorBrace = false;
+
+                       if (typeof token !== 'string') {
+                               if (token.type === 'tag' && token.content[0] && token.content[0].type === 
'tag') {
+                                       // We found a tag, now find its kind
+
+                                       if (token.content[0].content[0].content === '</') {
+                                               // Closing tag
+                                               if (openedTags.length > 0 && openedTags[openedTags.length - 
1].tagName === stringifyToken(token.content[0].content[1])) {
+                                                       // Pop matching opening tag
+                                                       openedTags.pop();
+                                               }
                                        } else {
-                                               // Opening tag
-                                               openedTags.push({
-                                                       tagName: stringifyToken(token.content[0].content[1]),
-                                                       openedBraces: 0
-                                               });
+                                               if (token.content[token.content.length - 1].content === '/>') 
{
+                                                       // Autoclosed tag, ignore
+                                               } else {
+                                                       // Opening tag
+                                                       openedTags.push({
+                                                               tagName: 
stringifyToken(token.content[0].content[1]),
+                                                               openedBraces: 0
+                                                       });
+                                               }
                                        }
-                               }
-                       } else if (openedTags.length > 0 && token.type === 'punctuation' && token.content === 
'{') {
+                               } else if (openedTags.length > 0 && token.type === 'punctuation' && 
token.content === '{') {
 
-                               // Here we might have entered a JSX context inside a tag
-                               openedTags[openedTags.length - 1].openedBraces++;
+                                       // Here we might have entered a JSX context inside a tag
+                                       openedTags[openedTags.length - 1].openedBraces++;
 
-                       } else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 
0 && token.type === 'punctuation' && token.content === '}') {
+                               } else if (openedTags.length > 0 && openedTags[openedTags.length - 
1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {
 
-                               // Here we might have left a JSX context inside a tag
-                               openedTags[openedTags.length - 1].openedBraces--;
+                                       // Here we might have left a JSX context inside a tag
+                                       openedTags[openedTags.length - 1].openedBraces--;
 
-                       } else {
-                               notTagNorBrace = true
-                       }
-               }
-               if (notTagNorBrace || typeof token === 'string') {
-                       if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) {
-                               // Here we are inside a tag, and not inside a JSX context.
-                               // That's plain text: drop any tokens matched.
-                               var plainText = stringifyToken(token);
-
-                               // And merge text with adjacent text
-                               if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 
1].type === 'plain-text')) {
-                                       plainText += stringifyToken(tokens[i + 1]);
-                                       tokens.splice(i + 1, 1);
+                               } else {
+                                       notTagNorBrace = true;
                                }
-                               if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 
'plain-text')) {
-                                       plainText = stringifyToken(tokens[i - 1]) + plainText;
-                                       tokens.splice(i - 1, 1);
-                                       i--;
+                       }
+                       if (notTagNorBrace || typeof token === 'string') {
+                               if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces 
=== 0) {
+                                       // Here we are inside a tag, and not inside a JSX context.
+                                       // That's plain text: drop any tokens matched.
+                                       var plainText = stringifyToken(token);
+
+                                       // And merge text with adjacent text
+                                       if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || 
tokens[i + 1].type === 'plain-text')) {
+                                               plainText += stringifyToken(tokens[i + 1]);
+                                               tokens.splice(i + 1, 1);
+                                       }
+                                       if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type 
=== 'plain-text')) {
+                                               plainText = stringifyToken(tokens[i - 1]) + plainText;
+                                               tokens.splice(i - 1, 1);
+                                               i--;
+                                       }
+
+                                       tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);
                                }
+                       }
 
-                               tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);
+                       if (token.content && typeof token.content !== 'string') {
+                               walkTokens(token.content);
                        }
                }
+       };
 
-               if (token.content && typeof token.content !== 'string') {
-                       walkTokens(token.content);
+       Prism.hooks.add('after-tokenize', function (env) {
+               if (env.language !== 'jsx' && env.language !== 'tsx') {
+                       return;
                }
-       }
-};
-
-Prism.hooks.add('after-tokenize', function (env) {
-       if (env.language !== 'jsx' && env.language !== 'tsx') {
-               return;
-       }
-       walkTokens(env.tokens);
-});
+               walkTokens(env.tokens);
+       });
 
 }(Prism));
 
@@ -4007,17 +4327,26 @@ Prism.languages.scss['atrule'].inside.rest = Prism.languages.scss;
 
        var strings = [
                // normal string
-               // 1 capturing group
-               /(["'])(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|(?!\1)[^\\`$])*\1/.source,
+               /"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/.source,
+               /'[^']*'/.source,
+               /\$'(?:[^'\\]|\\[\s\S])*'/.source,
 
                // here doc
                // 2 capturing groups
-               /<<-?\s*(["']?)(\w+)\2\s[\s\S]*?[\r\n]\3/.source
+               /<<-?\s*(["']?)(\w+)\1\s[\s\S]*?[\r\n]\2/.source
        ].join('|');
 
        Prism.languages['shell-session'] = {
                'command': {
-                       pattern: 
RegExp(/^(?:[^\s@:$#*!/\\]+@[^\s@:$#*!/\\]+(?::[^\0-\x1F$#*?"<>:;|]+)?|[^\0-\x1F$#*?"<>:;|]+)?[$#](?:[^\\\r\n'"<]|\\.|<<str>>)+/.source.replace(/<<str>>/g,
 function () { return strings; }), 'm'),
+                       pattern: RegExp(
+                               // user info
+                               
/^(?:[^\s@:$#*!/\\]+@[^\s@:$#*!/\\]+(?::[^\0-\x1F$#*?"<>:;|]+)?|[^\0-\x1F$#*?"<>:;|]+)?/.source +
+                               // shell symbol
+                               /[$#]/.source +
+                               // bash command
+                               
/(?:[^\\\r\n'"<$]|\\(?:[^\r]|\r\n?)|\$(?!')|<<str>>)+/.source.replace(/<<str>>/g, function () { return 
strings; }),
+                               'm'
+                       ),
                        greedy: true,
                        inside: {
                                'info': {
@@ -4085,11 +4414,16 @@ Prism.languages.sql = {
                        greedy: true,
                        inside: null // see below
                },
-               // From JavaScript Prism keyword list and TypeScript language spec: 
https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#221-reserved-words
-               'keyword': 
/\b(?:abstract|as|asserts|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|undefined|var|void|while|with|yield)\b/,
                'builtin': 
/\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/,
        });
 
+       // The keywords TypeScript adds to JavaScript
+       Prism.languages.typescript.keyword.push(
+               /\b(?:abstract|as|declare|implements|is|keyof|readonly|require)\b/,
+               // keywords that have to be followed by an identifier
+               /\b(?:asserts|infer|interface|module|namespace|type)(?!\s*[^\s_${}*a-zA-Z\xA0-\uFFFF])/
+       );
+
        // doesn't work with TS because TS is too complex
        delete Prism.languages.typescript['parameter'];
 
@@ -4100,6 +4434,16 @@ Prism.languages.sql = {
        Prism.languages.typescript['class-name'].inside = typeInside;
 
        Prism.languages.insertBefore('typescript', 'function', {
+               'decorator': {
+                       pattern: /@[$\w\xA0-\uFFFF]+/,
+                       inside: {
+                               'at': {
+                                       pattern: /^@/,
+                                       alias: 'operator'
+                               },
+                               'function': /^[\s\S]+/
+                       }
+               },
                'generic-function': {
                        // e.g. foo<T extends "bar" | "baz">( ...
                        pattern: 
/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,
@@ -4146,7 +4490,7 @@ Prism.languages.sql = {
                flags = (flags || '').replace(/m/g, '') + 'm'; // add m flag
                var pattern = /([:\-,[{]\s*(?:\s<<prop>>[ \t]+)?)(?:<<value>>)(?=[ 
\t]*(?:$|,|]|}|(?:[\r\n]\s*)?#))/.source
                        .replace(/<<prop>>/g, function () { return properties; }).replace(/<<value>>/g, 
function () { return value; });
-               return RegExp(pattern, flags)
+               return RegExp(pattern, flags);
        }
 
        Prism.languages.yaml = {
@@ -4202,4 +4546,3 @@ Prism.languages.sql = {
        Prism.languages.yml = Prism.languages.yaml;
 
 }(Prism));
-
diff --git a/lib/docs/filters/latex/clean_html.rb b/lib/docs/filters/latex/clean_html.rb
index cc327b04..5902679f 100644
--- a/lib/docs/filters/latex/clean_html.rb
+++ b/lib/docs/filters/latex/clean_html.rb
@@ -13,6 +13,11 @@ module Docs
 
         css('h1, h2, h3, h4').each { |node| node.content = node.content.sub /^[0-9A-Z]+(\.[0-9]+)* /, '' }
 
+        css('pre').each do |node|
+          node.delete 'class'
+          node['data-language'] = 'latex'
+        end
+
         doc
       end
     end


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]