// 0.3.3
// - Options accordion. Can be used to turn a feature on and off.
// - -dt-'s script compatibility mode
// (can be [en/dis]able via the options accordion)
// - Forced to be iso-8859-1.
// 0.3.2
// - More timeouts for user to go away.
// - Better handling of commands.. For example:
// /test => "I am testing"
// is defined, if you type
// /test the shoutbox
// it will become I am testing the shoutbox.
// - Will no more allow /help to be sent via shoutbox.
// - Quick text box is moved to lower-left
// - Quick texts sliding...
// 0.3.2pre
// - Hidden feature requested by MeEtc.
// 0.3.1
// - Some commands will not be executed using XMLHttpRequest.
// - Hide the menu when the user clicks other places.
// 0.3.0
// - When you click on a user's name, you can choose to quote them, view their profile, or send them a PM.
// - Quick texts.
// - An error message (toast) will be displayed when the post was not sucessful.
// 0.2.1
// - Message sending bux fix.
// 0.2.0
// - Message sending using XMLHttpRequest.
// - Alert when a user comes in or go out.
// - Some code rewrited.
// - Fixed users online bug.
// 0.1.2
// - Some fixes, thanks to WDZ.
// - Comments added, so it would be easier to edit.
// ==UserScript==
// @name Messenger Plus! Live Shoutbox Improvements
// @namespace http://www.msghelp.net/
// @description .....
// @include http://msghelp.net/shoutbox.php*
// @include *.msghelp.net/shoutbox.php*
// @exclude *action=stats*
// @exclude *action=edit*
// ==/UserScript==
(function(window){
//
// We prefer to use unsafeWindow's document because it has control over
// everything.
//
var document = window.document;
//
// WDZ's way to detect the page. If we are not on page 1, bye bye.
//
var links = document.getElementsByTagName('link')[0];
var i;
for (i = 0; i < links.length; i ++) {
if (links[i].rel == 'prev')
return;
}
//
// No message form? Ahhhh. Bye.
//
if (typeof document.msgform.message == 'undefined')
return;
//
// Toasters
//
var alerts = document.createElement('div');
alerts.appendChild (document.createElement('div'));
alerts.style.position = 'fixed';
alerts.style.bottom = '0';
alerts.style.right = '0';
document.body.appendChild (alerts);
//
// This makes the animations smoother
//
function _smooth(x) {
if (x == 1) {
return 1;
} else if (x == 0) {
return 0;
} else if (x < 0.5) {
x *= 2;
x *= x;
x /= 2;
} else {
x *= 2;
x -= 1;
x = 1 - x;
x *= x;
x = 1 - x;
x += 1;
x /= 2;
}
return x;
}
//
// Creates a toast.
//
function _alert(x) {
//
// Create 2 elements and set their style.
//
var ca = [document.createElement('div'), document.createElement('div')];
ca[0].appendChild(ca[1]);
ca[0].style.width = '24em';
ca[0].style.overflow = 'hidden';
ca[1].style.textAlign = 'center';
ca[1].style.border = '1px solid #7493B8';
ca[1].style.borderWidth = '1px 0 0 1px';
ca[1].style.padding = '1em';
ca[1].style.background = '#E5EFFA';
ca[1].style.color = '#1C2530';
ca[1].style.font = '8pt Tahoma';
ca[1].innerHTML = x;
//
// Animation: Slide out
//
var o2 = 1;
var o2f = 0;
function f2() {
o2 -= 0.08;
if (o2 < 0)
o2 = 0;
ca[0].style.height = Math.round(_smooth(o2) * o2f) + 'px';
if (o2 > 0)
setTimeout (f2, 30);
else
ca[0].parentNode.removeChild (ca[0]);
}
//
// Animation: Fade in
//
var o1 = 0;
function f1() {
o1 += 4;
if (o1 > 100)
o1 = 100;
ca[0].style.MozOpacity = o1 / 100;
ca[0].style.opacity = o1 / 100;
if (o1 < 100)
setTimeout (f1, 20);
else
setTimeout (function() {
o2f = ca[0].offsetHeight;
f2 ();
}, 6000);
}
//
// Add to the toast list.
//
setTimeout (function() {
f1 ();
ca[0].style.display = 'block';
}, (alerts.childNodes.length - 1) * 1234);
ca[0].style.display = 'none';
alerts.insertBefore (ca[0], alerts.firstChild);
}
//
// Welcome toast was here.
//
//
// Get the default value
//
var dfv = document.msgform.message.defaultValue;
//
// Used with range
//
var ranger = document.createElement('div');
document.body.appendChild(ranger);
//
// XMLHttpRequest
//
function _xh() {
return new XMLHttpRequest();
}
var timrr = 0;
//
// Parses the HTML into objects.
//
function _parse(x, y) {
var r = document.createRange();
r.selectNode (ranger);
var d = r.createContextualFragment(x);
//window.console.dirxml (d);
var i;
var e = {};
for (i = 0; i < d.childNodes.length; i ++) {
if (typeof e[d.childNodes[i].nodeName] == 'undefined')
e[d.childNodes[i].nodeName] = [];
e[d.childNodes[i].nodeName][e[d.childNodes[i].nodeName].length] = d.childNodes[i];
}
return e;
}
//
// Parse #2 - parse the data recieved from _parse.
//
var oldctl = {};
var firsttime = 1;
function _parse2(e) {
var oldimsrc = document.images["rfswitch"].src;
document.getElementsByTagName('table')[0].innerHTML = e['TABLE'][0].innerHTML;
document.getElementsByTagName('table')[5].innerHTML = e['TABLE'][1].innerHTML;
document.msgform.validshout.value = e['FORM'][0].getElementsByTagName('input')[2].value;
document.images["rfswitch"].src = oldimsrc;
var newctl = {};
var i;
var nl = e['TABLE'][1].getElementsByTagName('a');
var par = nl[0].parentNode.parentNode;
for (i = 1; i < nl.length; i ++) {
newctl[nl[i].innerHTML] = 1;
nl[i].style.textDecoration = 'none';
}
if (!firsttime) {
for (i in oldctl) {
if (typeof newctl[i] == 'undefined') {
if (oldctl[i] == 1) {
newctl[i] = 2;
} else if (oldctl[i][1] == 2) {
_alert (i + ' left the shoutbox.');
}
}
}
for (i in newctl) {
if (typeof oldctl[i] == 'undefined') {
_alert (i + ' entered the shoutbox.');
}
}
}
oldctl = newctl;
firsttime = 0;
}
//
// This function re-refreshes.
//
function _refresh() {
var x = _xh();
//
// Status handling.
//
x.onreadystatechange = function() {
if (x.readyState == 4 && x.status == 200) {
try {
_parse2 (_parse(x.responseText));
} catch (er) {
_alert ('Could not parse the response text.');
}
}
};
//
// Open a request.
//
x.open ('get', location.href.indexOf('?') == -1 ? location.href + '?dtuniqid=' + Math.random() : location.href + '&dtuniqid=' + Math.random(), true);
x.overrideMimeType ('text/html; charset=iso-8859-1');
x.send ('');
//
// Queue a timer.
//
timrr = setTimeout (_refresh, 11111);
}
//
// Focus the message entry
//
setTimeout (function() {
document.msgform.message.focus ();
}, 100);
//
// Edit the message box.
//
document.msgform.message.style.width = '20%';
document.msgform.message.style.fontSize = '13px';
//
// Quick Texts!
//
var qm = document.createElement('div');
qm.style.position = 'fixed';
qm.style.bottom = '6em';
qm.style.width = '24em';
qm.style.left = '2em';
document.body.style.paddingBottom = '0em';
qm.style.padding = '1px';
qm.className = 'tborder';
document.body.appendChild (qm);
var qmph = document.createElement('div');
qm.appendChild (qmph);
var qmt = document.createElement('div');
qmt.style.marginBottom = '1px';
qmt.style.padding = '5px';
qmt.style.fontWeight = 'bold';
qmt.appendChild (document.createTextNode('Quick Texts (hover me)'));
qmt.className = 'trow1';
qmph.appendChild (qmt);
var qmbc = document.createElement('div');
qmbc.style.height = '0';
qmbc.style.overflow = 'hidden';
var qmb = document.createElement('div');
qmb.style.padding = '4px';
qmb.className = 'trow2';
qmph.appendChild (qmbc);
qmbc.appendChild (qmb);
var qmd = {};
//
// Show the quick messages list on mouse over.
//
var qmdat = 0;
var qmto = 0;
var qmtim = 0;
function qmfollow() {
if (qmdat < qmto) {
qmdat += 0.07;
if (qmdat >= qmto) {
qmdat = qmto;
qmbc.style.height = '';
} else {
qmbc.style.height = Math.round(_smooth(qmdat) * qmb.offsetHeight) + 'px';
qmtim = setTimeout(qmfollow, 24);
}
} else if (qmdat > qmto) {
qmdat -= 0.07;
if (qmdat < qmto) qmdat = qmto;
qmbc.style.height = Math.round(_smooth(qmdat) * qmb.offsetHeight) + 'px';
qmtim = setTimeout(qmfollow, 24);
}
}
qmph.onmouseover = function() {
qmto = 1;
clearTimeout (qmtim);
qmtim = setTimeout(qmfollow, 16);
}
qmph.onmouseout = function() {
qmto = 0;
clearTimeout (qmtim);
qmtim = setTimeout(qmfollow, 16);
}
//
// Saves the quick message list.
//
function qmSave() {
function qmEscape(z) {
return z.replace(/(\\|')/g, '\\$1');
}
var o = '';
for (i in qmd) {
if (o != '')
o += ',';
o += '[\'' + (qmEscape(i)) + '\',\'' + (qmEscape(qmd[i])) + '\']';
}
GM_setValue('quicks', escape('[' + o + ']'));
qmUpdate ();
}
//
// Options!
//
var opph = document.createElement('div');
qm.appendChild (opph);
var opt = document.createElement('div');
opt.style.marginBottom = '1px';
opt.style.padding = '5px';
opt.style.fontWeight = 'bold';
opt.appendChild (document.createTextNode('Options (hover me)'));
opt.className = 'trow1';
opph.appendChild (opt);
var opbc = document.createElement('div');
opbc.style.height = '0';
opbc.style.overflow = 'hidden';
var opb = document.createElement('div');
opb.style.padding = '4px';
opb.className = 'trow2';
opph.appendChild (opbc);
opbc.appendChild (opb);
var opdat = 0;
var opto = 0;
var optim = 0;
function opfollow() {
if (opdat < opto) {
opdat += 0.07;
if (opdat >= opto) {
opdat = opto;
opbc.style.height = '';
} else {
opbc.style.height = Math.round(_smooth(opdat) * opb.offsetHeight) + 'px';
optim = setTimeout(opfollow, 24);
}
} else if (opdat > opto) {
opdat -= 0.07;
if (opdat < opto) opdat = opto;
opbc.style.height = Math.round(_smooth(opdat) * opb.offsetHeight) + 'px';
optim = setTimeout(opfollow, 24);
}
}
opph.onmouseover = function() {
opto = 1;
clearTimeout (optim);
optim = setTimeout(opfollow, 16);
}
opph.onmouseout = function() {
opto = 0;
clearTimeout (optim);
optim = setTimeout(opfollow, 16);
}
var opts = {};
function optAddVal(vald, labl, df) {
var dc = document.createElement('div');
dc.appendChild (document.createTextNode(labl));
dc.style.padding = '0.4em 0';
var ah = document.createElement('a');
function updLook() {
ah.innerHTML = opts[vald] ? '[ON]' : '[off]';
}
ah.href = '#';
ah.onclick = function() {
var nv = GM_getValue(vald, df) ^ 1;
opts[vald] = nv;
GM_setValue(vald, nv);
updLook ();
return false;
};
dc.appendChild (ah);
opts[vald] = GM_getValue(vald, df);
updLook ();
opb.appendChild (dc);
}
optAddVal ('dtcompat', '* Enable text sending using XMLHttpRequest (-dt-\'s shoutbox cc script will fail if this is set to on) ', 1);
optAddVal ('toasts', '* Enable toasts (disable this to get rid of stupid, annoying toasts) ', 1);
optAddVal ('quicktexts', '* Parse quick texts (you still can edit the list of quick texts when this is not turned on) ', 1);
var old_alert = _alert;
_alert = function(x) {
if (opts.toasts) {
old_alert (x);
}
}
//
// Saves the quick message list.
//
function qmSave() {
function qmEscape(z) {
return z.replace(/(\\|')/g, '\\$1');
}
var o = '';
for (i in qmd) {
if (o != '')
o += ',';
o += '[\'' + (qmEscape(i)) + '\',\'' + (qmEscape(qmd[i])) + '\']';
}
GM_setValue('quicks', escape('[' + o + ']'));
qmUpdate ();
}
//
// Update the quick message list.
//
function qmUpdate() {
//
// Reset the body and the data.
//
qmb.innerHTML = '';
qmd = {};
//
// Add one to list.
//
function a2l(x) {
qmd[x[0]] = x[1];
var cm = document.createElement('div');
var cl = [document.createElement('a'), document.createElement('a'), document.createElement('a')];
cl[0].href = '#';
cl[1].href = '#';
cl[2].href = '#';
cl[0].appendChild (document.createTextNode('/' + x[0]));
cl[1].appendChild (document.createTextNode('Edit'));
cl[2].appendChild (document.createTextNode('Delete'));
cl[0].onclick = function() {
document.msgform.message.value = '/' + x[0];
document.msgform.message.focus ();
return false;
};
cl[1].onclick = function() {
var yzz = prompt('Please enter the text to bind with this command.', qmd[x[0]]);
if (!yzz || yzz == '') {
alert ('Error: No text specified.');
return false;
}
qmd[x[0]] = yzz;
qmSave ();
return false;
};
cl[2].onclick = function() {
delete qmd[x[0]];
qmSave ();
return false;
};
cm.appendChild (cl[0]);
cm.appendChild (document.createTextNode(' ('));
cm.appendChild (cl[1]);
cm.appendChild (document.createTextNode(' / '));
cm.appendChild (cl[2]);
cm.appendChild (document.createTextNode(')'));
qmb.appendChild (cm);
}
//
// Get the data.
//
var gmd = eval(unescape(GM_getValue('quicks', escape('[]'))));
//
// Make the list.
//
if (gmd.length == 0) {
var nm = document.createElement('div');
nm.style.textAlign = 'center';
nm.appendChild (document.createTextNode('No quick messages.'));
qmb.appendChild (nm);
} else {
var i;
for (i = 0; i < gmd.length; i ++) {
a2l (gmd[i]);
}
}
//
// Add new one
//
var am = document.createElement('div');
am.style.textAlign = 'center';
am.style.paddingTop = '1em';
var al = document.createElement('a');
al.href = '#';
al.appendChild (document.createTextNode('(Create one!)'));
al.onclick = function() {
var xzz = prompt('Please enter the command.\r\n\r\nFor example, if you input "ttyl", you can access it by typing "/ttyl" in the message entry.', '');
if (!xzz || xzz == '') {
alert ('Error: No commands specified.');
return false;
}
var yzz = prompt('Please enter the text to bind with this command.', '');
if (!yzz || yzz == '') {
alert ('Error: No text specified.');
return false;
}
qmd[xzz] = yzz;
qmSave ();
return false;
};
am.appendChild (al);
qmb.appendChild (am);
}
qmUpdate ();
//
// Show the error
//
function showError() {
var $z = GM_getValue ('error', 0);
$z ++;
GM_setValue ('error', $z);
_alert ('Error: An error has occured while sending the message.
You may talk too fast, you may quoted a non-existant shout or a server error has occured. Please try again.
You made ' + $z + ' error' + ($z == 1 ? '' : 's') + ' to date.');
return $z;
}
//
// Edit the sending behavior.
//
document.msgform.onsubmit = function() {
//
// Check message and create XMLHttpRequest.
//
if (document.msgform.message.value == dfv || document.msgform.message.value.match(/^\s*$/))
return false;
//
// Command key?
//
if (opts.quicktexts) {
if (document.msgform.message.value.charAt(0) == '/') {
var crp = document.msgform.message.value.substr(1);
var crp2 = (crp.indexOf(' ') == -1 ? crp : crp.substr(0, crp.indexOf(' ')));
var crp3 = (crp.indexOf(' ') == -1 ? '' : crp.substr(crp.indexOf(' ') + 1));
//
// Is that quick text exists?
//
if (typeof qmd[crp] != 'undefined') {
document.msgform.message.value = qmd[crp];
//
// Command with arguments?
//
} else if (typeof qmd[crp2] != 'undefined') {
document.msgform.message.value = qmd[crp2] + ' ' + crp3;
//
// Simulate an error?
//
} else if (document.msgform.message.value == '/errstats') {
document.msgform.message.value = 'My error-penis is ' + GM_getValue('error', 0) + ' cm long!! :pound:'; // MeEtc
//
// Normal commands?
//
} else if (document.msgform.message.value.match(/\/(ls\d*|shout|theme|help)/)) {
//
// Prevent AJAX submit by using some system commands.
//
return true;
}
}
} else {
if (document.msgform.message.value.match(/\/(ls\d*|shout|theme|help)/)) {
//
// Prevent AJAX submit by using some system commands.
//
return true;
}
}
//
// -dt-'s compatibility hack.
//
if (!opts['dtcompat'])
return unsafeWindow.checkName ();
var x = _xh();
//
// Status handling.
//
x.onreadystatechange = function() {
if (x.readyState == 4 && x.status == 200) {
try {
_parse2 (_parse(x.responseText));
document.msgform.message.value = '';
} catch (er) {
showError();
}
document.msgform.message.disabled = false;
setTimeout (function() {
document.msgform.message.focus ();
}, 100);
}
};
//
// Open a request.
//
x.open ('post', 'shoutbox.php?dtuniqid=' + Math.random(), true);
x.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded; charset=iso-8859-1');
x.overrideMimeType ('text/html; charset=iso-8859-1');
x.send ('rf=' + encodeURIComponent(document.msgform.rf.value) + '&action=send&validshout=' + encodeURIComponent(document.msgform.validshout.value) + '&message=' + escape(document.msgform.message.value).replace(/%u([a-f0-9]+)/gi, function (x, y) {
return '' + parseInt(y, 16) + ';';
}));
document.msgform.blur ();
document.msgform.message.disabled = true;
//
// Just return false
//
return false;
};
//
// Refreshing hack.
//
if (document.images) {
var loadon = new Image(15, 16);
var loadoff = new Image(15, 16);
loadon.src = "images/refreshon.gif";
loadoff.src = "images/refreshoff.gif";
}
//
// Don't do the old refresh.
//
window.sbRefresh = function() {}; // This function should do NOTHING!
//
// Use the new one!
//
//window.rfSwitch = function() {
// // New switching method.
// if (document.images["rfswitch"].src == loadon.src) {
// document.images["rfswitch"].src = loadoff.src;
// document.msgform.rf.value = '0';
// _alert ('Auto-reload turned off.');
// clearTimeout (timrr);
// } else {
// document.images["rfswitch"].src = loadon.src;
// document.msgform.rf.value = '1';
// clearTimeout (timrr);
// _alert ('Auto-reload turned on.');
// setTimeout (_refresh, 6000);
// }
//};
//
// These functions get the top and left position of an element.
// Modified from DtJS. http://dttvb.yi.org/dtjs/
//
function gp(el) {
var tmp = [el.offsetLeft, el.offsetTop];
el = el.offsetParent;
while (el) {
tmp[0] += el.offsetLeft;
tmp[1] += el.offsetTop;
el = el.offsetParent;
}
return tmp;
}
//
// User Menu Construction
//
var lmdat = [0, 0];
var lm = document.createElement('div');
lm.style.position = 'absolute';
lm.style.padding = '1px';
lm.style.width = '15em';
lm.style.paddingTop = '0';
lm.className = 'tborder';
lm.style.display = 'none';
document.body.appendChild (lm);
var lmid = 0;
var lmtid = 0;
//
// This function hides the menu and returns true.
//
function lmh() {
clearTimeout (lmtid);
setTimeout (function() {
lm.style.display = 'none';
}, 100);
return true;
}
//
// This function makes it easy to add more menu.
//
function lmc(tx, fu) {
var ce = document.createElement('div');
ce.style.padding = '3px';
ce.style.marginTop = '1px';
ce.style.cursor = 'pointer';
ce.appendChild (document.createTextNode(tx));
ce.onclick = fu;
ce.onmouseover = function() {
ce.style.fontWeight = 'bold';
};
ce.onmouseout = function() {
ce.style.fontWeight = '';
};
ce.className = 'trow' + ((lmid++ % 2) + 1);
lm.appendChild (ce);
return ce;
}
//
// Now add some menus using that function.
//
lmc ('Quote this shout', function() {
var hd = '/quote ' + lmdat[0] + ' ';
document.msgform.message.value = document.msgform.message.value.replace (new RegExp('^\\s*/quote\\s+\\d+\\s*', 'i'), '');
if (document.msgform.message.value == dfv || document.msgform.message.value.match(/^\s*$/))
document.msgform.message.value = '';
document.msgform.message.value = hd + document.msgform.message.value;
document.msgform.message.focus ();
setTimeout (function() {
document.msgform.message.selectionStart = hd.length;
document.msgform.message.selectionEnd = document.msgform.message.value.length;
}, 100);
});
lmc ('View profile', function() {
window.open ('member.php?uid=' + lmdat[1], '_blank');
});
lmc ('Send private message', function() {
window.open ('private.php?action=send&uid=' + lmdat[1], '_blank');
});
//
// Mouseclicking on a user
//
(function() {
//
// This function occurs on mouse down.
//
function oc(e) {
//
// Cancel the default behavior?
//
var ex = 0;
var idm;
var idu;
//
// Get the target.
//
var ce = e.target;
//
// Look for the link.
//
while (ce.nodeName != 'BODY' && ce.nodeName != 'HTML') {
if (ce.nodeName == 'A')
break;
ce = ce.parentNode;
}
//
// Cancel if link not found.
//
if (ce.nodeName != 'A')
return lmh();
//
// Cancel if not a member link.
//
if (ce.title != 'View profile')
return lmh();
//
// Cancel if the ID not specified.
//
if (!(idu = ce.href.match(/uid=(\d+)/)))
return lmh();
//
// Cancel if no shout ID is specified.
//
if (!(idm = ce.parentNode.parentNode.title.match(/ID (\d+)/))) if (!(idm = ce.parentNode.parentNode.parentNode.title.match(/ID (\d+)/)))
return lmh();
//
// OK, now prevent standard things to happen.
//
ex = 1;
//
// Set the values for the menu..
//
lmdat = [idm[1], idu[1]];
//
// Get the position of the link.
//
var geo = gp(ce);
//
// And place the popup menu..
//
lm.style.left = geo[0] + 'px';
lm.style.top = (geo[1] + ce.offsetHeight) + 'px';
//
// Display the menu!
//
lm.style.display = 'block';
//
// This do the trick! ;)
//
if (ex) {
e.stopPropagation ();
e.preventDefault ();
}
}
document.addEventListener ('click', oc, false);
})();
//
// Queue for update if previously.
//
if (document.images["rfswitch"].src == loadon.src) {
timrr = setTimeout (_refresh, 8000);
} else {
_alert ('Note: Auto-reload feature is not turned on. Click the auto-reload button at the bottom-left corner of this page to turn it on.');
}
})(typeof unsafeWindow == 'undefined' ? window : unsafeWindow);