// ==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 = "

Rentoraa's Distraction Sort script

" +"

Hello, and welcome to my Distraction Sort script! As you can see, I have taken over the Distractions page.

" +"

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.

" +"

The next time you come here, only new messages will be scanned, making it much faster.

" +"

I hope you enjoy the script, and I hope it makes it much easier to communicate!

"; 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 tag that has "Sent" in it, up to either a text node with "---Original Message from " or the first 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
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 "
"+box.innerHTML+"
"; } 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 = "

Conversation with "+convo.sender+"

<< Back | Reply

"+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(); } }