From 5712128ac06c5c85acdc00faea02f9d2fa515107 Mon Sep 17 00:00:00 2001 From: LenAnderson Date: Sat, 20 Jul 2024 12:16:47 -0400 Subject: [PATCH] improve QR editor performance - only run hljs with syntax enabled - only check localStorage once, then rely on the checkbox - run hljs on a 30fps loop instead of event-based - use morphdom to update syntax dom instead of innerHTML --- .../quick-reply/lib/morphdom-esm.js | 769 ++++++++++++++++++ .../quick-reply/lib/morphdom.LICENSE.txt | 21 + .../extensions/quick-reply/src/QuickReply.js | 34 +- 3 files changed, 819 insertions(+), 5 deletions(-) create mode 100644 public/scripts/extensions/quick-reply/lib/morphdom-esm.js create mode 100644 public/scripts/extensions/quick-reply/lib/morphdom.LICENSE.txt diff --git a/public/scripts/extensions/quick-reply/lib/morphdom-esm.js b/public/scripts/extensions/quick-reply/lib/morphdom-esm.js new file mode 100644 index 000000000..7a13a27fc --- /dev/null +++ b/public/scripts/extensions/quick-reply/lib/morphdom-esm.js @@ -0,0 +1,769 @@ +var DOCUMENT_FRAGMENT_NODE = 11; + +function morphAttrs(fromNode, toNode) { + var toNodeAttrs = toNode.attributes; + var attr; + var attrName; + var attrNamespaceURI; + var attrValue; + var fromValue; + + // document-fragments dont have attributes so lets not do anything + if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE || fromNode.nodeType === DOCUMENT_FRAGMENT_NODE) { + return; + } + + // update attributes on original DOM element + for (var i = toNodeAttrs.length - 1; i >= 0; i--) { + attr = toNodeAttrs[i]; + attrName = attr.name; + attrNamespaceURI = attr.namespaceURI; + attrValue = attr.value; + + if (attrNamespaceURI) { + attrName = attr.localName || attrName; + fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName); + + if (fromValue !== attrValue) { + if (attr.prefix === 'xmlns'){ + attrName = attr.name; // It's not allowed to set an attribute with the XMLNS namespace without specifying the `xmlns` prefix + } + fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue); + } + } else { + fromValue = fromNode.getAttribute(attrName); + + if (fromValue !== attrValue) { + fromNode.setAttribute(attrName, attrValue); + } + } + } + + // Remove any extra attributes found on the original DOM element that + // weren't found on the target element. + var fromNodeAttrs = fromNode.attributes; + + for (var d = fromNodeAttrs.length - 1; d >= 0; d--) { + attr = fromNodeAttrs[d]; + attrName = attr.name; + attrNamespaceURI = attr.namespaceURI; + + if (attrNamespaceURI) { + attrName = attr.localName || attrName; + + if (!toNode.hasAttributeNS(attrNamespaceURI, attrName)) { + fromNode.removeAttributeNS(attrNamespaceURI, attrName); + } + } else { + if (!toNode.hasAttribute(attrName)) { + fromNode.removeAttribute(attrName); + } + } + } +} + +var range; // Create a range object for efficently rendering strings to elements. +var NS_XHTML = 'http://www.w3.org/1999/xhtml'; + +var doc = typeof document === 'undefined' ? undefined : document; +var HAS_TEMPLATE_SUPPORT = !!doc && 'content' in doc.createElement('template'); +var HAS_RANGE_SUPPORT = !!doc && doc.createRange && 'createContextualFragment' in doc.createRange(); + +function createFragmentFromTemplate(str) { + var template = doc.createElement('template'); + template.innerHTML = str; + return template.content.childNodes[0]; +} + +function createFragmentFromRange(str) { + if (!range) { + range = doc.createRange(); + range.selectNode(doc.body); + } + + var fragment = range.createContextualFragment(str); + return fragment.childNodes[0]; +} + +function createFragmentFromWrap(str) { + var fragment = doc.createElement('body'); + fragment.innerHTML = str; + return fragment.childNodes[0]; +} + +/** + * This is about the same + * var html = new DOMParser().parseFromString(str, 'text/html'); + * return html.body.firstChild; + * + * @method toElement + * @param {String} str + */ +function toElement(str) { + str = str.trim(); + if (HAS_TEMPLATE_SUPPORT) { + // avoid restrictions on content for things like `Hi` which + // createContextualFragment doesn't support + //