// ==UserScript== // @name ii-block // @namespace http://enquirer.improbableisland.com/dokuwiki/doku.php?id=greasemonkey // @author Full Metal Lion // @description Blocks a user in chat // @version 2.1 // @match *://*.improbableisland.com/* // @exclude *://enquirer.improbableisland.com/* // @icon http://improbableisland.com/favicon.ico // @grant none // ==/UserScript== //USER-SET VARIABLES: var blockedUserIDs = []; //put users ids here, separated by commas (eg [100001,47,201]) var showBlankLines = true; //set this to false if you want no indication that a message was blocked //INTERNAL PROGRAM LOGIC: //helper function function delL(element){ if(element && element.className != "singlecomment"){ delL(element.previousSibling); element.remove(); } } //the actual function of interest function removeBlockedMessages(){ for (var x of blockedUserIDs){ for (var m of document.querySelectorAll("a[href='bio.php?char="+x+"']")){ //you'd think that we could just call .parent.remove() on the things we find // but actually the chat html is hot garbage and nested improperly, so we have to do this delL(m.previousSibling); if(showBlankLines && m.innerHTML){ //there are actually two elements per comment, one empty. //we only want one (else we'd get double spacing) so we pick the nonempty one. m.before(document.createElement('p')); } } for (var p of ps){ var re = RegExp('<a href="bio\\.php\\?char='+x+'" target="_blank" class="commentarymouseoverlink">[\\w\\W]*?</a>[\\w\\W]*?<br>','gi'); var egregiousExtraMouseoverLink = RegExp('<a href="bio\\.php\\?char='+x+'" target="_blank" class="commentarymouseoverlink"><!--le--></a></span>','gi'); var html = p.innerHTML.replace(egregiousExtraMouseoverLink,''); p.innerHTML = html.replace(re,''); } } } var ps = []; //find chat areas on page load for (var c of document.getElementsByClassName("singlecomment")){ var p = c.parentNode; if(p&&!ps.includes(p)&&p.className!="singlecomment"){ ps.push(p); } } removeBlockedMessages(); //invoke function when page loads //run removeBlockedMessages whenever an ajax chat auto-update completes) //(This is the part that doesn't work in greasemonkey without later shenanigans) //adapted from from https://stackoverflow.com/a/29293383 : (function(open) { window.XMLHttpRequest.prototype.open = function() { this.addEventListener("readystatechange", function() { if(this.readyState == 4 && this.responseURL.startsWith("https://www.improbableisland.com/ajaxchat.php")){ removeBlockedMessages(); } }, false); open.apply(this, arguments); }; })(window.XMLHttpRequest.prototype.open); //If we're running on a system that supports @grant none, then we're done. //However, the greasemonkey extension on Firefox does not //so we must do something special to support it //Here I use the Content Scope Runner as found in //https://wiki.greasespot.net/index.php?title=Content_Scope_Runner&oldid=7215 if ('undefined' == typeof __PAGE_SCOPE_RUN_II_BLOCK_) { (function page_scope_runner() { // If we're _not_ already running in the page, grab the full source // of this script. var my_src = "(" + page_scope_runner.caller.toString() + ")();"; // Create a script node holding this script, plus a marker that lets us // know we are running in the page scope (not the Greasemonkey sandbox). // Note that we are intentionally *not* scope-wrapping here. var script = document.createElement('script'); script.setAttribute("type", "text/javascript"); script.textContent = "var __PAGE_SCOPE_RUN_II_BLOCK_ = true;\n" + my_src; // Insert the script node into the page, so it will run, and immediately // remove it to clean up. Use setTimeout to force execution "outside" of // the user script scope completely. setTimeout(function() { document.body.appendChild(script); document.body.removeChild(script); }, 0); })(); // Stop running, because we know Greasemonkey actually runs us in // an anonymous wrapper. return; }