greasemonkey:ii-distraction-conversations
ii-distraction-conversations
- ii-distraction-conversations.user.js
// ==UserScript== // @name ii-distractionsort // @namespace http://thedarkworld.net/projects/ii-distractionsort/ // @version 0.1 // @description Compiles your Distractions into converstions for easier reading. // @match http://improbableisland.com/mail.php // @match http://www.improbableisland.com/mail.php // @match http://improbableisland.com/mail.php?sendmail=true // @match http://www.improbableisland.com/mail.php?sendmail=true // @copyright 2013+, Rentoraa // ==/UserScript== if( location.search.match(/sendmail/)) {location.href = "mail.php";} else { var firstrun, messages, senders, unread, inboxdata, inbox, toread, pagetitle, content, outboxdata, outbox; firstrun = !localStorage["iids-messages"]; localStorage["iids-messages"] = localStorage["iids-messages"] || "{}"; localStorage["iids-unread"] = localStorage["iids-unread"] || "[]"; messages = JSON.parse(localStorage["iids-messages"]); senders = {}; unread = JSON.parse(localStorage["iids-unread"]); // quickly scan inbox for IDs, outbox will be read later. inboxdata = document.querySelectorAll("input[type=checkbox][name='markedmessages[]']"); inbox = []; [].forEach.call(inboxdata,function(cb) { var sender, id; sender = cb.parentNode.parentNode.children[1].children[0].innerHTML; id = parseInt(cb.getAttribute("value"),10); senders[sender] = senders[sender] || []; senders[sender].push(id); inbox.push(id); if( cb.parentNode.parentNode.className.match(/\btrlight\b/)) {unread.push(sender);} }); localStorage["iids-unread"] = JSON.stringify(unread); toread = {"inbox":inbox.filter(function(id) {return !messages[id];})}; pagetitle = document.querySelector("h2"); content = document.createElement('div'); // delete everything between the title and the privacy warning - we are taking over! (function() { var curr, next; curr = pagetitle.nextSibling; while(curr.nodeName != "DIV" || curr.getAttribute("align") != "center") { next = curr.nextSibling; curr.parentNode.removeChild(curr); curr = next; } curr.style.marginTop = "40px"; curr.parentNode.insertBefore(content,curr); }()); if( !firstrun) {goForIt();} else { // let's say hello first. content.innerHTML = "<h2>Rentoraa's Distraction Sort script</h2>" +"<p>Hello, and welcome to my Distraction Sort script! As you can see, I have taken over the Distractions page.</p>" +"<p>In a moment, we'll go through the initial setup. This will scan your Inbox and Outbox to build Conversations. It has to load each message in turn, so this may take a little time the first time through.</p>" +"<p>The next time you come here, only new messages will be scanned, making it much faster.</p>" +"<p>I hope you enjoy the script, and I hope it makes it much easier to communicate!</p>"; content.appendChild(document.createElement('p')).appendChild(document.createElement('button')).appendChild(document.createTextNode("Get on with it!")).parentNode.onclick = function() { content.innerHTML = ""; goForIt(); }; } } function goForIt() { content.appendChild(document.createElement('p')).appendChild(document.createTextNode("Loading, please wait a moment...")); haxFrame("mail.php?sent=true",null,function(d) { var count, progbar, tocheck, tocompile, convos, booted; outboxdata = d.querySelectorAll("input[type=checkbox][name='markedmessages[]']"); outbox = []; [].forEach.call(outboxdata,function(cb) { var sender, id; sender = cb.parentNode.parentNode.children[1].children[0].innerHTML; id = parseInt(cb.getAttribute("value"),10); senders[sender] = senders[sender] || []; senders[sender].push(id); outbox.push(id); }); toread.outbox = outbox.filter(function(id) {return !messages[id];}); count = toread.inbox.length+toread.outbox.length; content.children[0].firstChild.nodeValue = "There are a total of "+count+" messages to process."; // make a progress bar, and for coolness it looks like the healthbar in battle. Sadly, the HTML is horrendous. Tables? EW! progbar = (function() { var tbl, tr, td1, td2; tbl = document.createElement("table"); tbl.setAttribute("cellpadding",0); tbl.setAttribute("cellspacing",0); tr = tbl.appendChild(document.createElement('tbody')).appendChild(document.createElement('tr')); td1 = tr.appendChild(document.createElement('td')); td1.setAttribute("width","220px"); td1 = td1.appendChild(document.createElement('div')); td1.style.cssText = "width:200px; height:25px; background:url('/modules/combatbars/lifebar-bottom.png'); border:1px solid #000;"; td1 = td1.appendChild(document.createElement('div')); td1.style.cssText = "width:195px; height:25px; padding-left:5px; background:url('/modules/combatbars/lifebar-top.png') no-repeat; line-height:25px; background-position:-800px 0; color:#fff; text-shadow: -1px 1px #000;"; td1.appendChild(document.createElement('b')).appendChild(document.createTextNode("0%")); td2 = tr.appendChild(document.createElement('td')).appendChild(document.createElement('b')).appendChild(document.createTextNode("Loading...")); return { "box":tbl, "bar":td1, "progress":td1.children[0].firstChild, "status":td2 }; }()); // now that sodding bar is created, let's use it! content.appendChild(progbar.box); function extractMessage(d,side) { // when reading a message, we go from the <b> tag that has "Sent" in it, up to either a text node with "---Original Message from " or the first <a> tag. var startnode, currnode, box, name; startnode = [].filter.call(d.getElementsByTagName('b'),function(b) {return b.textContent == "Sent:";})[0]; currnode = startnode; while(currnode) { if( currnode.nodeName == "A" || (currnode.nodeValue && currnode.nodeValue.match(/\s+---Original Message from /))) { break; } currnode = currnode.nextSibling; } // back up, we don't want the trailing <br>s! currnode = currnode.previousSibling; while(currnode.nodeName == "BR") {currnode = currnode.previousSibling;} // okay! We have our message contents! Now to move everything to a new container so we can get the innerHTML box = document.createElement('div'); while(startnode.nextSibling != currnode) {box.appendChild(startnode.nextSibling);} box.appendChild(currnode); box.insertBefore(startnode,box.firstChild); return "<div style="\"width:400px;border:1px" dotted #000;"+(side ? "margin-left:200px" : "")+"\">"+box.innerHTML+"</div>"; } progbar.status.nodeValue = "Updating Inbox..."; booted = 0; count = toread.inbox.length; setTimeout(function() { var msgid, doagain; doagain = arguments.callee; msgid = toread.inbox.shift(); if( msgid) { haxFrame("mail.php?op=read&msgid;="+msgid+"&sent;=",null,function(d) { messages[msgid] = extractMessage(d,false); progbar.bar.style.backgroundPosition = (-800+200*(count-toread.inbox.length)/count)+"px 0"; progbar.progress.nodeValue = Math.round(25*(count-toread.inbox.length)/count)+"%"; if( toread.inbox.length == 0) { progbar.status.nodeValue = "Updating Outbox..."; count = toread.outbox.length; booted = 1; } setTimeout(doagain,1); }); } else { if( booted == 0) { progbar.status.nodeValue = "Updating Outbox..."; count = toread.outbox.length; booted = 1; } msgid = toread.outbox.shift(); if( msgid) { haxFrame("mail.php?op=read&msgid;="+msgid+"&sent;=true",null,function(d) { messages[msgid] = extractMessage(d,true); progbar.bar.style.backgroundPosition = (-600+200*(count-toread.outbox.length)/count)+"px 0"; progbar.progress.nodeValue = Math.round(25+25*(count-toread.outbox.length)/count)+"%"; if( toread.outbox.length == 0) { progbar.status.nodeValue = "Cleaning up messages..."; // so we're done getting our messages. All sent and received messages are now safe in messages{} // now we need to go delete any messages that have been deleted, as they may have been left in storage. tocheck = Object.keys(messages).map(function(a) {return parseInt(a,10);}); count = tocheck.length; booted = 2; } setTimeout(doagain,1); }); } else { if( booted == 1) { progbar.status.nodeValue = "Cleaning up messages..."; tocheck = Object.keys(messages).map(function(a) {return parseInt(a,10);}); count = tocheck.length; booted = 2; } msgid = tocheck.shift(); if( msgid) { if( inbox.indexOf(msgid) == -1 && outbox.indexOf(msgid) == -1) { delete messages[msgid]; } progbar.bar.style.backgroundPosition = (-400+200*(count-tocheck.length)/count)+"px 0"; progbar.progress.nodeValue = Math.round(50+25*(count-tocheck.length)/count)+"%"; if( tocheck.length == 0) { progbar.status.nodeValue = "Compiling messages..."; // save the messages! localStorage["iids-messages"] = JSON.stringify(messages); // the final step! Now we just need to iterate through the remaining messages, and build Conversations! Almost there! // PS. If you're reading this and trying to understand my code, good luck to you! tocompile = Object.keys(senders); count = tocompile.length; convos = []; booted = 3; } setTimeout(doagain,1); } else { if( booted == 2) { progbar.status.nodeValue = "Compiling messages..."; localStorage["iids-messages"] = JSON.stringify(messages); tocompile = Object.keys(senders); count = tocompile.length; convos = []; booted = 3; } msgid = tocompile.shift(); if( msgid) { // msgid is actually the sender's name. Oh well. // Also senders and unread are defined WAAAAY back at the top of this script. senders[msgid].sort(function(a,b) { var at = messages[a].match(/\d{4}-\d\d-\d\d \d\d:\d\d:\d\d/), bt = messages[b].match(/\d{4}-\d\d-\d\d \d\d:\d\d:\d\d/); return bt < at ? -1 : 1; }); convos.push({ "messages":senders[msgid].map(function(id) { // here id is the message ID. How confusing! Yay for comments! return messages[id]; }), "sender":msgid, "unread":unread.indexOf(msgid) > -1, "mostrecent":senders[msgid].filter(function(a) {return inbox.indexOf(a) > -1;}).pop() }); progbar.bar.style.backgroundPosition = (-200+200*(count-tocheck.length)/count)+"px 0"; progbar.progress.nodeValue = Math.round(75+25*(count-tocheck.length)/count)+"%"; if( tocompile.length == 0) { progbar.status.nodeValue = "Done!"; setTimeout(function() { content.removeChild(progbar.box); document.title = pagetitle.innerHTML = "Awesome Conversations"; showConversations(convos); },1000); } else {setTimeout(doagain,1);} } else { progbar.status.nodeValue = "Done!"; setTimeout(function() { content.removeChild(progbar.box); document.title = pagetitle.innerHTML = "Awesome Conversations"; showConversations(convos); },1000); } } } } },1); }); } function showConversations(convos,defaultindex) { // okay! We've made it! We now have "convos", an array of conversations // each conversation has "messages", an array. "sender" the username, "unread" a boolean showing if there are any unread messages here. // "mostrecent" the ID of the most recent message in the stack. while(content.firstChild) {content.removeChild(content.firstChild);} var ul, p, a; p = content.appendChild(document.createElement('p')); p.appendChild(document.createTextNode("You have "+convos.length+" Conversation"+(convos.length==1?"":"s")+" ")); a = p.appendChild(document.createElement('a')); a.href = "/mail.php"; a.appendChild(document.createTextNode("Reload")); ul = content.appendChild(document.createElement('ul')); convos.forEach(function(convo,cindex) { a = ul.appendChild(document.createElement('li')).appendChild(document.createElement('a')); a.innerHTML = convo.sender+(convo.unread ? " (New!)" : ""); a.href = "#"; a.onclick = function() { var del, id; content.innerHTML = "<h2>Conversation with "+convo.sender+"</h2><p><a href="\"#\""><< Back</a> | <a href="\"/mail.php?op=write&replyto="+convo.mostrecent+"\"">Reply</a></p>"+convo.messages.join("\n"); content.children[1].children[0].onclick = function() { showConversations(convos); return false; }; [].slice.call(content.children,2).forEach(function(c,index) { var p, inp, btn; p = c.appendChild(document.createElement('p')); p.style.textAlign = "right"; inp = document.createElement('input'); inp.type = "checkbox"; inp.setAttribute("name","tobedeleted"); inp.value = (c.style.marginLeft ? "1" : "0")+"-"+senders[convo.sender][index]; btn = document.createElement('button'); btn.appendChild(document.createTextNode("Delete")); p.appendChild(inp); p.appendChild(document.createTextNode(" ")); p.appendChild(btn); p.style.margin = "0"; btn.onclick = function() { deleteMessage([inp],convos,cindex); }; }); del = content.appendChild(document.createElement('p')).appendChild(document.createElement('button')); del.appendChild(document.createTextNode("Delete selected")); del.onclick = function() { deleteMessage(document.querySelectorAll("input[name=tobedeleted]:checked"),convos,cindex); }; if( convo.unread) { id = unread.indexOf(convo.sender); if( id > -1) {unread.splice(id,1);} localStorage["iids-unread"] = JSON.stringify(unread); } return false; }; }); if( defaultindex) { ul.children[defaultindex].children[0].onclick(); } } function deleteMessage(cbs,convos,cindex) { var todel = [[],[],[],[]]; [].forEach.call(cbs,function(cb) { var issent, id; issent = parseInt(cb.value.split("-")[0],10); id = parseInt(cb.value.split("-")[1],10); todel[issent].push(id); todel[2].push(id); todel[3].push(cb.parentNode.parentNode); }); if( todel[2].length > 0 && confirm("Are you sure you want to delete "+(todel[2].length == 1 ? "this message" : "these "+todel[2].length+" messages")+"?")) { if( todel[0].length > 0) { haxFrame("mail.php",{"markedmessages[]":todel[0],"delete_button":"Delete Checked Messages"},function() { if( todel[1].length > 0) { haxFrame("mail.php?sent=true",{"markedmessages[]":todel[1],"delete_button":"Delete Checked Messages"},function() { todel[3].forEach(function(n) {n.parentNode.removeChild(n);}); }); } else { todel[3].forEach(function(n) {n.parentNode.removeChild(n);}); } }); } else { haxFrame("mail.php?sent=true",{"markedmessages[]":todel[1],"delete_button":"Delete Checked Messages"},function() { todel[3].forEach(function(n) {n.parentNode.removeChild(n);}); }); } senders[convos[cindex].sender] = senders[convos[cindex].sender].filter(function(id) {return todel[2].indexOf(id) < 0;}); convos[cindex].messages = senders[convos[cindex].sender].map(function(id) {return messages[id];}); showConversations(convos,cindex); } } function haxFrame(url,post,cb) { var frame, form, k, i; frame = haxFrame.theFrame; if( !frame) { haxFrame.theFrame = frame = document.createElement('iframe'); frame.style.display = "none"; frame.name = "haxframe"; document.body.appendChild(frame); } frame.onload = function() {cb(frame.contentDocument);}; if( !post) { frame.src = url; } else { form = haxFrame.theForm; if( !form) { haxFrame.theForm = form = document.createElement('form'); form.style.display = "none"; form.method = "post"; form.target = "haxframe"; document.body.appendChild(form); } form.action = url; while(form.firstChild) {form.removeChild(form.firstChild);} for( k in post) { if( post.hasOwnProperty(k)) { (typeof post[k] == "object" ? post[k] : [post[k]]).forEach(function(v) { i = document.createElement('input'); i.type = "text"; i.name = k; i.value = v; form.appendChild(i); }); } } form.submit(); } }
Version 0.1 by Rentoraa (archive.org link)
This is probably the one I'm most proud of. This script completely overhauls the Distractions page. Now, instead of having a hundred messages to sort through, this script will arrange them all neatly into chat-like Conversations. Click a conversation to read it, and respond to it, you get the idea.
Still some work to do on smaller features, but it works and it's definitely much easier to find stuff!
TODO:
Re-add the “Write message” button, right now the only way to talk to someone new is to go to their Bio page and click the link there.
Add a “Reply” form to the conversation itself, rather than having to go to a separate page (since that then requires the system to reload messages). This will also include saving the newly-sent message directly without having to reload, and also allow you to quote any message in the conversation directly - very useful!
Add a “select all” button to make mass-deleting easier.
greasemonkey/ii-distraction-conversations.txt · Last modified: 2023/11/21 18:04 by 127.0.0.1