// ==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();
}
}