In this post, I will present how I wrote a standalone XUL application: it's a simple GUI translating a DNA sequence to a proteic sequence. (Wikipedia:) XUL stands for "XML User Interface Language". It's a language developed by the Mozilla project which operates in Mozilla cross-platform applications, it relies on multiple existing web standards and technologies, including CSS, JavaScript, and DOM. Such reliance makes XUL relatively easy to learn for people with a background in web-programming and design.
A XUL standalone application has not the barreers of security of the web browsers: It can open/save/write/read a file, create a database (via sqlite/mozstorage), etc...
This post was mostly written with the help of the xulrunner tutorial (https://developer.mozilla.org/en/Getting_started_with_XULRunner).
The complete source code of this application is available at: http://code.google.com/p/lindenb/source/browse/#svn/trunk/proj/tinyxul/translate.
File hierarchy
tinyxul/
+translate/
application.ini
+chrome/
chrome.manifest
+translate
translate.xul
+defaults
+preferences/
prefs.js
- application.ini: provides metadata that allows XULRunner to launch the application properly.
- prefs.js: the preferences file
- translate.xul: the xul file containing the layout of the application and, in this case, the javascript methods
translate/application.ini
This file provides metadata that allows XULRunner to launch the application properly.
[App]
Vendor=lindenb
Name=translate
Version=0.1
BuildID=20090209
ID=translation@pierre.lindenbaum.fr
[Gecko]
MinVersion=1.8
translate/defaults/preferences/prefs.js
The preference file contains among other things, the URI of the main xul window to be opened when the application starts
pref("toolkit.defaultChromeURI", "chrome://translate/content/translate.xul");
translate/chrome/translate/translate.xul
The window is defined with the XML/XUL layout. At the end, it will look like this:
The application is a <window>, it contains a <menubar>, two <textbox> (for the DNA and the proteic sequences) and a <menulist> where the user select the Genetic Code (standard, mitochondrial...)
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window id="main" title="Translate" width="800" height="600"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="init()"
>
<script>(...)
</script>
<toolbox flex="1">
<menubar id="sample-menubar">
<menu label="File">
<menupopup id="file-popup">
<menuitem label="New" oncommand="doNewWindow();"/>
<menuitem label="Save As..." oncommand="doSaveAs();"/>
<menuseparator/>
<menuitem label="Exit" oncommand="window.close();"/>
</menupopup>
</menu>
</menubar>
</toolbox>
<vbox flex="13">
<hbox><label control="dnaseq" value="Your DNA Sequence" label="Your DNA Sequence" /><label id="dnalength" flex="1" value="0 pb"/></hbox>
<textbox flex="6" id="dnaseq" rows="5" multiline="true" onchange="doTranslate()" oninput="doTranslate()"/>
<hbox>
<label flex="1" control="protseq" value="The Translated Sequence"/>
<label id="protlength" flex="1" value="0 AA"/>
<menulist flex="1" oncommand="currentGeneticCode=GeneticCode[selectedIndex];doTranslate();">
<menupopup id="gcpopup">
</menupopup>
</menulist>
</hbox>
<textbox flex="6" id="protseq" rows="5" multiline="true" readOnly="true"/>
</vbox>
</window>
An array of genetic codes is stored as a javascript array (BTW, thanks to
Brad Chapman and
PJ Davis for their suggestions):
/* via ftp://ftp.ncbi.nih.gov/entrez/misc/data/gc.prt */
var GeneticCode=[
{
name: "Standard" ,
ncbieaa : "FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG"
},
{
name: "Vertebrate Mitochondrial" ,
ncbieaa : "FFLLSSSSYY**CCWWLLLLPPPPHHQQRRRRIIMMTTTTNNKKSS**VVVVAAAADDEEGGGG"
},
{
name: "Yeast Mitochondrial" ,
ncbieaa : "FFLLSSSSYY**CCWWTTTTPPPPHHQQRRRRIIMMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
},
{
name: "Bacterial and Plant Plastid" ,
ncbieaa : "FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG",
}
];
Each time the DNA sequence is modified, the method
doTranslate is called.
function doTranslate()
{
var ncbieaa= currentGeneticCode.ncbieaa ;
var dna=document.getElementById('dnaseq').value;
var prot="";
var i=0;
var aa="";
var dnalength=0;
var protlength=0;
while(i< dna.length)
{
var c= dna.charAt(i++);
if("\n\t \r".indexOf(c)!=-1)
{
continue;
}
dnalength++;
aa+=c;
if(aa.length==3)
{
prot+=translation(ncbieaa,aa);
protlength++;
if(protlength % 50==0) { prot+="\n";}
aa="";
}
}
document.getElementById('protseq').value = prot;
document.getElementById('protlength').value = protlength+" AA";
document.getElementById('dnalength').value = dnalength+" pb";
}
And because, this is a standalone application, the user can
SAVE the sequence of the protein.
function doSaveAs()
{
try {
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, "Save As...", nsIFilePicker.modeSave);
var rv = fp.show();
if (!(rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) ) return;
var file = fp.file;
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
var data=document.getElementById('protseq').value;
foStream.write(data, data.length);
foStream.close();
} catch(err){ alert(err.message);}
}
Testing the application
To launch the application, call
xulrunnerxulrunner translate/application.ini
Or firefox with the
app option
firefox -app translate/application.ini
I guess there should have a way to package this application in a jar/zip, but I still haven't found a way to to this.
That's it !
Pierre