2

I am currently making a userscript to interpret the APL programming language in a Stackexchange chat window. This is the code I have come up with:

// ==UserScript==
// @name     APL chat
// @version  2
// @grant    GM_xmlhttpRequest
// @grant    GM_listValues
// @match    https://chat.stackexchange.com/*
// @run-at   document-start
// @require  https://cdn.jsdelivr.net/npm/apl@0.1.15/lib/apl.min.js
// ==/UserScript==

// thanks to @cvzi for making it work correctly!

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations, observer) {
    // fired when a mutation occurs
    console.log(mutations, observer);
    let codes = document.getElementsByTagName("code");
        for (let elem of codes) {
            if(elem.innerText && !('interpreted' in elem.dataset) && elem.innerText[0] == '⋄') {
                elem.dataset.interpreted = true;  // see https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes#JavaScript_access
                let result = ''
                let color = 'red'
                // Catch apl error and show it in orange color
                try {
                    result = apl(elem.innerText).toString()
                } catch(e) {
                    result = e.toString()
                    color = 'orange'
                }
                let tmp = document.createElement("div");
                tmp.innerHTML = "<pre style=\"color:"+color+"\">"+result.replace("\n","<br>")+"</pre>";
                let parent = elem.parentElement;
                parent.appendChild(tmp.firstChild);
                //console.log(result);
            }
        }
});

window.addEventListener('DOMContentLoaded', (event) => {
    alert = function() {}; // Prevents ⎕← and ⍞← from trggering alerts
    window.alert = function(){};
      observer.observe(document, {
      subtree: true,
    ChildList:true,
      attributes: true
    });

});

When a code block is written to the body with a character in it, it should be executed and displayed in red, underneath the code.

The apl() function takes a single string, and interprets in in the APL language, and it is imported from the @require statement, using ngn's javascript APL interpreter. After ngn's confirmation, I can say that it does not interfere with any globals.

It works perfectly on firefox(Greasemonkey), but on Chrome's Tampermonkey, any chat.stackexchange.com page I visit never stops loading. It gets stuck at this point:

Screenshot of website

And these are the console errors:

Uncaught TypeError: o.substr is not a function
    at HTMLLIElement.<anonymous> (master-chat-with-millinery.js?v=93a5b9100f35:1)
    at Function.each (jquery.min.js:2)
    at n.fn.init.each (jquery.min.js:2)
    at o (master-chat-with-millinery.js?v=93a5b9100f35:1)
    at Sidebar (master-chat-with-millinery.js?v=93a5b9100f35:1)
    at Vt (master-chat-with-millinery.js?v=93a5b9100f35:3)
    at StartChat (master-chat-with-millinery.js?v=93a5b9100f35:3)
    at HTMLDocument.<anonymous> (the-nineteenth-byte:182)
    at i (jquery.min.js:2)
    at Object.fireWith [as resolveWith] (jquery.min.js:2)
master-chat-with-millinery.js?v=93a5b9100f35:7 Uncaught TypeError: Cannot read property 'resolve' of undefined
    at Object.<anonymous> (master-chat-with-millinery.js?v=93a5b9100f35:7)
    at i (jquery.min.js:2)
    at Object.fireWith [as resolveWith] (jquery.min.js:2)
    at y (jquery.min.js:4)
    at XMLHttpRequest.c (jquery.min.js:4)
DevTools failed to load SourceMap: Could not load content for chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/sm/66503b0e4799e2375dd4e11269326fc0fcb9a65474ff19f45c7f956fd381cea8.map: HTTP error: status code 404, net::ERR_UNKNOWN_URL_SCHEME

I have my suspicions on the MutationObserver and the @grant, but I'm not sure what exactly is causing this issue. It could be something else.

What is the correct fix for this problem?

Razetime
  • 216
  • 3
  • 19
  • I cannot reproduce the problem. Have you tried to execute the script later, when the chat is fully loaded. For example window.setTimeout around the whole script to wait a few seconds before execution – cuzi Dec 29 '20 at 20:12
  • @cuzi sure, will try that. – Razetime Dec 30 '20 at 01:47
  • @cuzi it seems to have the same problem with a 10 second timeout. I also tried clearing cookies and cache. – Razetime Dec 30 '20 at 02:06
  • @cuzi ok, it seems to work when I inline the minified js instead of requiring it. – Razetime Dec 30 '20 at 02:22

2 Answers2

1

You set @run-at document-start in your userscript, this makes the script run before the page loaded, if you remove it, the script is injected after DOMContentLoaded was fired.

If that does not help, you could try to use the original source from gitlab @require https://gitlab.com/n9n/apl/-/raw/master/apl.js you would need to change the apl call to result = apl.fmt(apl(elem.innerText)).toString()

I assume the conflict happens because the apl library (https://cdn.jsdelivr.net/npm/apl@0.1.15/lib/apl.js) has some coffee script helper functions included at the top of the file, notably there are some Array.prototype.* methods declared which will also be available in the page. These could break the page, see this question for more information: Why is extending native objects a bad practice?

cuzi
  • 978
  • 10
  • 21
0

The problem here was resolved by moving the @require script inside the DOMContentLoaded listener like in this link.

I think the main problem was that the interpreter was causing conflicts, since it was getting loaded before the page loaded. I'd appreciate it if someone could find a way to make this work with @require in it though.

Citation

Razetime
  • 216
  • 3
  • 19
  • You set @ run-at document-start in your userscript, this makes the script run before the page loaded, if you remove it, the script is injected after DOMContentLoaded was fired. If that does not help, you could try to use the original source from gitlab @ require https://gitlab.com/n9n/apl/-/raw/master/apl.js you would need to change the apl call to result = apl.fmt(apl(elem.innerText)).toString() – cuzi Dec 31 '20 at 20:08
  • @cuzi This works perfectly. Feel free to add an answer so I can award the bounty. – Razetime Jan 01 '21 at 09:35
  • 1
    [See the possible values `@run-at` can take and what each means](https://www.tampermonkey.net/documentation.php#_run_at). – double-beep Jan 01 '21 at 16:22