hnOsmium0001

Custom version of MathCord (working 2020-5-12) v2.1

May 13th, 2020
116
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Mathcord
  3. // @namespace    http://tampermonkey.net/
  4. // @version      0.3
  5. // @description  Typeset equations in Discord messages.
  6. // @author       Till Hoffmann
  7. // @match        https://discordapp.com/*
  8. // @match        https://discord.com/*
  9. // @resource     katexCSS https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css
  10. // @require      https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js
  11. // @require      https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/contrib/auto-render.min.js
  12. // @grant        GM_addStyle
  13. // @grant        GM_getResourceText
  14. // ==/UserScript==
  15.  
  16. /**
  17.  * Evaluate whether an element has a certain class prefix.
  18.  */
  19. function hasClassPrefix(element, prefix) {
  20.     var classes = (element.getAttribute("class") || "").split();
  21.     return classes.some(x => x.startsWith(prefix));
  22. }
  23.  
  24. (function() {
  25.     'use strict';
  26.  
  27.     if (!renderMathInElement) throw "Katex did not load correctly!";
  28.  
  29.     // Declare rendering options (see https://katex.org/docs/autorender.html#api for details)
  30.     const options = {
  31.         delimiters: [
  32.             {left: "$$", right: "$$", display: true},
  33.             {left: "\\(", right: "\\)", display: false},
  34.             {left: "\\[", right: "\\]", display: true},
  35.             // Needs to come last to prevent over-eager matching of delimiters
  36.             {left: "$", right: "$", display: false},
  37.         ],
  38.     };
  39.  
  40.     // We need to download the CSS, modify any relative urls to be absolute, and inject the CSS
  41.     let katexCSS = GM_getResourceText("katexCSS");
  42.     let pattern = /url\((.*?)\)/gi;
  43.     katexCSS = katexCSS.replace(pattern, 'url(https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/$1)');
  44.     GM_addStyle(katexCSS);
  45.  
  46.     class ChildrenSelector {
  47.         constructor(elm) {
  48.             this.elm = elm;
  49.         }
  50.  
  51.         andThenTag(tag, alternativeElm) {
  52.             if (!this.elm) {
  53.                 this.elm = alternativeElm;
  54.                 return this;
  55.             }
  56.  
  57.             for (const child of this.elm.childNodes) {
  58.                 if (child.tagName === tag) {
  59.                     this.elm = child;
  60.                     return this;
  61.                 }
  62.             }
  63.             this.elm = alternativeElm;
  64.             return this;
  65.         }
  66.  
  67.         andThenClass(prefix, alternativeElm) {
  68.             if (!this.elm) {
  69.                 this.elm = alternativeElm;
  70.                 return this;
  71.             }
  72.  
  73.             for (const child of this.elm.childNodes) {
  74.                 if (hasClassPrefix(child, prefix)) {
  75.                     this.elm = child;
  76.                     return this;
  77.                 }
  78.             }
  79.             // Failed to find a matching children
  80.             this.elm = alternativeElm;
  81.             return this;
  82.         }
  83.  
  84.         accept(successful, failed) {
  85.             if (this.elm) {
  86.                 successful(this.elm);
  87.             } else {
  88.                 failed();
  89.             }
  90.         }
  91.     }
  92.  
  93.     // Monitor the document for changes and render math as necessary
  94.     var observer = new MutationObserver(function(mutations, observer) {
  95.         for (const mutation of mutations) {
  96.             const target = mutation.target;
  97.             // Respond to newly loaded messages
  98.             if (target.tagName === "DIV" && hasClassPrefix(target, "scroller")) {
  99.                 // Iterate over all messages added to the scroller and typeset them
  100.                 for (const added of mutation.addedNodes) {
  101.                     if (added.tagName === "DIV" && hasClassPrefix(added, "message")) {
  102.                         renderMathInElement(added, options);
  103.                     }
  104.                 }
  105.             }
  106.             // Respond to edited messages
  107.             else if (target.tagName === "DIV" && hasClassPrefix(target, "contents") &&
  108.                        hasClassPrefix(target.parentNode, "message")) {
  109.                 for (const added of mutation.addedNodes) {
  110.                     // Do not typeset the interactive edit container
  111.                     if (added.tagName === "DIV" && !added.getAttribute("class")) {
  112.                         continue;
  113.                     }
  114.                     // Hack to get around Discord's slight delay between confirm edit and edit displayed
  115.                     setTimeout(_ => renderMathInElement(added, options), 1000);
  116.                 }
  117.             }
  118.             /*// Hack to respond to loading cached messages. These mutations are only triggered when the user mouse hovers over them
  119.             else if (target.tagName === "DIV" && hasClassPrefix(target, "message")) {
  120.                 renderMathInElement(target, options);
  121.             }*/
  122.             // Respond to reloading cached messages
  123.             else if (target.tagName === "DIV" && hasClassPrefix(target, "content")) {
  124.                 for (const added of mutation.addedNodes) {
  125.                     if (!hasClassPrefix(added, "chat")) continue;
  126.                     //renderMathInElement(added, options);
  127.                     // We expect this element to be a "chat-xxxxx" one
  128.                     new ChildrenSelector(added)
  129.                         .andThenClass("content")
  130.                         .andThenTag("MAIN")
  131.                         .andThenClass("messagesWrapper")
  132.                         .andThenClass("scrollerWrap")
  133.                         .andThenClass("scroller")
  134.                         .andThenClass("scrollerInner")
  135.                         .accept(
  136.                             scroller => {
  137.                                 for (const candidate of scroller.children) {
  138.                                     if (candidate.tagName === "DIV" && hasClassPrefix(candidate, "message")) {
  139.                                         renderMathInElement(candidate, options);
  140.                                     }
  141.                                 }
  142.                             },
  143.                             () => {
  144.                                 throw "Failed to find 'scrollerInner' element on content change (reloading cached meesages)";
  145.                             }
  146.                         )
  147.                 }
  148.             }
  149.         }
  150.     });
  151.     observer.observe(document.body, { childList: true, subtree: true });
  152. })();
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×