// ==UserScript==
// @name BBCode in Text
// @namespace http://mailerdaemon.home.comcast.net/
// @include http://forums.secondlife.com/*
// @version 0.2
// @description Adds BBCode support to an antique forum.
// ==/UserScript==
//configures how urls are displayed.
const ERRORS = false;
const url_start = 40;
const url_end = 25;
const url_len = url_start + url_end;
GM_addStyle = function(css){
style = document.createElement("style");
style.type = "text/css";
style.innerHTML = css;
document.getElementsByTagName('head')[0].appendChild(style);
};
//GM_log = function(text){};
GM_addStyle(".GMCode {border: 1px inset ; margin: 0px; padding: 6px; overflow: auto; width: auto; height: auto; text-align: left; font-family:monospace; } .GMCode br {display:none;}")
const chars = String.fromCharCode(38, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 248, 249, 250, 251, 252, 253, 254, 255, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, 220, 221, 222, 8364, 34, 223, 60, 62, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 8364, 8226, 160, 161);
const entities = new Array ('amp','agrave','aacute','acirc','atilde','auml','aring',
'aelig','ccedil','egrave','eacute','ecirc','euml','igrave',
'iacute','icirc','iuml','eth','ntilde','ograve','oacute',
'ocirc','otilde','ouml','oslash','ugrave','uacute','ucirc',
'uuml','yacute','thorn','yuml','Agrave','Aacute','Acirc',
'Atilde','Auml','Aring','AElig','Ccedil','Egrave','Eacute',
'Ecirc','Euml','Igrave','Iacute','Icirc','Iuml','ETH','Ntilde',
'Ograve','Oacute','Ocirc','Otilde','Ouml','Oslash','Ugrave',
'Uacute','Ucirc','Uuml','Yacute','THORN','euro','quot','szlig',
'lt','gt','cent','pound','curren','yen','brvbar','sect','uml',
'copy','ordf','laquo','not','shy','reg','macr','deg','plusmn',
'sup2','sup3','acute','micro','para','middot','cedil','sup1',
'ordm','raquo','frac14','frac12','frac34',
'euro','bull','nbsp','iexcl');
if(!String.prototype.trim) String.prototype.trim = function() { return this.replace(/^\s*/,'').replace(/\s*$/, ''); }
if(!String.prototype.htmlUnescape) String.prototype.htmlUnescape = function(){
var r = /&(#[0-9]*|[A-Za-z0-9]*);/;
var i = this;
var o = ""
var p;
do
{
if(!(p = r.exec(i)))
return o + i;
if(p.index > 0)
o += i.slice(0, p.index);
i = i.slice(p.index + p[0].length);
if(p[1].charAt(0) == '#')
o += String.fromCharCode(p[1].slice(1))
else
{
var w = entities.indexOf(p[1]);
o += (w != -1)?chars[w]:("&"+p[1]+";");
}
}while(1);
}
if(!String.prototype.htmlEscape) String.prototype.htmlEscape = function(){
var i = this;
for(var k = 0;k < chars.length; k++)
i = i.replace(new RegExp(chars[k], "g"), "&"+entities[k]+";");
return i;
}
if(!Array.prototype.insert) Array.prototype.insert = function( i, v ) {
var b = this.splice( i );
this.push(v);
return this.concat( b );
};
var count = 0;
var base = document.URL;
count = base.indexOf("?");
if(count >= 0) base = base.substring(0, count);
base = base.substring(0, base.lastIndexOf("/")+1);
var pattern = /(?:\[(?:[\/\\]?)(url|email|thread|post|indent|code|php|font|size|color|b|u|i|right|left|center|highlight|list|img|[*])(?:|=[^\]]*)\]|((?:https?|mms|rstp|secondlife|ftp|mailto):[^\s\[]*)|((?:[a-z0-9.\-]{3,}\.(?:com|net|org|co\.uk)|(?:(?:0x[0-9a-f]{1,8}|[0-9]{1,3})\.){3}(?:0x[0-9a-f]{1,8}|[0-9]{1,3}))(?:(?:\:[0-9]+|)\/[^\s\[]*|(?=[^0-9a-z\-.]|$))))/im;
var split_pattern = /\[([\/\\]?)([^=\]]*?)(?:\s*=\s*"?([^\]"]*)"?\s*|)\]/im;
count = 0;
var calls = 100000;//stops a race condition that doesn't happen anymore.
start();
function start()
{
var i, j, divs;
var xpath = "//td[@class='thead']/../../tr[2]/td[position()=2 and @class='alt1']"+"|"+//thread
"//tr[contains(td, 'Preview')]/td[@class='tcat']/../../tr[2]"+"|"+//edit - preview
"//td[@class='thead']/../../tr/td[position()=2 and @class='alt2']/..";//topic review
var res = document.evaluate(xpath, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (i = 0; divs = res.snapshotItem(i); ++i) {
var t = divs.childNodes.length;
// GM_log(divs.nodeName + "\nChild Count = " + t+ "\n"+divs.innerHTML);
for (t=0; t<divs.childNodes.length; t++){
parse(divs.childNodes[t]);
}
}
if(i > 0 || count > 0)
{
// GM_log(document.URL.concat("\nHad "+count+" broken tag"+((count != 1)?"s":"") + " in "+i +" messages. (debug = " + calls + " )"));
// GM_log(calls);
}
}
function parse(element, only, nourl)
{
var table = new Array(0);
var urls = new Array(0);
var badurls = new Array(0);
var t = element.firstChild;
for (;t != null; t = t.nextSibling){
if(--calls < 0)
return;
// GM_log("nodeName = "+t.nodeName+"\nnodeType = "+t.nodeType );
if(t.nodeType == 3)
{
var nodes = pattern;
var p = pattern.exec(t.nodeValue);
if(p != null)
{
// GM_log(p.length+"\n"+p[0]+"\n"+p[1]+"\n"+p[2]+"\n"+p[3]);
// if(p[1] != null || p[2] != null || p[3] != null)
{
var m=document.createTextNode(p[0]);
var n=document.createTextNode(t.nodeValue.substring(p.index + p[0].length));
t.nodeValue = t.nodeValue.substring(0,p.index);
insertAfter(m, t);
insertAfter(n, m);
++count;
}
if(p[1] != null)
table.push(m);
else if(p[2] != null && !nourl)
urls.push(m);
else if(p[3] != null && !nourl)
badurls.push(m);
t = t.nextSibling;
}
}
else if(t.nodeType == 1)
{
if(t.nodeName != "TEXTAREA" && t.nodeName != "INPUT" && t.nodeName != "PRE")
parse(t, only, nourl);
}
// else
// GM_log("nodeName = "+t.nodeName+"\nnodeType = "+t.nodeType );
}
// if(element.innerHTML)
// GM_log("Count = "+table.length+"\n"+table+"\n" + element.innerHTML);
if(table.length > 0)
tag(table, only);
var i;
for(i = 0; i < urls.length; i++)
{
var t = urls[i];
var v = t;
while(v = v.parentNode)
{
if(v.nodeName == "A" || v.nodeName == "PRE")//keep it honest
break;
}
if(v == null && t.parentNode)
{
// GM_log(t.nodeValue);
var m=document.createElement("A");
m.href = (m.innerHTML = t.nodeValue).htmlUnescape();
if(m.innerHTML.length > url_len)
m.innerHTML = m.innerHTML.slice(0,url_start)+"..."+m.innerHTML.slice(m.innerHTML.length-url_end);
t.parentNode.replaceChild(m, t);
}
}
for(i = 0; i < badurls.length; i++)
{
var t = badurls[i];
var v = t;
while(v = v.parentNode)
{
if(v.nodeName == "A" || v.nodeName == "PRE")//keep it honest
break;
}
if(v == null && t.parentNode)
{
// GM_log(t.nodeValue);
var m=document.createElement("A");
m.href = "http://"+(m.innerHTML = t.nodeValue).htmlUnescape();
if(m.innerHTML.length > url_len)
m.innerHTML = m.innerHTML.slice(0,url_start)+"..."+m.innerHTML.slice(m.innerHTML.length-url_end);
t.parentNode.replaceChild(m, t);
}
}
badurls = urls = table = null;
}
/*
function juggle(table)
{
var t;
var map = new Object();
for (t=0; t<table.length; t++){
var type = split_pattern.exec(table[t].nodeValue);
if(type[1] == "")
{
if(map[type[2].toLowerCase()])
++map[type[2].toLowerCase()];
else
map[type[2].toLowerCase()] = 1;
}
else
{
if(map[type[2].toLowerCase()])
--map[type[2].toLowerCase()];
else
map[type[2].toLowerCase()] = -1;
}
}
map = null;
return table;
}
*/
function log(table, sep, msg_a, msg_b)
{
var kw = "";
Array.forEach(table, function(item) {if(kw !="") kw +=sep;kw += item.nodeValue;});
// GM_log(msg_a+kw+msg_b);
}
function oc(a)
{
if(a && a.length)
{
var o = {};
for(var i=0;i<a.length;i++)
{
o[a[i]]='';
}
return o;
}
return null;
}
function tag(table, only)
{
// log(table, "\n", "tags = [\n","\n]");
var t;
var z = oc(only);
for (t=0; t<table.length; t++){
if(--calls < 0)
return;
var type = split_pattern.exec(table[t].nodeValue);
if(type)//make sure it is infact a valid tag.
{
if(type[1] == "")
{
var c = 1;
var m = t;
var r = type[2].toLowerCase();
var dtype;
var k = table.length - 1;
if(z && !(r in z));//silently ignore missing tags
else if(r == "*")
{
while(t < k && c != 0)
{
if(--calls < 0)
return;
dtype = split_pattern.exec(table[++t].nodeValue);
if(dtype)
{
var dt = dtype[2].toLowerCase();
if(dt == "list")
{
if(dtype[1] == "")
c++;
else
c--;
}
else if(dt == "*" && c == 1)
c = 0;
}
}
if(c > 0)
t++;
}
else
{
while(t < k && c != 0)
{
if(--calls < 0)
return;
dtype = split_pattern.exec(table[++t].nodeValue);
if(dtype)
{
if(dtype[2].toLowerCase() == r)
{
if(dtype[1] == "")
c++;
else
c--;
}
}
}
if(c > 0 && (r == "url" || r =="img") && type[3] && type[3] != "")
{//some idiot didn't close thier URL/IMG tag, *rolls eyes*
// log(table, ", ", "before: " + table.length+"\n")
table.splice(t = m+1, 0, insertAfter(document.createTextNode("[/"+r+"]"), table[m]));
// log(table, ", ", "after: " + table.length+"\n")
insertAfter(document.createTextNode(type[3]), table[m]);
k+=1;//adjust the end...
type[3]="";//makes it work harder
c = 0;
}
}
// if(table.length > t)
// GM_log("( "+(t - m)+", " + c + " ) = " + type[0] +" && " + split_pattern.exec(table[t].nodeValue)[0]);
// else
// GM_log("( "+(t - m)+", " + c + " ) = " + type[0] +" && last");
if(c == 0 || r == "*")
{
var nodes = new Array(0);
var p = null;
var q = null;
if(r == "url" || r == "thread" || r == "post" || r =="email")
{
p = document.createElement("a");
var d = "";
if(type[3] != null)
d = type[3].replace(/ /g,"").htmlUnescape();
if(r == "thread")
p.href = (base + "showthread.php?t=" + d);
else if(r == "post")
p.href = (base + "showthread.php?p=" + d);
else if(d != "")
{
if(r == "email")
p.href = (d.trim().toLowerCase().indexOf("mailto:") != 0)?"mailto:"+d:d;
else if(r == "url")
{
var d1 = d.trim();
var d3 = d1.indexOf("/");
var d4 = d1.indexOf(":");
var d5 = d1.indexOf("?");
if(d4 == -1 || (d4 > d3 && d3 != -1))
{
if(d5 == -1 || d3 != -1)
p.href = "http://"+d;
else
p.href = base + d;
}
else
p.href = d;
}
}
}
else if(r == "color" || r == "size" || r == "font")
{
p = document.createElement("font");
if(r == "color")
p.color = type[3];
else if(r == "size")
p.size = type[3];
else if(r == "font")
p.face = type[3];
}
else if(r == "right" || r == "left" || r == "center")
{
p = document.createElement("div");
if(r == "right")
p.align = "right";
else if(r == "left")
p.align = "left";
else if(r == "center")
p.align = "center";
}
else if(r == "u" || r == "b" || r == "i")
{
p = document.createElement(r);
}
else if(r == "highlight")
{
p = document.createElement("span");
p.className = "highlight";
}
else if(r == "indent")
{
p = document.createElement("blockquote");
q = document.createElement("div");
p.appendChild(q);
}
else if(r == "code" || r == "php")
{
p = document.createElement("div");
p.style.margin="5px 20px 20px";
q = document.createElement("div");
q.className = "smallfont";
q.style.marginBottom="2px";
q.appendChild(document.createTextNode("Code:"));
p.appendChild(q);
q = document.createElement("pre");
q.className = "alt2 GMCode";
q.dir = "ltr";
p.appendChild(q);
}
else if(r == "list")
{//<ol type="1"><li>list item 1</li><li>list item 2</li></ol>
if(type[3] != "" && type[3] != null)
{
p = document.createElement("ol");
p.type=type[3];
}
else
{
p = document.createElement("ul");
}
}
else if(r == "img")
{
p = document.createElement("img");
q = document.createElement("span");
}
else if(r == "*")
{
p = document.createElement("li");
}
if(q == null)
q = p;
if(p != null)
{
var h = table[m];
var w = h.parentNode;
var n = h.nextSibling;
var s = null;
if(table.length > t)
s = table[t];
while(n != s && n != null)
{
y = n.nextSibling;
child = w.removeChild(n);
if(child.nodeName != "BR" || (r != "code" && r != "php"))
q.appendChild(child);
if(child.nodeName == "#text")
child.nodeValue = child.nodeValue.replace(/([\S]{50,52}) /gm,"$1");
n = y;
if(--calls < 0)
return;
}
if(s != null)
{
var rtype = split_pattern.exec(s.nodeValue);
if(rtype[1] != "" && rtype[2].toLowerCase() == r /*/&& w/**/)//catch for lists ~_~
w.removeChild(s);
}
if(r == "code" || r == "php")//keeps it from trying to parse code and php stuff.
{
//FIXME: make the replacement smarter, i'm too lazy.
q.innerHTML = q.innerHTML.replace(/\t/g," ");
//if(r != "code")//oddly enabling this test borks things, not exactly sure what causesit.
m = t;//no internal parsing
if(r == "code")//this is the wrong solutions but it works
parse(q, ["i", "b", "u", "color", "highlight", "url", "thread", "post", "email"], true);
}
else if(r == "img")
{
if("" == (p.src = q.textContent.trim().htmlUnescape()))
p = q;
m = t;//no internal parsing
}
else if((r == "url" || r == "email" || r == "post" || r == "thread") && (type[3]=="" || type[3] == null))
{
s = q.textContent.trim().replace(/ /g,"").htmlUnescape();
if(r == "email")
q.href = (s.toLowerCase().indexOf("mailto:") != 0)?"mailto:"+s:s;
else if(r == "url")
q.href = ((s+"/:").indexOf(":") > s.indexOf("/"))?"http://"+s:s;
else
p.href += s;
if(s.length > url_len)
q.innerHTML = (s.slice(0,url_start)+"..."+s.slice(s.length-url_end)).htmlEscape();
else
q.innerHTML = q.textContent;//strip the html from it, it should be unformated.
m = t;//no internal parsing
}
/*/if(w)/**/
w.replaceChild(p,h);
}
}
else //if(c > 0 && r != "*")
{
++t;
if(ERRORS)
{
// GM_log("tag error = " + type[0]);
// log(table, "\n", "tags = [\n","\n]");
}
}
if(m+1 < t)
tag(table.slice(m+1, t));
if((r == "*" && c == 0))// || (c == 1 && r != "*"))
--t;
}
else if(ERRORS)
{
// GM_log("tag error = " + type[0]);
// log(table, "\n", "tags = [\n","\n]");
}
}
}
}
function insertAfter(insert, after)
{
return after.parentNode.insertBefore(insert, after.nextSibling);
}