diff options
author | Olivier Hallot <olivier.hallot@libreoffice.org> | 2020-05-29 16:45:56 -0300 |
---|---|---|
committer | Olivier Hallot <olivier.hallot@libreoffice.org> | 2020-05-31 16:11:47 +0200 |
commit | 7b5fe18bbcca0811ae386c9230bf5f3d9da88acb (patch) | |
tree | e4605663e14c37274a946576303098dbb716f9be /help3xsl | |
parent | 980d7730dbc2275d80fd059f58db56d37a225247 (diff) |
Update prism.js to latest version
Update prism.js and companion css to latest version
Fixes the Currency and Type keyword highlight
Fixes line numbering interference with a11y-toggle.js
Change-Id: Ibbb5c95790e1366f0a1dbc497d8741e69a0a3957
Reviewed-on: https://gerrit.libreoffice.org/c/help/+/95173
Tested-by: Jenkins
Reviewed-by: Olivier Hallot <olivier.hallot@libreoffice.org>
Diffstat (limited to 'help3xsl')
-rw-r--r-- | help3xsl/README.prism.js.txt | 5 | ||||
-rw-r--r-- | help3xsl/default.css | 7 | ||||
-rw-r--r-- | help3xsl/prism.css | 6 | ||||
-rw-r--r-- | help3xsl/prism.js | 1069 |
4 files changed, 709 insertions, 378 deletions
diff --git a/help3xsl/README.prism.js.txt b/help3xsl/README.prism.js.txt index 581a1fe71c..fe0acc0d2b 100644 --- a/help3xsl/README.prism.js.txt +++ b/help3xsl/README.prism.js.txt @@ -1,3 +1,7 @@ +Latest download version + +PrismJS 1.20.0 + Using prism.js for Basic code highlight http://prismjs.com @@ -37,3 +41,4 @@ Add class 'language-visual-basic' and 'line-numbers' to <code> as in <div class="bascode" itemscope="true" itemtype="http://schema.org/SoftwareSourceCode" itemprop="codeSampleType" content="snippet"> <pre><code class="language-visual-basic line-numbers"><xsl:apply-templates /></code></pre></div> </xsl:template> + diff --git a/help3xsl/default.css b/help3xsl/default.css index 0ae386088a..dd647d14f2 100644 --- a/help3xsl/default.css +++ b/help3xsl/default.css @@ -361,10 +361,7 @@ header { height: 60px; margin-right: 10px; } -[aria-hidden='true'], -[data-a11y-toggle]:not([aria-controls]) { - display: none; -} + #langs-nav:not([aria-hidden='true']), #modules-nav:not([aria-hidden='true']) { z-index: 100; /* line them up horizontally */ @@ -739,7 +736,7 @@ li.disabled a { } } @media screen and (min-width: 960px) { - #langs-nav { + #langs-nav, #modules-nav { display: none; } #langs-nav a { diff --git a/help3xsl/prism.css b/help3xsl/prism.css index 6e31d4d8fa..8bd96d9cc3 100644 --- a/help3xsl/prism.css +++ b/help3xsl/prism.css @@ -1,4 +1,4 @@ -/* PrismJS 1.15.0 +/* PrismJS 1.20.0 https://prismjs.com/download.html#themes=prism-coy&languages=markup+css+clike+javascript+python+visual-basic&plugins=line-numbers+normalize-whitespace */ /** * prism.js Coy theme for JavaScript, CoffeeScript, CSS and HTML @@ -11,6 +11,7 @@ pre[class*="language-"] { color: black; background: none; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; text-align: left; white-space: pre; word-spacing: normal; @@ -179,7 +180,7 @@ pre[class*="language-"]:after { cursor: help; } -.namespace { +.token.namespace { opacity: .7; } @@ -255,7 +256,6 @@ pre[class*="language-"].line-numbers > code { } .line-numbers-rows > span { - pointer-events: none; display: block; counter-increment: linenumber; } diff --git a/help3xsl/prism.js b/help3xsl/prism.js index c085b09b00..1080118885 100644 --- a/help3xsl/prism.js +++ b/help3xsl/prism.js @@ -1,4 +1,4 @@ -/* PrismJS 1.15.0 +/* PrismJS 1.20.0 https://prismjs.com/download.html#themes=prism-coy&languages=markup+css+clike+javascript+python+visual-basic&plugins=line-numbers+normalize-whitespace */ var _self = (typeof window !== 'undefined') ? window // if in browser @@ -14,21 +14,22 @@ var _self = (typeof window !== 'undefined') * @author Lea Verou http://lea.verou.me */ -var Prism = (function(){ +var Prism = (function (_self){ // Private helper vars var lang = /\blang(?:uage)?-([\w-]+)\b/i; var uniqueId = 0; -var _ = _self.Prism = { + +var _ = { manual: _self.Prism && _self.Prism.manual, disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler, util: { - encode: function (tokens) { + encode: function encode(tokens) { if (tokens instanceof Token) { - return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias); - } else if (_.util.type(tokens) === 'Array') { - return tokens.map(_.util.encode); + return new Token(tokens.type, encode(tokens.content), tokens.alias); + } else if (Array.isArray(tokens)) { + return tokens.map(encode); } else { return tokens.replace(/&/g, '&').replace(/</g, '<').replace(/\u00a0/g, ' '); } @@ -46,41 +47,104 @@ var _ = _self.Prism = { }, // Deep clone a language definition (e.g. to extend it) - clone: function (o, visited) { - var type = _.util.type(o); + clone: function deepClone(o, visited) { + var clone, id, type = _.util.type(o); visited = visited || {}; switch (type) { case 'Object': - if (visited[_.util.objId(o)]) { - return visited[_.util.objId(o)]; + id = _.util.objId(o); + if (visited[id]) { + return visited[id]; } - var clone = {}; - visited[_.util.objId(o)] = clone; + clone = {}; + visited[id] = clone; for (var key in o) { if (o.hasOwnProperty(key)) { - clone[key] = _.util.clone(o[key], visited); + clone[key] = deepClone(o[key], visited); } } return clone; case 'Array': - if (visited[_.util.objId(o)]) { - return visited[_.util.objId(o)]; + id = _.util.objId(o); + if (visited[id]) { + return visited[id]; } - var clone = []; - visited[_.util.objId(o)] = clone; + clone = []; + visited[id] = clone; o.forEach(function (v, i) { - clone[i] = _.util.clone(v, visited); + clone[i] = deepClone(v, visited); }); return clone; + + default: + return o; } + }, - return o; + /** + * 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'; + }, + + /** + * 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) { + return document.currentScript; + } + + // 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; + } } }, @@ -98,45 +162,32 @@ var _ = _self.Prism = { /** * Insert a token before another token in a language literal * As this needs to recreate the object (we cannot actually insert before keys in object literals), - * we cannot just provide an object, we need anobject and a key. + * we cannot just provide an object, we need an object and a key. * @param inside The key (or language id) of the parent - * @param before The key to insert before. If not provided, the function appends instead. + * @param before The key to insert before. * @param insert Object with the key/value pairs to insert * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted. */ insertBefore: function (inside, before, insert, root) { root = root || _.languages; var grammar = root[inside]; - - if (arguments.length == 2) { - insert = arguments[1]; - - for (var newToken in insert) { - if (insert.hasOwnProperty(newToken)) { - grammar[newToken] = insert[newToken]; - } - } - - return 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]; } } } - ret[token] = grammar[token]; + // Do not insert token which also occur in insert. See #1525 + if (!insert.hasOwnProperty(token)) { + ret[token] = grammar[token]; + } } } @@ -154,19 +205,25 @@ var _ = _self.Prism = { }, // Traverse a language definition with Depth First Search - DFS: function(o, callback, type, visited) { + 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); - if (_.util.type(o[i]) === 'Object' && !visited[_.util.objId(o[i])]) { - visited[_.util.objId(o[i])] = true; - _.languages.DFS(o[i], callback, null, visited); + var property = o[i], + propertyType = _.util.type(property); + + if (propertyType === 'Object' && !visited[objId(property)]) { + visited[objId(property)] = true; + DFS(property, callback, null, visited); } - else if (_.util.type(o[i]) === 'Array' && !visited[_.util.objId(o[i])]) { - visited[_.util.objId(o[i])] = true; - _.languages.DFS(o[i], callback, i, visited); + else if (propertyType === 'Array' && !visited[objId(property)]) { + visited[objId(property)] = true; + DFS(property, callback, i, visited); } } } @@ -181,41 +238,33 @@ var _ = _self.Prism = { 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); + _.hooks.run('before-highlightall', env); - var elements = env.elements || container.querySelectorAll(env.selector); + env.elements = Array.prototype.slice.apply(env.container.querySelectorAll(env.selector)); - for (var i=0, element; element = elements[i++];) { + _.hooks.run('before-all-elements-highlight', env); + + for (var i = 0, element; element = env.elements[i++];) { _.highlightElement(element, async === true, env.callback); } }, highlightElement: function(element, async, callback) { // Find language - var language, grammar, parent = element; - - while (parent && !lang.test(parent.className)) { - parent = parent.parentNode; - } - - if (parent) { - language = (parent.className.match(lang) || [,''])[1].toLowerCase(); - grammar = _.languages[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; - if (element.parentNode) { - // Set language on the parent, for styling - parent = element.parentNode; - - if (/pre/i.test(parent.nodeName)) { - parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language; - } + // Set language on the parent, for styling + var parent = element.parentNode; + if (parent && parent.nodeName.toLowerCase() === 'pre') { + parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language; } var code = element.textContent; @@ -227,33 +276,38 @@ var _ = _self.Prism = { code: code }; + function insertHighlightedCode(highlightedCode) { + env.highlightedCode = highlightedCode; + + _.hooks.run('before-insert', env); + + env.element.innerHTML = env.highlightedCode; + + _.hooks.run('after-highlight', env); + _.hooks.run('complete', env); + callback && callback.call(env.element); + } + _.hooks.run('before-sanity-check', env); - if (!env.code || !env.grammar) { - if (env.code) { - _.hooks.run('before-highlight', env); - env.element.textContent = env.code; - _.hooks.run('after-highlight', env); - } + if (!env.code) { _.hooks.run('complete', env); + callback && callback.call(env.element); return; } _.hooks.run('before-highlight', env); + if (!env.grammar) { + insertHighlightedCode(_.util.encode(env.code)); + return; + } + if (async && _self.Worker) { var worker = new Worker(_.filename); worker.onmessage = function(evt) { - env.highlightedCode = evt.data; - - _.hooks.run('before-insert', env); - - env.element.innerHTML = env.highlightedCode; - - _.hooks.run('after-highlight', env); - _.hooks.run('complete', env); - callback && callback.call(env.element); + insertHighlightedCode(evt.data); }; worker.postMessage(JSON.stringify({ @@ -263,17 +317,7 @@ var _ = _self.Prism = { })); } else { - env.highlightedCode = _.highlight(env.code, env.grammar, env.language); - - _.hooks.run('before-insert', env); - - env.element.innerHTML = env.highlightedCode; - - _.hooks.run('after-highlight', env); - - _.hooks.run('complete', env); - - callback && callback.call(element); + insertHighlightedCode(_.highlight(env.code, env.grammar, env.language)); } }, @@ -289,139 +333,8 @@ var _ = _self.Prism = { return Token.stringify(_.util.encode(env.tokens), env.language); }, - matchGrammar: function (text, strarr, grammar, index, startPos, oneshot, target) { - var Token = _.Token; - - for (var token in grammar) { - if(!grammar.hasOwnProperty(token) || !grammar[token]) { - continue; - } - - if (token == target) { - return; - } - - var patterns = grammar[token]; - patterns = (_.util.type(patterns) === "Array") ? patterns : [patterns]; - - for (var j = 0; j < patterns.length; ++j) { - var pattern = patterns[j], - inside = pattern.inside, - lookbehind = !!pattern.lookbehind, - greedy = !!pattern.greedy, - lookbehindLength = 0, - alias = pattern.alias; - - if (greedy && !pattern.pattern.global) { - // Without the global flag, lastIndex won't work - var flags = pattern.pattern.toString().match(/[imuy]*$/)[0]; - pattern.pattern = RegExp(pattern.pattern.source, flags + "g"); - } - - pattern = pattern.pattern || pattern; - - // Don’t cache length as it changes during the loop - for (var i = index, pos = startPos; i < strarr.length; pos += strarr[i].length, ++i) { - - var str = strarr[i]; - - if (strarr.length > text.length) { - // Something went terribly wrong, ABORT, ABORT! - return; - } - - if (str instanceof Token) { - continue; - } - - if (greedy && i != strarr.length - 1) { - pattern.lastIndex = pos; - var match = pattern.exec(text); - if (!match) { - break; - } - - var from = match.index + (lookbehind ? match[1].length : 0), - to = match.index + match[0].length, - k = i, - p = pos; - - for (var len = strarr.length; k < len && (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); ++k) { - p += strarr[k].length; - // Move the index i to the element in strarr that is closest to from - if (from >= p) { - ++i; - pos = p; - } - } - - // If strarr[i] is a Token, then the match starts inside another Token, which is invalid - if (strarr[i] instanceof Token) { - continue; - } - - // Number of tokens to delete and replace with the new match - delNum = k - i; - str = text.slice(pos, p); - match.index -= pos; - } else { - pattern.lastIndex = 0; - - var match = pattern.exec(str), - delNum = 1; - } - - if (!match) { - if (oneshot) { - break; - } - - continue; - } - - if(lookbehind) { - lookbehindLength = match[1] ? match[1].length : 0; - } - - var from = match.index + lookbehindLength, - match = match[0].slice(lookbehindLength), - to = from + match.length, - before = str.slice(0, from), - after = str.slice(to); - - var args = [i, delNum]; - - if (before) { - ++i; - pos += before.length; - args.push(before); - } - - var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy); - - args.push(wrapped); - - if (after) { - args.push(after); - } - - Array.prototype.splice.apply(strarr, args); - - if (delNum != 1) - _.matchGrammar(text, strarr, grammar, i, pos, true, token); - - if (oneshot) - break; - } - } - } - }, - - tokenize: function(text, grammar, language) { - var strarr = [text]; - + tokenize: function(text, grammar) { var rest = grammar.rest; - if (rest) { for (var token in rest) { grammar[token] = rest[token]; @@ -430,9 +343,12 @@ var _ = _self.Prism = { delete grammar.rest; } - _.matchGrammar(text, strarr, grammar, 0, 0, false); + var tokenList = new LinkedList(); + addAfter(tokenList, tokenList.head, text); + + matchGrammar(text, tokenList, grammar, tokenList.head, 0); - return strarr; + return toArray(tokenList); }, hooks: { @@ -457,58 +373,290 @@ var _ = _self.Prism = { callback(env); } } - } + }, + + Token: Token }; -var Token = _.Token = function(type, content, alias, matchedStr, greedy) { +_self.Prism = _; + +function Token(type, content, alias, matchedStr, greedy) { this.type = type; this.content = content; this.alias = alias; // Copy of the full string this token was created from - this.length = (matchedStr || "").length|0; + this.length = (matchedStr || '').length|0; this.greedy = !!greedy; -}; +} -Token.stringify = function(o, language, parent) { +Token.stringify = function stringify(o, language) { if (typeof o == 'string') { return o; } - - if (_.util.type(o) === 'Array') { - return o.map(function(element) { - return Token.stringify(element, language, o); - }).join(''); + if (Array.isArray(o)) { + var s = ''; + o.forEach(function (e) { + s += stringify(e, language); + }); + return s; } var env = { type: o.type, - content: Token.stringify(o.content, language, parent), + content: stringify(o.content, language), tag: 'span', classes: ['token', o.type], attributes: {}, - language: language, - parent: parent + language: language }; - if (o.alias) { - var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias]; - Array.prototype.push.apply(env.classes, aliases); + var aliases = o.alias; + if (aliases) { + if (Array.isArray(aliases)) { + Array.prototype.push.apply(env.classes, aliases); + } else { + env.classes.push(aliases); + } } _.hooks.run('wrap', env); - var attributes = Object.keys(env.attributes).map(function(name) { - return name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"'; - }).join(' '); - - return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + '</' + env.tag + '>'; + var attributes = ''; + for (var name in env.attributes) { + attributes += ' ' + name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"'; + } + return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + attributes + '>' + env.content + '</' + env.tag + '>'; }; +/** + * @param {string} text + * @param {LinkedList<string | Token>} tokenList + * @param {any} grammar + * @param {LinkedListNode<string | Token>} startNode + * @param {number} startPos + * @param {boolean} [oneshot=false] + * @param {string} [target] + */ +function matchGrammar(text, tokenList, grammar, startNode, startPos, oneshot, target) { + for (var token in grammar) { + if (!grammar.hasOwnProperty(token) || !grammar[token]) { + continue; + } + + var patterns = grammar[token]; + patterns = Array.isArray(patterns) ? patterns : [patterns]; + + for (var j = 0; j < patterns.length; ++j) { + if (target && target == token + ',' + j) { + return; + } + + var pattern = patterns[j], + inside = pattern.inside, + lookbehind = !!pattern.lookbehind, + greedy = !!pattern.greedy, + lookbehindLength = 0, + alias = pattern.alias; + + if (greedy && !pattern.pattern.global) { + // Without the global flag, lastIndex won't work + var flags = pattern.pattern.toString().match(/[imsuy]*$/)[0]; + pattern.pattern = RegExp(pattern.pattern.source, flags + 'g'); + } + + pattern = pattern.pattern || pattern; + + 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 str = currentNode.value; + + if (tokenList.length > text.length) { + // Something went terribly wrong, ABORT, ABORT! + return; + } + + if (str instanceof Token) { + continue; + } + + var removeCount = 1; // this is the to parameter of removeBetween + + if (greedy && currentNode != tokenList.tail.prev) { + pattern.lastIndex = pos; + var match = pattern.exec(text); + if (!match) { + break; + } + + var from = match.index + (lookbehind && match[1] ? match[1].length : 0); + 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.prev.value.greedy)); + k = k.next + ) { + removeCount++; + p += k.value.length; + } + removeCount--; + + // replace with the new match + str = text.slice(pos, p); + match.index -= pos; + } else { + pattern.lastIndex = 0; + + var match = pattern.exec(str); + } + + if (!match) { + if (oneshot) { + break; + } + + continue; + } + + if (lookbehind) { + lookbehindLength = match[1] ? match[1].length : 0; + } + + var from = match.index + lookbehindLength, + match = match[0].slice(lookbehindLength), + to = from + match.length, + before = str.slice(0, from), + after = str.slice(to); + + var removeFrom = currentNode.prev; + + if (before) { + removeFrom = addAfter(tokenList, removeFrom, before); + pos += before.length; + } + + removeRange(tokenList, removeFrom, removeCount); + + var wrapped = new Token(token, inside ? _.tokenize(match, inside) : match, alias, match, greedy); + currentNode = addAfter(tokenList, removeFrom, wrapped); + + if (after) { + addAfter(tokenList, currentNode, after); + } + + + if (removeCount > 1) + matchGrammar(text, tokenList, grammar, currentNode.prev, pos, true, token + ',' + j); + + if (oneshot) + break; + } + } + } +} + +/** + * @typedef LinkedListNode + * @property {T} value + * @property {LinkedListNode<T> | null} prev The previous node. + * @property {LinkedListNode<T> | null} next The next node. + * @template T + */ + +/** + * @template T + */ +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; + + 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; + } + 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; +} + + if (!_self.document) { if (!_self.addEventListener) { // in Node.js - return _self.Prism; + return _; } if (!_.disableWorkerMessageHandler) { @@ -526,32 +674,48 @@ if (!_self.document) { }, false); } - return _self.Prism; + return _; } //Get current script and highlight -var script = document.currentScript || [].slice.call(document.getElementsByTagName("script")).pop(); +var script = _.util.currentScript(); if (script) { _.filename = script.src; - if (!_.manual && !script.hasAttribute('data-manual')) { - if(document.readyState !== "loading") { - if (window.requestAnimationFrame) { - window.requestAnimationFrame(_.highlightAll); - } else { - window.setTimeout(_.highlightAll, 16); - } - } - else { - document.addEventListener('DOMContentLoaded', _.highlightAll); + if (script.hasAttribute('data-manual')) { + _.manual = true; + } +} + +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); + } else { + window.setTimeout(highlightAutomaticallyCallback, 16); } } } -return _self.Prism; +return _; -})(); +})(_self); if (typeof module !== 'undefined' && module.exports) { module.exports = Prism; @@ -565,28 +729,47 @@ if (typeof global !== 'undefined') { Prism.languages.markup = { 'comment': /<!--[\s\S]*?-->/, 'prolog': /<\?[\s\S]+?\?>/, - 'doctype': /<!DOCTYPE[\s\S]+?>/i, + 'doctype': { + // https://www.w3.org/TR/xml/#NT-doctypedecl + pattern: /<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i, + greedy: true, + inside: { + 'internal-subset': { + pattern: /(\[)[\s\S]+(?=\]>$)/, + lookbehind: true, + greedy: true, + inside: null // see below + }, + 'string': { + pattern: /"[^"]*"|'[^']*'/, + greedy: true + }, + 'punctuation': /^<!|>$|[[\]]/, + 'doctype-tag': /^DOCTYPE/, + 'name': /[^\s<>'"]+/ + } + }, 'cdata': /<!\[CDATA\[[\s\S]*?]]>/i, 'tag': { - pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i, + pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/, greedy: true, inside: { 'tag': { - pattern: /^<\/?[^\s>\/]+/i, + pattern: /^<\/?[^\s>\/]+/, inside: { 'punctuation': /^<\/?/, 'namespace': /^[^\s>\/:]+:/ } }, 'attr-value': { - pattern: /=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i, + pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/, inside: { 'punctuation': [ - /^=/, { - pattern: /(^|[^\\])["']/, - lookbehind: true - } + pattern: /^=/, + alias: 'attr-equals' + }, + /"|'/ ] } }, @@ -600,78 +783,144 @@ Prism.languages.markup = { } }, - 'entity': /&#?[\da-z]{1,8};/i + 'entity': [ + { + pattern: /&[\da-z]{1,8};/i, + alias: 'named-entity' + }, + /&#x?[\da-f]{1,8};/i + ] }; Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] = Prism.languages.markup['entity']; +Prism.languages.markup['doctype'].inside['internal-subset'].inside = Prism.languages.markup; // Plugin to make entity title show the real entity, idea by Roman Komarov -Prism.hooks.add('wrap', function(env) { +Prism.hooks.add('wrap', function (env) { if (env.type === 'entity') { env.attributes['title'] = env.content.replace(/&/, '&'); } }); -Prism.languages.xml = Prism.languages.markup; +Object.defineProperty(Prism.languages.markup.tag, 'addInlined', { + /** + * Adds an inlined language to markup. + * + * An example of an inlined language is CSS with `<style>` tags. + * + * @param {string} tagName 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 + * addInlined('style', 'css'); + */ + value: function addInlined(tagName, lang) { + var includedCdataInside = {}; + includedCdataInside['language-' + lang] = { + pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i, + lookbehind: true, + inside: Prism.languages[lang] + }; + includedCdataInside['cdata'] = /^<!\[CDATA\[|\]\]>$/i; + + var inside = { + 'included-cdata': { + pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i, + inside: includedCdataInside + } + }; + inside['language-' + lang] = { + pattern: /[\s\S]+/, + inside: Prism.languages[lang] + }; + + var def = {}; + def[tagName] = { + pattern: RegExp(/(<__[\s\S]*?>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(/__/g, function () { return tagName; }), 'i'), + lookbehind: true, + greedy: true, + inside: inside + }; + + Prism.languages.insertBefore('markup', 'cdata', def); + } +}); + Prism.languages.html = Prism.languages.markup; Prism.languages.mathml = Prism.languages.markup; Prism.languages.svg = Prism.languages.markup; -Prism.languages.css = { - 'comment': /\/\*[\s\S]*?\*\//, - 'atrule': { - pattern: /@[\w-]+?.*?(?:;|(?=\s*\{))/i, - inside: { - 'rule': /@[\w-]+/ - // See rest below - } - }, - 'url': /url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i, - 'selector': /[^{}\s][^{};]*?(?=\s*\{)/, - 'string': { - pattern: /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, - greedy: true - }, - 'property': /[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i, - 'important': /!important\b/i, - 'function': /[-a-z0-9]+(?=\()/i, - 'punctuation': /[(){};:]/ -}; +Prism.languages.xml = Prism.languages.extend('markup', {}); +Prism.languages.ssml = Prism.languages.xml; +Prism.languages.atom = Prism.languages.xml; +Prism.languages.rss = Prism.languages.xml; -Prism.languages.css['atrule'].inside.rest = Prism.languages.css; +(function (Prism) { -if (Prism.languages.markup) { - Prism.languages.insertBefore('markup', 'tag', { - 'style': { - pattern: /(<style[\s\S]*?>)[\s\S]*?(?=<\/style>)/i, - lookbehind: true, - inside: Prism.languages.css, - alias: 'language-css', - greedy: true - } - }); + var string = /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/; - Prism.languages.insertBefore('inside', 'attr-value', { - 'style-attr': { - pattern: /\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i, + Prism.languages.css = { + 'comment': /\/\*[\s\S]*?\*\//, + 'atrule': { + pattern: /@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/, inside: { - 'attr-name': { - pattern: /^\s*style/i, - inside: Prism.languages.markup.tag.inside - }, - 'punctuation': /^\s*=\s*['"]|['"]\s*$/, - 'attr-value': { - pattern: /.+/i, - inside: Prism.languages.css + 'rule': /^@[\w-]+/, + 'selector-function-argument': { + pattern: /(\bselector\s*\((?!\s*\))\s*)(?:[^()]|\((?:[^()]|\([^()]*\))*\))+?(?=\s*\))/, + lookbehind: true, + alias: 'selector' } - }, - alias: 'language-css' - } - }, Prism.languages.markup.tag); -} -; + // See rest below + } + }, + 'url': { + pattern: RegExp('url\\((?:' + string.source + '|[^\n\r()]*)\\)', 'i'), + greedy: true, + inside: { + 'function': /^url/i, + 'punctuation': /^\(|\)$/ + } + }, + 'selector': RegExp('[^{}\\s](?:[^{};"\']|' + string.source + ')*?(?=\\s*\\{)'), + 'string': { + pattern: string, + greedy: true + }, + 'property': /[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i, + 'important': /!important\b/i, + 'function': /[-a-z0-9]+(?=\()/i, + 'punctuation': /[(){};:,]/ + }; + + Prism.languages.css['atrule'].inside.rest = Prism.languages.css; + + 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]|(?!\1)[^\\])*\1/i, + inside: { + 'attr-name': { + pattern: /^\s*style/i, + inside: markup.tag.inside + }, + 'punctuation': /^\s*=\s*['"]|['"]\s*$/, + 'attr-value': { + pattern: /.+/i, + inside: Prism.languages.css + } + }, + alias: 'language-css' + } + }, markup.tag); + } + +}(Prism)); + Prism.languages.clike = { 'comment': [ { @@ -689,17 +938,17 @@ Prism.languages.clike = { greedy: true }, 'class-name': { - pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i, + pattern: /(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i, lookbehind: true, inside: { - punctuation: /[.\\]/ + 'punctuation': /[.\\]/ } }, 'keyword': /\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/, 'boolean': /\b(?:true|false)\b/, 'function': /\w+(?=\()/, 'number': /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i, - 'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/, + 'operator': /[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/, 'punctuation': /[{}[\];(),.:]/ }; @@ -716,37 +965,66 @@ Prism.languages.javascript = Prism.languages.extend('clike', { pattern: /((?:^|})\s*)(?:catch|finally)\b/, lookbehind: true }, - /\b(?:as|async|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|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|for|from|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 + }, ], - 'number': /\b(?:(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+)n?|\d+n|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/, + 'number': /\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/, // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444) - 'function': /[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\(|\.(?:apply|bind|call)\()/, - 'operator': /-[-=]?|\+[+=]?|!=?=?|<<?=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/ + 'function': /#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/, + 'operator': /--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/ }); -Prism.languages.javascript['class-name'][0].pattern = /(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/ +Prism.languages.javascript['class-name'][0].pattern = /(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/; Prism.languages.insertBefore('javascript', 'keyword', { 'regex': { - pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/, + pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/, lookbehind: true, greedy: true }, // This must be declared before keyword because we use "function" inside the look-forward 'function-variable': { - pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i, + pattern: /#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/, alias: 'function' }, - 'constant': /\b[A-Z][A-Z\d_]*\b/ + 'parameter': [ + { + pattern: /(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/, + lookbehind: true, + inside: Prism.languages.javascript + }, + { + pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i, + inside: Prism.languages.javascript + }, + { + pattern: /(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/, + lookbehind: true, + inside: Prism.languages.javascript + }, + { + pattern: /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/, + lookbehind: true, + inside: Prism.languages.javascript + } + ], + 'constant': /\b[A-Z](?:[A-Z_]|\dx?)*\b/ }); Prism.languages.insertBefore('javascript', 'string', { 'template-string': { - pattern: /`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/, + pattern: /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/, greedy: true, inside: { + 'template-punctuation': { + pattern: /^`|`$/, + alias: 'string' + }, 'interpolation': { - pattern: /\${[^}]+}/, + pattern: /((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/, + lookbehind: true, inside: { 'interpolation-punctuation': { pattern: /^\${|}$/, @@ -761,15 +1039,7 @@ Prism.languages.insertBefore('javascript', 'string', { }); if (Prism.languages.markup) { - Prism.languages.insertBefore('markup', 'tag', { - 'script': { - pattern: /(<script[\s\S]*?>)[\s\S]*?(?=<\/script>)/i, - lookbehind: true, - inside: Prism.languages.javascript, - alias: 'language-javascript', - greedy: true - } - }); + Prism.languages.markup.tag.addInlined('script', 'javascript'); } Prism.languages.js = Prism.languages.javascript; @@ -779,13 +1049,36 @@ Prism.languages.python = { pattern: /(^|[^\\])#.*/, lookbehind: true }, + 'string-interpolation': { + pattern: /(?:f|rf|fr)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i, + greedy: true, + inside: { + 'interpolation': { + // "{" <expression> <optional "!s", "!r", or "!a"> <optional ":" format specifier> "}" + pattern: /((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/, + lookbehind: true, + inside: { + 'format-spec': { + pattern: /(:)[^:(){}]+(?=}$)/, + lookbehind: true + }, + 'conversion-option': { + pattern: /![sra](?=[:}]$)/, + alias: 'punctuation' + }, + rest: null + } + }, + 'string': /[\s\S]+/ + } + }, 'triple-quoted-string': { - pattern: /("""|''')[\s\S]+?\1/, + pattern: /(?:[rub]|rb|br)?("""|''')[\s\S]*?\1/i, greedy: true, alias: 'string' }, 'string': { - pattern: /("|')(?:\\.|(?!\1)[^\\\r\n])*\1/, + pattern: /(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i, greedy: true }, 'function': { @@ -796,17 +1089,29 @@ Prism.languages.python = { pattern: /(\bclass\s+)\w+/i, lookbehind: true }, + 'decorator': { + pattern: /(^\s*)@\w+(?:\.\w+)*/im, + lookbehind: true, + alias: ['annotation', 'punctuation'], + inside: { + 'punctuation': /\./ + } + }, 'keyword': /\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/, - 'builtin':/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/, + 'builtin': /\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/, 'boolean': /\b(?:True|False|None)\b/, 'number': /(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i, 'operator': /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/, 'punctuation': /[{}[\];(),.:]/ }; +Prism.languages.python['string-interpolation'].inside['interpolation'].inside.rest = Prism.languages.python; + +Prism.languages.py = Prism.languages.python; + Prism.languages['visual-basic'] = { 'comment': { - pattern: /(?:['‘’]|REM\b).*/i, + pattern: /(?:['‘’]|REM\b)(?:[^\r\n_]|_(?:\r\n?|\n)?)*/i, inside: { 'keyword': /^REM/i } @@ -817,7 +1122,7 @@ Prism.languages['visual-basic'] = { greedy: true }, 'string': { - pattern: /["“”](?:["“”]{2}|[^"“”])*["“”]C?/i, + pattern: /\$?["“”](?:["“”]{2}|[^"“”])*["“”]C?/i, greedy: true }, 'date': { @@ -826,7 +1131,7 @@ Prism.languages['visual-basic'] = { }, 'number': /(?:(?:\b\d+(?:\.\d+)?|\.\d+)(?:E[+-]?\d+)?|&[HO][\dA-F]+)(?:U?[ILS]|[FRD])?/i, 'boolean': /\b(?:True|False|Nothing)\b/i, - 'keyword': /\b(?:AddHandler|AddressOf|Alias|And(?:Also)?|As|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|C(?:Bool|Byte|Char|Date|Dbl|Dec|Int|Lng|Obj|SByte|Short|Sng|Str|Type|UInt|ULng|UShort)|Char|Class|Const|Continue|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else(?:If)?|End(?:If)?|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get(?:Type|XMLNamespace)?|Global|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|IsNot|Let|Lib|Like|Long|Loop|Me|Mod|Module|Must(?:Inherit|Override)|My(?:Base|Class)|Namespace|Narrowing|New|Next|Not(?:Inheritable|Overridable)?|Object|Of|On|Operator|Option(?:al)?|Or(?:Else)?|Out|Overloads|Overridable|Overrides|ParamArray|Partial|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|SByte|Select|Set|Shadows|Shared|short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TryCast|TypeOf|U(?:Integer|Long|Short)|Using|Variant|Wend|When|While|Widening|With(?:Events)?|WriteOnly|Xor)\b/i, + 'keyword': /\b(?:AddHandler|AddressOf|Alias|And(?:Also)?|As|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|C(?:Bool|Byte|Char|Date|Dbl|Dec|Int|Lng|Obj|SByte|Short|Sng|Str|Type|UInt|ULng|UShort)|Char|Class|Const|Continue|Currency|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else(?:If)?|End(?:If)?|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get(?:Type|XMLNamespace)?|Global|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|IsNot|Let|Lib|Like|Long|Loop|Me|Mod|Module|Must(?:Inherit|Override)|My(?:Base|Class)|Namespace|Narrowing|New|Next|Not(?:Inheritable|Overridable)?|Object|Of|On|Operator|Option(?:al)?|Or(?:Else)?|Out|Overloads|Overridable|Overrides|ParamArray|Partial|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|SByte|Select|Set|Shadows|Shared|short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TryCast|Type|TypeOf|U(?:Integer|Long|Short)|Using|Variant|Wend|When|While|Widening|With(?:Events)?|WriteOnly|Xor)\b/i, 'operator': [ /[+\-*/\\^<=>&#@$%!]/, { @@ -838,6 +1143,7 @@ Prism.languages['visual-basic'] = { }; Prism.languages.vb = Prism.languages['visual-basic']; + (function () { if (typeof self === 'undefined' || !self.Prism || !self.document) { @@ -867,6 +1173,9 @@ Prism.languages.vb = Prism.languages['visual-basic']; if (whiteSpace === 'pre-wrap' || whiteSpace === 'pre-line') { var codeElement = element.querySelector('code'); var lineNumbersWrapper = element.querySelector('.line-numbers-rows'); + if (!codeElement || !lineNumbersWrapper) { + return; + } var lineNumberSizer = element.querySelector('.line-numbers-sizer'); var codeLines = codeElement.textContent.split(NEW_LINE_EXP); @@ -911,28 +1220,38 @@ Prism.languages.vb = Prism.languages['visual-basic']; return; } + var code = env.element; + var pre = code.parentNode; + // works only for <code> wrapped inside <pre> (not inline) - var pre = env.element.parentNode; - var clsReg = /\s*\bline-numbers\b\s*/; - if ( - !pre || !/pre/i.test(pre.nodeName) || - // Abort only if nor the <pre> nor the <code> have the class - (!clsReg.test(pre.className) && !clsReg.test(env.element.className)) - ) { + if (!pre || !/pre/i.test(pre.nodeName)) { return; } - if (env.element.querySelector('.line-numbers-rows')) { - // Abort if line numbers already exists + // Abort if line numbers already exists + if (code.querySelector('.line-numbers-rows')) { return; } - if (clsReg.test(env.element.className)) { - // Remove the class 'line-numbers' from the <code> - env.element.className = env.element.className.replace(clsReg, ' '); + var addLineNumbers = false; + var lineNumbersRegex = /(?:^|\s)line-numbers(?:\s|$)/; + + for (var element = code; element; element = element.parentNode) { + if (lineNumbersRegex.test(element.className)) { + addLineNumbers = true; + break; + } + } + + // only add line numbers if <code> or one of its ancestors has the `line-numbers` class + if (!addLineNumbers) { + return; } - if (!clsReg.test(pre.className)) { - // Add the class 'line-numbers' to the <pre> + + // Remove the class 'line-numbers' from the <code> + code.className = code.className.replace(lineNumbersRegex, ' '); + // Add the class 'line-numbers' to the <pre> + if (!lineNumbersRegex.test(pre.className)) { pre.className += ' line-numbers'; } @@ -940,8 +1259,7 @@ Prism.languages.vb = Prism.languages['visual-basic']; var linesNum = match ? match.length + 1 : 1; var lineNumbersWrapper; - var lines = new Array(linesNum + 1); - lines = lines.join('<span></span>'); + var lines = new Array(linesNum + 1).join('<span></span>'); lineNumbersWrapper = document.createElement('span'); lineNumbersWrapper.setAttribute('aria-hidden', 'true'); @@ -969,10 +1287,10 @@ Prism.languages.vb = Prism.languages['visual-basic']; */ Prism.plugins.lineNumbers = { /** - * Get node for provided line number - * @param {Element} element pre element - * @param {Number} number line number - * @return {Element|undefined} + * Returns the node of the given line number in the given element. + * @param {Element} element A `<pre>` element with line numbers. + * @param {Number} number + * @returns {Element | undefined} */ getLine: function (element, number) { if (element.tagName !== 'PRE' || !element.classList.contains(PLUGIN_NAME)) { @@ -993,10 +1311,21 @@ Prism.languages.vb = Prism.languages['visual-basic']; var lineIndex = number - lineNumberStart; return lineNumberRows.children[lineIndex]; + }, + /** + * Resizes the line numbers of the given element. + * + * This function will not add line numbers. It will only resize existing ones. + * @param {Element} element A `<pre>` element with line numbers. + * @returns {void} + */ + resize: function (element) { + _resizeElement(element); } }; }()); + (function() { var assign = Object.assign || function (obj1, obj2) { @@ -1147,7 +1476,7 @@ Prism.hooks.add('before-sanity-check', function (env) { // Normal mode var pre = env.element.parentNode; - var clsReg = /\bno-whitespace-normalization\b/; + var clsReg = /(?:^|\s)no-whitespace-normalization(?:\s|$)/; if (!env.code || !pre || pre.nodeName.toLowerCase() !== 'pre' || clsReg.test(pre.className) || clsReg.test(env.element.className)) return; |