/*
(c) Mike Sugarbaker, misuba@gmail.com
Based on Instant Textile,
(c) Phil Wilson, greasemonkey@philwilson.org
in turn based on Textileable by Roberto De Almeida
(http://dealmeida.net/en/Projects/PyTextile/greasemonkey_and_textile.html)
and the JS Textile function by Jeff Minard (http://www.creatimation.net/textile.js)
GPL.
This (Mike's) version is the epitome of an ugly hack. Please forgive me.
This is version 0.1 of Lingua Franca.
*/
// ==UserScript==
// @name Lingua Franca
// @namespace http://www.gibberish.com/hacks/gm/
// @description Translate a textarea from Textile to HTML, BBCode, or MediaWiki markup
// @include http://*
// @include https://*
// ==/UserScript==
(function() {
function init()
{
var textareas = window._content.document.getElementsByTagName("textarea");
var i;
var id;
var button;
for (i = 0; i < textareas.length; i++)
{
// Get id.
id = textareas[i].getAttribute("id");
if (!id)
{
id = "__textarea_" + i;
textareas[i].setAttribute("id", id);
}
themenu = document.createElementNS("http://www.w3.org/1999/xhtml", "select");
defaulty = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
defaulty.setAttribute("value", "");
defaulty.appendChild(document.createTextNode('Textile to:'));
themenu.appendChild(defaulty);
htmlguy = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
htmlguy.setAttribute("value", "H");
htmlguy.appendChild(document.createTextNode('HTML'));
themenu.appendChild(htmlguy);
bbguy = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
bbguy.setAttribute("value", "B");
bbguy.appendChild(document.createTextNode('BBCode'));
themenu.appendChild(bbguy);
mediaguy = document.createElementNS("http://www.w3.org/1999/xhtml", "option");
mediaguy.setAttribute("value", "W");
mediaguy.appendChild(document.createTextNode('MediaWiki'));
themenu.appendChild(mediaguy);
themenu.setAttribute("id", '__linguaf_' + id);
themenu.onchange = doTextile;
textareas[i].parentNode.insertBefore(themenu, textareas[i].nextSibling);
}
}
function doTextile()
{
var head = window._content.document.getElementsByTagName("head");
var id = this.id.substring(10, this.id.length);
var dest = this.options[this.selectedIndex].value;
var textarea = window._content.document.getElementById(id);
//alert("dest = " + dest);
textarea.value = textile(textarea.value,dest);
this.selectedIndex = 0;
}
function textile(s,dest)
{
var r = s;
// quick tags first
qtags = [['\\*', 'strong', 'b', '\'\'\''],
['\\?\\?', 'cite', 'quote', ':'],
['\\+', 'ins', '', ''], //fixed
['~', 'sub', '', 'sub'],
['\\^', 'sup', '', 'sup'], // me
['@', 'code', 'code', 'code']];
for (var i=0;i<qtags.length;i++) {
ttag = qtags[i][0];
htag = qtags[i][1];
btag = qtags[i][2];
wtag = qtags[i][3];
re = new RegExp(ttag+'\\b(.+?)\\b'+ttag,'g');
if (dest == "H")
{
r = r.replace(re,'<'+htag+'>'+'$1'+'</'+htag+'>');
}
else if (dest == "B")
{
if (btag != '')
{
r = r.replace(re,'['+btag+']'+'$1'+'[/'+btag+']');
}
else
{
r = r.replace(re,'$1');
}
}
else if (dest == "W")
{
if (wtag == ":")
{
// just needs not to go on the end
r = r.replace(re,wtag+'$1');
}
else if (wtag == "sub" || wtag == "sup" || wtag == "code")
{
// use HTML for these
r = r.replace(re,'<'+wtag+'>'+'$1'+'</'+wtag+'>');
}
else if (wtag != "")
{
// uh, i guess we're down to bold
r = r.replace(re,wtag+'$1'+wtag);
}
}
}
// underscores count as part of a word, so do them separately
re = new RegExp('\\b_(.+?)_\\b','g');
if (dest == "H")
{
r = r.replace(re,'<em>$1</em>');
}
else if (dest == "B")
{
r = r.replace(re,'[i]$1[/i]');
}
else if (dest == "W")
{
r = r.replace(re,'\'\'$1\'\'');
}
//jeff: so do dashes
re = new RegExp('[\s\n]-(.+?)-[\s\n]','g');
if (dest == "H")
{
r = r.replace(re,'<del>$1</del>');
}
else
{
r = r.replace(re,'-$1-');
}
// links
re = new RegExp('"\\b(.+?)\\(\\b(.+?)\\b\\)":([^\\s]+)','g');
if (dest == "H")
{
r = r.replace(re,'<a href="$3" title="$2">$1</a>');
}
else if (dest == "B")
{
r = r.replace(re,'$1: [url]$3[/url]');
}
else if (dest == "W")
{
r = r.replace(re,'[$3 $1]');
}
re = new RegExp('"\\b(.+?)\\b":([^\\s]+)','g');
if (dest == "H")
{
r = r.replace(re,'<a href="$2">$1</a>');
}
else if (dest == "B")
{
r = r.replace(re,'$1: [url]$2[/url]');
}
else if (dest == "W")
{
r = r.replace(re,'[$2 $1]');
}
// images
re = new RegExp('!\\b(.+?)\\(\\b(.+?)\\b\\)!','g');
if (dest == "H")
{
r = r.replace(re,'<img src="$1" alt="$2" />');
}
else if (dest == "B")
{
r = r.replace(re,'[img]$1[/img]');
}
else if (dest == "W")
{
r = r.replace(re,'[[Image:$1|$2]]');
}
re = new RegExp('!\\b(.+?)\\b!','g');
if (dest == "H")
{
r = r.replace(re,'<img src="$1" />');
}
else if (dest == "B")
{
r = r.replace(re,'[img]$1[/img]');
}
else if (dest == "W")
{
r = r.replace(re,'[[Image:$1]]');
}
// block level formatting
// Jeff's hack to show single line breaks as they should.
// insert breaks - but you get some....stupid ones
re = new RegExp('(.*)\n([^#\*\n].*)','g');
if (dest != "B") r = r.replace(re,'$1<br />$2');
// remove the stupid breaks.
re = new RegExp('\n<br />','g');
if (dest != "B") r = r.replace(re,'\n');
lines = r.split('\n');
nr = '';
for (var i=0;i<lines.length;i++)
{
line = lines[i].replace(/\s*$/,'');
changed = 0;
if (line.search(/^\s*bq\.\s+/) != -1)
{
if (dest == "H")
{
line = line.replace(/^\s*bq\.\s+/,'\t<blockquote>')+'</blockquote>';
}
else if (dest == "B")
{
line = line.replace(/^\s*bq\.\s+/,'\t[quote]')+'[/quote]';
}
else if (dest == "W")
{
line = line.replace(/^\s*bq\.\s+/,'\:');
}
changed = 1;
}
// jeff adds h#.
if (line.search(/^\s*h[1-6]\.\s+/) != -1)
{
re = new RegExp('h([1-6])\.(.+)','g');
if (dest == "H")
{
line = line.replace(re,'<h$1>$2</h$1>');
}
else if (dest == "B")
{
line = line.replace(re,'$2');
}
else
{
thematch = line.match(re);
whatlevel = parseInt(thematch[1]);
if (whatlevel > 3) whatlevel = 3;
theguys = "";
while (whatlevel)
{
theguys += "="; whatlevel--;
} // must be a better way to do all this...
line = line.replace(re,'='+theguys+' $2 ='+theguys);
}
changed = 1;
}
if (dest != "W")
{
if (line.search(/^\s*\*\s+/) != -1)
{
line = line.replace(/^\s*\*\s+/,'\t<liu>') + '</liu>';
changed = 1;
} // * for bullet list; make up an liu tag to be fixed later
if (line.search(/^\s*#\s+/) != -1)
{
line = line.replace(/^\s*#\s+/,'\t<lio>') + '</lio>';
changed = 1;
} // # for numeric list; make up an lio tag to be fixed later
if (!changed && (line.replace(/\s/g,'').length > 0))
{
if (dest == "H") line = '<p>'+line+'</p>';
}
lines[i] = line + '\n';
}
}
// Second pass to do lists
if (dest != "W")
{
inlist = 0;
listtype = '';
for (var i = 0; i < lines.length; i++)
{
line = lines[i];
if (inlist && listtype == 'ul' && !line.match(/^\t<liu/))
{
line = (dest == "B" ? '[/list]\n' : '</ul>\n') + line;
inlist = 0;
}
if (inlist && listtype == 'ol' && !line.match(/^\t<lio/))
{
line = (dest == "B" ? '[/list]\n' : '</ol>\n') + line;
inlist = 0;
}
if (!inlist && line.match(/^\t<liu/))
{
line = (dest == "B" ? '[list]' : '<ul>') + line;
inlist = 1;
listtype = 'ul';
}
if (!inlist && line.match(/^\t<lio/))
{
line = (dest == "B" ? '[list]' : '<ol>') + line;
inlist = 1;
listtype = 'ol';
}
lines[i] = line;
}
if (inlist) // hey, we shouldn't still be in it, we're done!!
{
if (listtype == 'ol')
{
lines[lines.length] = (dest == "B" ? '[/list]\n' : '</ol>\n');
}
else
{
lines[lines.length] = (dest == "B" ? '[/list]\n' : '</ul>\n');
}
}
}
r = lines.join('\n');
// jeff added : will correctly replace <li(o|u)> AND </li(o|u)>
if (dest == "H")
{
r = r.replace(/<(\/?)li[o|u]>/g,'<'+'$1'+'li>');
}
else if (dest == "B")
{
r = r.replace(/<li[o|u]>/g,'[*]');
r = r.replace(/<\/li[o|u]>/g,'');
}
return r;
}
init();
})();