From ebbd4ee99be8172c6c62b8d6963feb7f7b72bfa1 Mon Sep 17 00:00:00 2001 From: Olivier Hallot Date: Fri, 29 May 2020 16:45:56 -0300 Subject: 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 (cherry picked from commit 0c721f4ecf55911d125e33e3afea534d1ffe42b2) Reviewed-on: https://gerrit.libreoffice.org/c/help/+/95129 Reviewed-by: Ilmari Lauhakangas --- help3xsl/README.prism.js.txt | 5 + help3xsl/default.css | 7 +- help3xsl/prism.css | 6 +- help3xsl/prism.js | 1069 +++++++++++++++++++++++++++--------------- 4 files changed, 711 insertions(+), 376 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 as in
+ diff --git a/help3xsl/default.css b/help3xsl/default.css index 0ae386088a..76bf71c666 100644 --- a/help3xsl/default.css +++ b/help3xsl/default.css @@ -361,10 +361,11 @@ header { height: 60px; margin-right: 10px; } -[aria-hidden='true'], -[data-a11y-toggle]:not([aria-controls]) { + +[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 +740,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(/ 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 + ''; + 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 + ''; }; +/** + * @param {string} text + * @param {LinkedList} tokenList + * @param {any} grammar + * @param {LinkedListNode} 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 | null} prev The previous node. + * @property {LinkedListNode | null} next The next node. + * @template T + */ + +/** + * @template T + */ +function LinkedList() { + /** @type {LinkedListNode} */ + var head = { value: null, prev: null, next: null }; + /** @type {LinkedListNode} */ + var tail = { value: null, prev: head, next: null }; + head.next = tail; + + /** @type {LinkedListNode} */ + this.head = head; + /** @type {LinkedListNode} */ + this.tail = tail; + this.length = 0; +} + +/** + * Adds a new node with the given value to the list. + * @param {LinkedList} list + * @param {LinkedListNode} node + * @param {T} value + * @returns {LinkedListNode} 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} list + * @param {LinkedListNode} 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} 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': //, 'prolog': /<\?[\s\S]+?\?>/, - 'doctype': //i, + 'doctype': { + // https://www.w3.org/TR/xml/#NT-doctypedecl + pattern: /"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\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': //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 `