[med-svn] [gbrowse] 01/04: New upstream version 2.56+dfsg

Andreas Tille tille at debian.org
Mon Jan 16 04:36:36 UTC 2017


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository gbrowse.

commit 84a062670bd5438f676cd4335d8f1d59cb4c30d1
Author: Andreas Tille <tille at debian.org>
Date:   Mon Jan 16 04:54:01 2017 +0100

    New upstream version 2.56+dfsg
---
 Build.PL                                           |   11 +-
 Changes                                            |   30 +
 MANIFEST                                           |   78 +-
 META.json                                          |  317 +++---
 META.yml                                           |  197 ++--
 Makefile.PL                                        |    2 +-
 README                                             |    4 +-
 README.IF.GBROWSE.ISNT.WORKING                     |    7 +
 bin/gbrowse_aws_balancer.pl                        |  562 +++++++++++
 bin/gbrowse_configure_slaves.pl                    |    2 +-
 bin/gbrowse_launch_aws_slaves.pl                   |  222 -----
 bin/gbrowse_syn_load_alignments_msa.pl             |   40 +-
 bin/gbrowse_sync_aws_slave.pl                      |  176 ++++
 bin/split_wig.pl                                   |   74 ++
 bin/wiggle2gff3.pl                                 |    8 +
 cgi-bin/das                                        |    6 +
 cgi-bin/gbgff                                      |    6 +
 cgi-bin/gbrowse                                    |   22 +-
 cgi-bin/gbrowse_details                            |    6 +
 cgi-bin/gbrowse_gmap                               |    6 +
 cgi-bin/gbrowse_img                                |  114 ++-
 cgi-bin/gbrowse_key_img                            |    6 +
 cgi-bin/gbrowse_login                              |    6 +
 cgi-bin/gbrowse_syn                                |   38 +-
 conf/GBrowse.conf                                  |   23 +-
 conf/aws_balancer.conf                             |   28 +
 conf/languages/POSIX.pm                            |    4 +
 conf/plugins/Blat.pm                               |  112 ++-
 conf/plugins/FilterTest.pm                         |    2 +-
 conf/plugins/PrimerDesigner.pm                     |    9 +-
 conf/plugins/RestrictionAnnotator.pm               |    5 +-
 conf/plugins/Submitter.pm                          |    9 +-
 conf/renderfarm.conf                               |    3 -
 conf/slave_preload.conf                            |   19 +-
 ...ynconf.disabled => oryza.synconf.disabled.conf} |    8 +-
 conf/synteny/rice_synteny.conf                     |    4 +-
 conf/synteny/wild_rice_synteny.conf                |    4 +-
 conf/yeast_chr1+2.conf                             |    2 +-
 etc/default/gbrowse-aws-balancer                   |   16 +
 etc/default/gbrowse-slave                          |    4 +-
 etc/init.d/gbrowse-aws-balancer                    |   74 ++
 etc/init.d/gbrowse-slave                           |    9 +-
 htdocs/annotation_help.html                        |  152 ++-
 htdocs/cloud_index.html                            |    1 +
 htdocs/css/karyotype.css                           |    8 +-
 htdocs/css/tracks.css                              |   13 +
 htdocs/gbrowse_syn_help.html                       |  110 +--
 htdocs/images/buttons/pop_in.png                   |  Bin 0 -> 189 bytes
 htdocs/images/buttons/pop_out.png                  |  Bin 0 -> 201 bytes
 htdocs/images/help/sugarcane_sorghum_synteny.png   |  Bin 0 -> 17912 bytes
 htdocs/index.html                                  |   18 +-
 htdocs/js/buttons.js                               |    2 +
 htdocs/js/controller.js                            |  134 ++-
 htdocs/js/dragdrop.js                              |    5 +-
 htdocs/js/login.js                                 |   10 +-
 htdocs/js/overviewSelect.js                        |   40 +-
 htdocs/js/toggle.js                                |   58 ++
 htdocs/js/track_pan.js                             |    5 +-
 htdocs/tutorial/conf_files/elegans_core.conf       |    2 +-
 htdocs/tutorial/conf_files/elegans_extra.conf      |    2 +-
 htdocs/tutorial/conf_files/volvox.conf             |    2 +-
 htdocs/tutorial/conf_files/volvox_final.conf       |    2 +-
 .../conf_files/volvox_final_withPhylo.conf         |    2 +-
 htdocs/tutorial/conf_files/volvox_halfway.conf     |    2 +-
 htdocs/tutorial/conf_files/volvox_quarter.conf     |    2 +-
 htdocs/tutorial/conf_files/volvox_refactored.conf  |   10 +-
 htdocs/tutorial/data_files/volvox_all.gff3         |    2 +-
 htdocs/tutorial/data_files/volvox_microarray.gff3  |    2 +-
 install_util/GBrowseGuessDirectories.pm            |   10 +-
 install_util/GBrowseInstall.pm                     |  162 +++-
 lib/Bio/DB/SeqFeature/Store/LoadHelper.pm          |  200 ++++
 lib/Bio/Graphics/Browser2.pm                       |   29 +-
 lib/Bio/Graphics/Browser2/Action.pm                |   30 +-
 lib/Bio/Graphics/Browser2/DataLoader/archive.pm    |  318 ++++++
 lib/Bio/Graphics/Browser2/DataLoader/bam.pm        |   18 +-
 lib/Bio/Graphics/Browser2/DataLoader/bigbed.pm     |  119 +++
 lib/Bio/Graphics/Browser2/DataLoader/useq.pm       |  361 +++++++
 lib/Bio/Graphics/Browser2/DataSource.pm            |    9 +-
 lib/Bio/Graphics/Browser2/Region.pm                |    1 +
 lib/Bio/Graphics/Browser2/RegionSearch.pm          |    1 +
 lib/Bio/Graphics/Browser2/Render.pm                |   53 +-
 lib/Bio/Graphics/Browser2/Render/HTML.pm           |   12 +-
 lib/Bio/Graphics/Browser2/Render/Login.pm          |    3 +-
 lib/Bio/Graphics/Browser2/Render/Slave.pm          |   16 +-
 .../Graphics/Browser2/Render/Slave/AWS_Balancer.pm | 1022 ++++++++++++++++++++
 lib/Bio/Graphics/Browser2/Render/Slave/Status.pm   |   17 +-
 lib/Bio/Graphics/Browser2/RenderPanels.pm          |  305 +++---
 lib/Bio/Graphics/Browser2/SendMail.pm              |    2 +-
 lib/Bio/Graphics/Browser2/TrackDumper.pm           |    2 +
 lib/Bio/Graphics/Browser2/UserDB.pm                |    6 +-
 lib/Bio/Graphics/Browser2/UserTracks.pm            |   79 +-
 lib/Bio/Graphics/Karyotype.pm                      |    1 -
 lib/Legacy/Graphics/Browser.pm                     |    2 +-
 lib/Legacy/Graphics/Browser/Synteny.pm             |   17 +-
 sample_data/yeast_chr1+2/dummy.fa                  |    0
 t/00.compile.t                                     |    3 +-
 t/04.remoteserver.t                                |  298 +++---
 t/07.balancer.t                                    |   45 +
 t/testdata/conf/aws_slave.conf                     |   23 +
 t/testdata/conf/languages/POSIX.pm                 |    4 +
 t/testdata/data/volvox/dummy.fa                    |    0
 t/testdata/data/volvox2/dummy.fa                   |    0
 t/testdata/data/volvox3/dummy.fa                   |    0
 t/testdata/data/volvox4/dummy.fa                   |    0
 104 files changed, 4747 insertions(+), 1280 deletions(-)

diff --git a/Build.PL b/Build.PL
index d03445b..7a664ab 100644
--- a/Build.PL
+++ b/Build.PL
@@ -17,13 +17,13 @@ my $build = GBrowseInstall->new(
 
     requires     => {
 	'perl'                => '5.008',
-	'Bio::Root::Version'  => '1.0069',
-	'Bio::Graphics'       => '2.31',
+	'Bio::Root::Version'  => '1.007001',
+	'Bio::Graphics'       => '2.34',
 	'CGI::Session'        => '4.02',
 	'Digest::MD5'         => 0,	
         'ExtUtils::CBuilder'  => 0,
 	'File::Temp'          => 0,
-	'GD'                  => '2.07',
+	'GD'                  => '2.50',
 	'Text::ParseWords'    => '3.27',
 	'IO::String'          => 0,
 	'JSON'                => 0,
@@ -34,6 +34,8 @@ my $build = GBrowseInstall->new(
         'Digest::SHA'         => 0,
         'Date::Parse'         => 0,
         'Term::ReadKey'       => 0,
+	'HTTP::Daemon'        => 0,
+	'parent'              => 0,
     },
     build_requires => {'Capture::Tiny'       => 0,},
     recommends  => {
@@ -66,7 +68,8 @@ my $build = GBrowseInstall->new(
 	'bin/gbrowse_import_ucsc_db.pl',
 	'bin/gbrowse_create_account.pl',
 	'bin/gbrowse_change_passwd.pl',
-	'bin/gbrowse_launch_aws_slaves.pl',
+	'bin/gbrowse_aws_balancer.pl',
+	'bin/gbrowse_sync_aws_slave.pl',
 	'bin/gbrowse_configure_slaves.pl',
 	'bin/gbrowse_metadb_config.pl',
 	'bin/gbrowse_grow_cloud_vol.pl',
diff --git a/Changes b/Changes
index b432185..0fd7f03 100644
--- a/Changes
+++ b/Changes
@@ -1,3 +1,33 @@
+2.56
+ * SECURITY FIXES:
+    - Prevent ability of an attacker to delete other users' accounts
+    - Fix cookies to carry the httponly flag, reducing chance of cross-site
+      scripting exploits.
+    - Fix cross-site scripting vulnerability in generation of citation text.
+ * Fix failure of feature summary message to appear on mousedown events.
+ * If svg2pdf executable is in search path, use this in preference to inkscape
+   (thanks to Nathan Weeks)
+ * Fix left-align of citation text in "?" menu (thanks to Nathan Weeks)
+ * Avoid accumulating many .inverted files in the bio_graphics_ff_UUID cache
+   (thanks to Nathan Weeks).
+ * Fix summary mode for features with no source tag (thanks to Nathan Weeks).
+ * Don't spawn child processes if there are no uncached requests (thanks to
+   Nathan Weeks yet again!)
+
+2.55
+ * Better detection and handling of render slaves that have gone now. If all slaves are
+   down then throws an error (once) and then reverts to local rendering for a period of time.
+ * Updated installer to give dire warning if installing into /usr/local/apache
+   and there doesn't appear to be an apache instance there.
+ * Support truetype fonts (need Bio::Graphics 2.33 or higher; set truetype=1 in GBrowse.conf, and
+   optionally pass font names like "Helvetica-12:Italic" to the track "font" option). 
+ * Clicking on the "Go" button when an annotation plugin is selected now turns on the corresponding track.
+ * Clicking on the "Go" button when a filter plugin is selected opens up the configuration dialog.
+ * Added Juan Tena's split_wig.pl script, which is useful for uploading WIG files from genomes with lots
+   of scaffolds.
+ * Fix performance problems when using in-memory databases composed of multiple GFF3 files. The fix is
+   a hack that involves overwriting the Bio::DB::SeqFeature::Store::LoadHelper module!
+
 2.54
  * Version 2.53 introduced a bad bug into track configuration such
    that semantic zooming no longer works for user-provided
diff --git a/MANIFEST b/MANIFEST
index 1dc49c7..2adbb44 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,13 +1,13 @@
 bin/auto_install_databases.pl
 bin/bed2gff3.pl
 bin/bp_load_gff.pl
+bin/gbrowse_aws_balancer.pl
 bin/gbrowse_change_passwd.pl
 bin/gbrowse_clean.pl
 bin/gbrowse_configure_slaves.pl
 bin/gbrowse_create_account.pl
 bin/gbrowse_grow_cloud_vol.pl
 bin/gbrowse_import_ucsc_db.pl
-bin/gbrowse_launch_aws_slaves.pl
 bin/gbrowse_metadb_config.pl
 bin/gbrowse_netinstall.pl
 bin/gbrowse_netinstall2.pl
@@ -16,6 +16,7 @@ bin/gbrowse_slave
 bin/gbrowse_slave_start_aws.sh
 bin/gbrowse_syn_load_alignment_database.pl
 bin/gbrowse_syn_load_alignments_msa.pl
+bin/gbrowse_sync_aws_slave.pl
 bin/gtf2gff3.pl
 bin/load_genbank.pl
 bin/make_das_conf.pl
@@ -25,6 +26,7 @@ bin/process_ncbi_human.pl
 bin/process_sgd.pl
 bin/report_missing_language_tags.pl
 bin/scan_gbrowse.pl
+bin/split_wig.pl
 bin/ucsc_genes2gff.pl
 bin/wiggle2gff3.pl
 Build.PL
@@ -38,6 +40,7 @@ cgi-bin/gbrowse_key_img
 cgi-bin/gbrowse_login
 cgi-bin/gbrowse_syn
 Changes
+conf/aws_balancer.conf
 conf/detail_select_menu.conf
 conf/enzymes.txt
 conf/GBrowse.conf
@@ -98,7 +101,7 @@ conf/pop_demo.conf
 conf/renderfarm.conf
 conf/slave_preload.conf
 conf/submitter_plugin.conf
-conf/synteny/oryza.synconf.disabled
+conf/synteny/oryza.synconf.disabled.conf
 conf/synteny/rice_synteny.conf
 conf/synteny/wild_rice_synteny.conf
 conf/themes/solid_gray_colors
@@ -108,6 +111,59 @@ conf/volvox.conf
 conf/yeast_chr1+2.conf
 conf/yeast_renderfarm.conf
 conf/yeast_simple.conf
+contrib/ace2gff.pl
+contrib/AxtPairwise_convert/axt2phy.pl
+contrib/blast2gff.pl
+contrib/conf_files/01.yeast.conf
+contrib/conf_files/02.wormbase.conf
+contrib/conf_files/03.fly.conf
+contrib/conf_files/04.human.conf
+contrib/conf_files/05.embl_proxy.conf
+contrib/conf_files/06.biosql.conf
+contrib/conf_files/07.chado.conf
+contrib/conf_files/08.genbank.conf
+contrib/conf_files/09.human_karyotype.conf
+contrib/conf_files/index.html
+contrib/conf_files/wormbase.syn
+contrib/Coverage/INSTALL
+contrib/Coverage/plugin/Coverage.pm
+contrib/Coverage/README
+contrib/gdump.pl
+contrib/GeneFinder/genefinder/gfcode.c
+contrib/GeneFinder/genefinder/README
+contrib/GeneFinder/genefinder/readseq.c
+contrib/GeneFinder/genefinder/readseq.h
+contrib/GeneFinder/nematode_gftables/cds.hex
+contrib/GeneFinder/nematode_gftables/gftables
+contrib/GeneFinder/nematode_gftables/newnem.atg
+contrib/GeneFinder/nematode_gftables/newnem.codon
+contrib/GeneFinder/nematode_gftables/newnem.gene
+contrib/GeneFinder/nematode_gftables/newnem.intron3
+contrib/GeneFinder/nematode_gftables/newnem.intron5
+contrib/GeneFinder/nematode_gftables/ref.hist
+contrib/GeneFinder/nematode_gftables/zk637.atg
+contrib/GeneFinder/nematode_gftables/zk637.intron3
+contrib/GeneFinder/nematode_gftables/zk637.intron5
+contrib/GeneFinder/nematode_gftables/zk637.trinuc
+contrib/import_ncbi_mv_hs.pl
+contrib/install_macosx/BuildConfig.pm
+contrib/install_macosx/README.html
+contrib/install_macosx/README.pod
+contrib/install_macosx/scripts/build-gbrowse.pl
+contrib/install_macosx/scripts/build-libraries.pl
+contrib/README
+contrib/SynView/cgi-lib/DAS/GUS.pm
+contrib/SynView/cgi-lib/DAS/GUS/Segment.pm
+contrib/SynView/cgi-lib/DAS/GUS/Segment/Feature.pm
+contrib/SynView/cgi-lib/DAS/Util/SqlParser.pm
+contrib/SynView/gbrowse.conf/plasmodb.conf
+contrib/SynView/gbrowse.conf/plasmodb.xml
+contrib/SynView/MAL11/MAL11.conf
+contrib/SynView/MAL11/MAL11.fa
+contrib/SynView/MAL11/MAL11.gff3
+contrib/SynView/README
+contrib/TextDumper/README
+contrib/TextDumper/TextDumper.pm
 DISCLAIMER
 docs/developer_notes/README.rearchitecture
 docs/developer_notes/README.sharing_tracks
@@ -134,7 +190,9 @@ docs/pod/README-gff-files.pod
 docs/pod/README-lucegene.pod
 docs/README.AWS
 docs/README.tutorial
+etc/default/gbrowse-aws-balancer
 etc/default/gbrowse-slave
+etc/init.d/gbrowse-aws-balancer
 etc/init.d/gbrowse-slave
 htdocs/annotation_help.html
 htdocs/cloud_index.html
@@ -242,6 +300,8 @@ htdocs/images/buttons/panright.png
 htdocs/images/buttons/panright2.png
 htdocs/images/buttons/plus.gif
 htdocs/images/buttons/plus.png
+htdocs/images/buttons/pop_in.png
+htdocs/images/buttons/pop_out.png
 htdocs/images/buttons/query.png
 htdocs/images/buttons/red.gif
 htdocs/images/buttons/red1.gif
@@ -286,6 +346,7 @@ htdocs/images/help/overview+detail.png
 htdocs/images/help/overview.gif
 htdocs/images/help/remote.png
 htdocs/images/help/search+settings.gif
+htdocs/images/help/sugarcane_sorghum_synteny.png
 htdocs/images/help/track+settings.gif
 htdocs/images/help/upload+edit.gif
 htdocs/images/help/upload+remote.gif
@@ -333,6 +394,7 @@ htdocs/js/sound.js
 htdocs/js/subtracktable.css
 htdocs/js/subtracktable.js
 htdocs/js/tabs.js
+htdocs/js/toggle.js
 htdocs/js/track.js
 htdocs/js/track_configure.js
 htdocs/js/track_pan.js
@@ -507,6 +569,7 @@ lib/Bio/DB/GFF/Aggregator/waba_alignment.pm
 lib/Bio/DB/GFF/Aggregator/wormbase_gene.pm
 lib/Bio/DB/SeqFeature/Store/Alias.pm
 lib/Bio/DB/SeqFeature/Store/BedLoader.pm
+lib/Bio/DB/SeqFeature/Store/LoadHelper.pm
 lib/Bio/DB/Tagger.pm
 lib/Bio/DB/Tagger/mysql.pm
 lib/Bio/DB/Tagger/Tag.pm
@@ -518,14 +581,17 @@ lib/Bio/Graphics/Browser2/CAlign.pm
 lib/Bio/Graphics/Browser2/CAlign.xs
 lib/Bio/Graphics/Browser2/DataBase.pm
 lib/Bio/Graphics/Browser2/DataLoader.pm
+lib/Bio/Graphics/Browser2/DataLoader/archive.pm
 lib/Bio/Graphics/Browser2/DataLoader/bam.pm
 lib/Bio/Graphics/Browser2/DataLoader/bed.pm
+lib/Bio/Graphics/Browser2/DataLoader/bigbed.pm
 lib/Bio/Graphics/Browser2/DataLoader/bigwig.pm
 lib/Bio/Graphics/Browser2/DataLoader/featurefile.pm
 lib/Bio/Graphics/Browser2/DataLoader/generic.pm
 lib/Bio/Graphics/Browser2/DataLoader/gff.pm
 lib/Bio/Graphics/Browser2/DataLoader/gff3.pm
 lib/Bio/Graphics/Browser2/DataLoader/sam.pm
+lib/Bio/Graphics/Browser2/DataLoader/useq.pm
 lib/Bio/Graphics/Browser2/DataLoader/wig2bigwig.pm
 lib/Bio/Graphics/Browser2/DataLoader/wiggle.pm
 lib/Bio/Graphics/Browser2/DataSource.pm
@@ -548,6 +614,7 @@ lib/Bio/Graphics/Browser2/Render/HTML/TrackListing.pm
 lib/Bio/Graphics/Browser2/Render/HTML/TrackListing/Categories.pm
 lib/Bio/Graphics/Browser2/Render/Login.pm
 lib/Bio/Graphics/Browser2/Render/Slave.pm
+lib/Bio/Graphics/Browser2/Render/Slave/AWS_Balancer.pm
 lib/Bio/Graphics/Browser2/Render/Slave/Status.pm
 lib/Bio/Graphics/Browser2/Render/SnapshotManager.pm
 lib/Bio/Graphics/Browser2/Render/TrackConfig.pm
@@ -593,6 +660,7 @@ sample_data/ideograms/human_cytobands.gff
 sample_data/ideograms/mouse_cytobands.gff
 sample_data/ideograms/rat_cytobands.gff
 sample_data/pop_demo/pop_demo.gff3
+sample_data/yeast_chr1+2/dummy.fa
 sample_data/yeast_chr1+2/yeast_chr1+2.gff3
 sample_data/yeast_scaffolds/chr1.fa
 sample_data/yeast_scaffolds/chr2.fa
@@ -604,9 +672,11 @@ t/03.render.t
 t/04.remoteserver.t
 t/05.deferredrendering.t
 t/06.featuresearch.t
+t/07.balancer.t
 t/07.karyotype.t
 t/08.calign.t
 t/Test.pm
+t/testdata/conf/aws_slave.conf
 t/testdata/conf/enzymes.txt
 t/testdata/conf/GBrowse.conf
 t/testdata/conf/languages/fr.pm
@@ -614,9 +684,13 @@ t/testdata/conf/languages/POSIX.pm
 t/testdata/conf/templates/volvox_final.conf
 t/testdata/conf/templates/yeast_chr1.conf
 t/testdata/conf/WHERE_ARE_THE_CONF_FILES.txt
+t/testdata/data/volvox/dummy.fa
 t/testdata/data/volvox/volvox.gff
+t/testdata/data/volvox2/dummy.fa
 t/testdata/data/volvox2/volvox2.gff
+t/testdata/data/volvox3/dummy.fa
 t/testdata/data/volvox3/volvox3.gff
+t/testdata/data/volvox4/dummy.fa
 t/testdata/data/volvox4/volvox4.gff
 t/testdata/phylo_align/phyloalign.fa
 t/testdata/phylo_align/readme.txt
diff --git a/META.json b/META.json
index 66314b0..8d0c525 100644
--- a/META.json
+++ b/META.json
@@ -4,7 +4,7 @@
       "Lincoln Stein <lincoln.stein at gmail.com>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "Module::Build version 0.38, CPAN::Meta::Converter version 2.112150",
+   "generated_by" : "Module::Build version 0.4216",
    "license" : [
       "perl_5"
    ],
@@ -16,64 +16,65 @@
    "prereqs" : {
       "build" : {
          "requires" : {
-            "Capture::Tiny" : 0,
-            "ExtUtils::CBuilder" : 0
+            "Capture::Tiny" : "0",
+            "ExtUtils::CBuilder" : "0"
          }
       },
       "configure" : {
          "requires" : {
-            "Module::Build" : "0.38"
+            "Module::Build" : "0.42"
          }
       },
       "runtime" : {
          "recommends" : {
             "Bio::DB::BigFile" : "1",
             "Bio::DB::Sam" : "1.36",
-            "Bio::Das" : 0,
-            "Crypt::SSLeay" : 0,
-            "DBD::Pg" : 0,
-            "DBD::SQLite" : 0,
-            "DBD::mysql" : 0,
-            "DBI" : 0,
-            "DB_File::Lock" : 0,
-            "Digest::SHA" : 0,
-            "FCGI" : 0,
-            "File::NFSLock" : 0,
-            "GD::SVG" : 0,
-            "Math::BigInt" : 0,
-            "Net::OpenID::Consumer" : 0,
-            "Net::SMTP::SSL" : 0,
-            "Parse::Apache::ServerStatus" : 0,
+            "Bio::Das" : "0",
+            "Crypt::SSLeay" : "0",
+            "DBD::Pg" : "0",
+            "DBD::SQLite" : "0",
+            "DBD::mysql" : "0",
+            "DBI" : "0",
+            "DB_File::Lock" : "0",
+            "Digest::SHA" : "0",
+            "FCGI" : "0",
+            "File::NFSLock" : "0",
+            "GD::SVG" : "0",
+            "Math::BigInt" : "0",
+            "Net::OpenID::Consumer" : "0",
+            "Net::SMTP::SSL" : "0",
+            "Parse::Apache::ServerStatus" : "0",
             "Template" : "2.2",
-            "Term::ReadKey" : 0,
+            "Term::ReadKey" : "0",
             "VM::EC2" : "1.19"
          },
          "requires" : {
-            "Bio::Graphics" : "2.31",
-            "Bio::Root::Version" : "1.0069",
+            "Bio::Graphics" : "2.34",
+            "Bio::Root::Version" : "1.007001",
             "CGI::Session" : "4.02",
-            "Date::Parse" : 0,
-            "Digest::MD5" : 0,
-            "Digest::SHA" : 0,
-            "ExtUtils::CBuilder" : 0,
-            "File::Temp" : 0,
-            "GD" : "2.07",
-            "IO::String" : 0,
-            "JSON" : 0,
-            "LWP" : 0,
-            "Statistics::Descriptive" : 0,
-            "Storable" : 0,
-            "Term::ReadKey" : 0,
+            "Date::Parse" : "0",
+            "Digest::MD5" : "0",
+            "Digest::SHA" : "0",
+            "ExtUtils::CBuilder" : "0",
+            "File::Temp" : "0",
+            "GD" : "2.50",
+            "HTTP::Daemon" : "0",
+            "IO::String" : "0",
+            "JSON" : "0",
+            "LWP" : "0",
+            "Statistics::Descriptive" : "0",
+            "Storable" : "0",
+            "Term::ReadKey" : "0",
             "Text::ParseWords" : "3.27",
-            "Time::HiRes" : 0,
+            "Time::HiRes" : "0",
+            "parent" : "0",
             "perl" : "5.008"
          }
       }
    },
    "provides" : {
       "Bio::DB::GFF::Aggregator::match_gap" : {
-         "file" : "lib/Bio/DB/GFF/Aggregator/match_gap.pm",
-         "version" : 0
+         "file" : "lib/Bio/DB/GFF/Aggregator/match_gap.pm"
       },
       "Bio::DB::GFF::Aggregator::reftranscript" : {
          "file" : "lib/Bio/DB/GFF/Aggregator/reftranscript.pm",
@@ -88,296 +89,246 @@
          "version" : "0.30"
       },
       "Bio::DB::SeqFeature::Store::Alias" : {
-         "file" : "lib/Bio/DB/SeqFeature/Store/Alias.pm",
-         "version" : 0
+         "file" : "lib/Bio/DB/SeqFeature/Store/Alias.pm"
       },
       "Bio::DB::SeqFeature::Store::Alias::Iterator" : {
-         "file" : "lib/Bio/DB/SeqFeature/Store/Alias.pm",
-         "version" : 0
+         "file" : "lib/Bio/DB/SeqFeature/Store/Alias.pm"
       },
       "Bio::DB::SeqFeature::Store::Alias::Segment" : {
-         "file" : "lib/Bio/DB/SeqFeature/Store/Alias.pm",
-         "version" : 0
+         "file" : "lib/Bio/DB/SeqFeature/Store/Alias.pm"
       },
       "Bio::DB::SeqFeature::Store::BedLoader" : {
-         "file" : "lib/Bio/DB/SeqFeature/Store/BedLoader.pm",
-         "version" : 0
+         "file" : "lib/Bio/DB/SeqFeature/Store/BedLoader.pm"
+      },
+      "Bio::DB::SeqFeature::Store::LoadHelper" : {
+         "file" : "lib/Bio/DB/SeqFeature/Store/LoadHelper.pm",
+         "version" : "1.10"
       },
       "Bio::DB::Tagger" : {
          "file" : "lib/Bio/DB/Tagger.pm",
          "version" : "1.00"
       },
       "Bio::DB::Tagger::Iterator" : {
-         "file" : "lib/Bio/DB/Tagger.pm",
-         "version" : 0
+         "file" : "lib/Bio/DB/Tagger.pm"
       },
       "Bio::DB::Tagger::Tag" : {
-         "file" : "lib/Bio/DB/Tagger/Tag.pm",
-         "version" : 0
+         "file" : "lib/Bio/DB/Tagger/Tag.pm"
       },
       "Bio::DB::Tagger::mysql" : {
-         "file" : "lib/Bio/DB/Tagger/mysql.pm",
-         "version" : 0
+         "file" : "lib/Bio/DB/Tagger/mysql.pm"
       },
       "Bio::Graphics::Browser2" : {
          "file" : "lib/Bio/Graphics/Browser2.pm",
-         "version" : "2.54"
+         "version" : "2.56"
       },
       "Bio::Graphics::Browser2::Action" : {
-         "file" : "lib/Bio/Graphics/Browser2/Action.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Action.pm"
       },
       "Bio::Graphics::Browser2::AdminTracks" : {
-         "file" : "lib/Bio/Graphics/Browser2/UserTracks.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/UserTracks.pm"
       },
       "Bio::Graphics::Browser2::AuthorizedFeatureFile" : {
-         "file" : "lib/Bio/Graphics/Browser2/AuthorizedFeatureFile.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/AuthorizedFeatureFile.pm"
       },
       "Bio::Graphics::Browser2::CAlign" : {
-         "file" : "lib/Bio/Graphics/Browser2/CAlign.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/CAlign.pm"
       },
       "Bio::Graphics::Browser2::CachedTrack" : {
-         "file" : "lib/Bio/Graphics/Browser2/CachedTrack.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/CachedTrack.pm"
       },
       "Bio::Graphics::Browser2::DataBase" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataBase.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataBase.pm"
       },
       "Bio::Graphics::Browser2::DataLoader" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader.pm"
+      },
+      "Bio::Graphics::Browser2::DataLoader::archive" : {
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/archive.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::bam" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/bam.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/bam.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::bed" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/bed.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/bed.pm"
+      },
+      "Bio::Graphics::Browser2::DataLoader::bigbed" : {
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/bigbed.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::bigwig" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/bigwig.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/bigwig.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::featurefile" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/featurefile.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/featurefile.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::generic" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/generic.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/generic.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::gff" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/gff.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/gff.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::gff3" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/gff3.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/gff3.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::sam" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/sam.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/sam.pm"
+      },
+      "Bio::Graphics::Browser2::DataLoader::useq" : {
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/useq.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::wig2bigwig" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/wig2bigwig.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/wig2bigwig.pm"
       },
       "Bio::Graphics::Browser2::DataLoader::wiggle" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/wiggle.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/wiggle.pm"
       },
       "Bio::Graphics::Browser2::DataSource" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataSource.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataSource.pm"
       },
       "Bio::Graphics::Browser2::ExternalData" : {
-         "file" : "lib/Bio/Graphics/Browser2/ExternalData.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/ExternalData.pm"
       },
       "Bio::Graphics::Browser2::GFFhelper" : {
-         "file" : "lib/Bio/Graphics/Browser2/GFFhelper.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/GFFhelper.pm"
       },
       "Bio::Graphics::Browser2::I18n" : {
-         "file" : "lib/Bio/Graphics/Browser2/I18n.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/I18n.pm"
       },
       "Bio::Graphics::Browser2::Markup" : {
-         "file" : "lib/Bio/Graphics/Browser2/Markup.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Markup.pm"
       },
       "Bio::Graphics::Browser2::MetaDB" : {
-         "file" : "lib/Bio/Graphics/Browser2/RegionSearch.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/RegionSearch.pm"
       },
       "Bio::Graphics::Browser2::MetaSegment" : {
-         "file" : "lib/Bio/Graphics/Browser2/RegionSearch.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/RegionSearch.pm"
       },
       "Bio::Graphics::Browser2::MetaSegment::Iterator" : {
-         "file" : "lib/Bio/Graphics/Browser2/RegionSearch.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/RegionSearch.pm"
       },
       "Bio::Graphics::Browser2::OptionPick" : {
-         "file" : "lib/Bio/Graphics/Browser2/OptionPick.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/OptionPick.pm"
       },
       "Bio::Graphics::Browser2::PadAlignment" : {
-         "file" : "lib/Bio/Graphics/Browser2/PadAlignment.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/PadAlignment.pm"
       },
       "Bio::Graphics::Browser2::Plugin" : {
          "file" : "lib/Bio/Graphics/Browser2/Plugin.pm",
          "version" : "0.30"
       },
       "Bio::Graphics::Browser2::Plugin::AuthPlugin" : {
-         "file" : "lib/Bio/Graphics/Browser2/Plugin/AuthPlugin.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Plugin/AuthPlugin.pm"
       },
       "Bio::Graphics::Browser2::PluginSet" : {
-         "file" : "lib/Bio/Graphics/Browser2/PluginSet.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/PluginSet.pm"
       },
       "Bio::Graphics::Browser2::Realign" : {
-         "file" : "lib/Bio/Graphics/Browser2/Realign.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Realign.pm"
       },
       "Bio::Graphics::Browser2::Region" : {
-         "file" : "lib/Bio/Graphics/Browser2/Region.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Region.pm"
       },
       "Bio::Graphics::Browser2::RegionSearch" : {
-         "file" : "lib/Bio/Graphics/Browser2/RegionSearch.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/RegionSearch.pm"
       },
       "Bio::Graphics::Browser2::RemoteSet" : {
-         "file" : "lib/Bio/Graphics/Browser2/RemoteSet.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/RemoteSet.pm"
       },
       "Bio::Graphics::Browser2::Render" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render.pm"
       },
       "Bio::Graphics::Browser2::Render::HTML" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render/HTML.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render/HTML.pm"
       },
       "Bio::Graphics::Browser2::Render::HTML::TrackListing" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render/HTML/TrackListing.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render/HTML/TrackListing.pm"
       },
       "Bio::Graphics::Browser2::Render::HTML::TrackListing::Categories" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render/HTML/TrackListing/Categories.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render/HTML/TrackListing/Categories.pm"
       },
       "Bio::Graphics::Browser2::Render::Login" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render/Login.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render/Login.pm"
       },
       "Bio::Graphics::Browser2::Render::Slave" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render/Slave.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render/Slave.pm"
+      },
+      "Bio::Graphics::Browser2::Render::Slave::AWS_Balancer" : {
+         "file" : "lib/Bio/Graphics/Browser2/Render/Slave/AWS_Balancer.pm"
+      },
+      "Bio::Graphics::Browser2::Render::Slave::StagingServer" : {
+         "file" : "lib/Bio/Graphics/Browser2/Render/Slave/AWS_Balancer.pm"
       },
       "Bio::Graphics::Browser2::Render::Slave::Status" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render/Slave/Status.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render/Slave/Status.pm"
       },
       "Bio::Graphics::Browser2::Render::SnapshotManager" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render/SnapshotManager.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render/SnapshotManager.pm"
       },
       "Bio::Graphics::Browser2::Render::TrackConfig" : {
-         "file" : "lib/Bio/Graphics/Browser2/Render/TrackConfig.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Render/TrackConfig.pm"
       },
       "Bio::Graphics::Browser2::RenderPanels" : {
-         "file" : "lib/Bio/Graphics/Browser2/RenderPanels.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/RenderPanels.pm"
       },
       "Bio::Graphics::Browser2::SendMail" : {
-         "file" : "lib/Bio/Graphics/Browser2/SendMail.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/SendMail.pm"
       },
       "Bio::Graphics::Browser2::Session" : {
-         "file" : "lib/Bio/Graphics/Browser2/Session.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Session.pm"
       },
       "Bio::Graphics::Browser2::Shellwords" : {
-         "file" : "lib/Bio/Graphics/Browser2/Shellwords.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Shellwords.pm"
       },
       "Bio::Graphics::Browser2::SubtrackTable" : {
-         "file" : "lib/Bio/Graphics/Browser2/SubtrackTable.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/SubtrackTable.pm"
       },
       "Bio::Graphics::Browser2::TrackDumper" : {
-         "file" : "lib/Bio/Graphics/Browser2/TrackDumper.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/TrackDumper.pm"
       },
       "Bio::Graphics::Browser2::TrackDumper::RichSeqMaker" : {
-         "file" : "lib/Bio/Graphics/Browser2/TrackDumper/RichSeqMaker.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/TrackDumper/RichSeqMaker.pm"
       },
       "Bio::Graphics::Browser2::UserConf" : {
-         "file" : "lib/Bio/Graphics/Browser2/UserTracks.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/UserTracks.pm"
       },
       "Bio::Graphics::Browser2::UserDB" : {
          "file" : "lib/Bio/Graphics/Browser2/UserDB.pm",
          "version" : "0.5"
       },
       "Bio::Graphics::Browser2::UserTracks" : {
-         "file" : "lib/Bio/Graphics/Browser2/UserTracks.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/UserTracks.pm"
       },
       "Bio::Graphics::Browser2::UserTracks::Database" : {
-         "file" : "lib/Bio/Graphics/Browser2/UserTracks/Database.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/UserTracks/Database.pm"
       },
       "Bio::Graphics::Browser2::UserTracks::Filesystem" : {
-         "file" : "lib/Bio/Graphics/Browser2/UserTracks/Filesystem.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/UserTracks/Filesystem.pm"
       },
       "Bio::Graphics::Browser2::Util" : {
-         "file" : "lib/Bio/Graphics/Browser2/Util.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/Util.pm"
       },
       "Bio::Graphics::GBrowseFeature" : {
-         "file" : "lib/Bio/Graphics/GBrowseFeature.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/GBrowseFeature.pm"
       },
       "Bio::Graphics::Karyotype" : {
-         "file" : "lib/Bio/Graphics/Karyotype.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Karyotype.pm"
       },
       "Bio::Graphics::Wiggle::Loader::Nosample" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/wiggle.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/wiggle.pm"
       },
       "CGI::Toggle" : {
-         "file" : "lib/CGI/Toggle.pm",
-         "version" : 0
+         "file" : "lib/CGI/Toggle.pm"
       },
       "FakeHomol" : {
-         "file" : "lib/Bio/Graphics/Browser2/GFFhelper.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/GFFhelper.pm"
       },
       "LRUCache" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataBase.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataBase.pm"
       },
       "Legacy::DB::SyntenyBlock" : {
-         "file" : "lib/Legacy/DB/SyntenyBlock.pm",
-         "version" : 0
+         "file" : "lib/Legacy/DB/SyntenyBlock.pm"
       },
       "Legacy::DB::SyntenyIO" : {
-         "file" : "lib/Legacy/DB/SyntenyIO.pm",
-         "version" : 0
+         "file" : "lib/Legacy/DB/SyntenyIO.pm"
       },
       "Legacy::Graphics::Browser" : {
          "file" : "lib/Legacy/Graphics/Browser.pm",
@@ -388,24 +339,20 @@
          "version" : "1.01"
       },
       "Legacy::Graphics::Browser::PageSettings" : {
-         "file" : "lib/Legacy/Graphics/Browser/PageSettings.pm",
-         "version" : 0
+         "file" : "lib/Legacy/Graphics/Browser/PageSettings.pm"
       },
       "Legacy::Graphics::Browser::Synteny" : {
          "file" : "lib/Legacy/Graphics/Browser/Synteny.pm",
          "version" : "0.01"
       },
       "Legacy::Graphics::Browser::Util" : {
-         "file" : "lib/Legacy/Graphics/Browser/Util.pm",
-         "version" : 0
+         "file" : "lib/Legacy/Graphics/Browser/Util.pm"
       },
       "Legacy::Graphics::BrowserConfig" : {
-         "file" : "lib/Legacy/Graphics/Browser.pm",
-         "version" : 0
+         "file" : "lib/Legacy/Graphics/Browser.pm"
       },
       "MyFeatureFileLoader" : {
-         "file" : "lib/Bio/Graphics/Browser2/DataLoader/featurefile.pm",
-         "version" : 0
+         "file" : "lib/Bio/Graphics/Browser2/DataLoader/featurefile.pm"
       }
    },
    "release_status" : "stable",
@@ -414,5 +361,5 @@
          "http://dev.perl.org/licenses/"
       ]
    },
-   "version" : "2.54"
+   "version" : "2.56"
 }
diff --git a/META.yml b/META.yml
index cb44768..abb3d9a 100644
--- a/META.yml
+++ b/META.yml
@@ -3,310 +3,251 @@ abstract: 'The GMOD Generic Genome Browser'
 author:
   - 'Lincoln Stein <lincoln.stein at gmail.com>'
 build_requires:
-  Capture::Tiny: 0
-  ExtUtils::CBuilder: 0
+  Capture::Tiny: '0'
+  ExtUtils::CBuilder: '0'
 configure_requires:
-  Module::Build: 0.38
+  Module::Build: '0.42'
 dynamic_config: 1
-generated_by: 'Module::Build version 0.38, CPAN::Meta::Converter version 2.112150'
+generated_by: 'Module::Build version 0.4216, CPAN::Meta::Converter version 2.150001'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
-  version: 1.4
+  version: '1.4'
 name: GBrowse
 provides:
   Bio::DB::GFF::Aggregator::match_gap:
     file: lib/Bio/DB/GFF/Aggregator/match_gap.pm
-    version: 0
   Bio::DB::GFF::Aggregator::reftranscript:
     file: lib/Bio/DB/GFF/Aggregator/reftranscript.pm
-    version: 0.10
+    version: '0.10'
   Bio::DB::GFF::Aggregator::waba_alignment:
     file: lib/Bio/DB/GFF/Aggregator/waba_alignment.pm
-    version: 0.20
+    version: '0.20'
   Bio::DB::GFF::Aggregator::wormbase_gene:
     file: lib/Bio/DB/GFF/Aggregator/wormbase_gene.pm
-    version: 0.30
+    version: '0.30'
   Bio::DB::SeqFeature::Store::Alias:
     file: lib/Bio/DB/SeqFeature/Store/Alias.pm
-    version: 0
   Bio::DB::SeqFeature::Store::Alias::Iterator:
     file: lib/Bio/DB/SeqFeature/Store/Alias.pm
-    version: 0
   Bio::DB::SeqFeature::Store::Alias::Segment:
     file: lib/Bio/DB/SeqFeature/Store/Alias.pm
-    version: 0
   Bio::DB::SeqFeature::Store::BedLoader:
     file: lib/Bio/DB/SeqFeature/Store/BedLoader.pm
-    version: 0
+  Bio::DB::SeqFeature::Store::LoadHelper:
+    file: lib/Bio/DB/SeqFeature/Store/LoadHelper.pm
+    version: '1.10'
   Bio::DB::Tagger:
     file: lib/Bio/DB/Tagger.pm
-    version: 1.00
+    version: '1.00'
   Bio::DB::Tagger::Iterator:
     file: lib/Bio/DB/Tagger.pm
-    version: 0
   Bio::DB::Tagger::Tag:
     file: lib/Bio/DB/Tagger/Tag.pm
-    version: 0
   Bio::DB::Tagger::mysql:
     file: lib/Bio/DB/Tagger/mysql.pm
-    version: 0
   Bio::Graphics::Browser2:
     file: lib/Bio/Graphics/Browser2.pm
-    version: 2.54
+    version: '2.56'
   Bio::Graphics::Browser2::Action:
     file: lib/Bio/Graphics/Browser2/Action.pm
-    version: 0
   Bio::Graphics::Browser2::AdminTracks:
     file: lib/Bio/Graphics/Browser2/UserTracks.pm
-    version: 0
   Bio::Graphics::Browser2::AuthorizedFeatureFile:
     file: lib/Bio/Graphics/Browser2/AuthorizedFeatureFile.pm
-    version: 0
   Bio::Graphics::Browser2::CAlign:
     file: lib/Bio/Graphics/Browser2/CAlign.pm
-    version: 0
   Bio::Graphics::Browser2::CachedTrack:
     file: lib/Bio/Graphics/Browser2/CachedTrack.pm
-    version: 0
   Bio::Graphics::Browser2::DataBase:
     file: lib/Bio/Graphics/Browser2/DataBase.pm
-    version: 0
   Bio::Graphics::Browser2::DataLoader:
     file: lib/Bio/Graphics/Browser2/DataLoader.pm
-    version: 0
+  Bio::Graphics::Browser2::DataLoader::archive:
+    file: lib/Bio/Graphics/Browser2/DataLoader/archive.pm
   Bio::Graphics::Browser2::DataLoader::bam:
     file: lib/Bio/Graphics/Browser2/DataLoader/bam.pm
-    version: 0
   Bio::Graphics::Browser2::DataLoader::bed:
     file: lib/Bio/Graphics/Browser2/DataLoader/bed.pm
-    version: 0
+  Bio::Graphics::Browser2::DataLoader::bigbed:
+    file: lib/Bio/Graphics/Browser2/DataLoader/bigbed.pm
   Bio::Graphics::Browser2::DataLoader::bigwig:
     file: lib/Bio/Graphics/Browser2/DataLoader/bigwig.pm
-    version: 0
   Bio::Graphics::Browser2::DataLoader::featurefile:
     file: lib/Bio/Graphics/Browser2/DataLoader/featurefile.pm
-    version: 0
   Bio::Graphics::Browser2::DataLoader::generic:
     file: lib/Bio/Graphics/Browser2/DataLoader/generic.pm
-    version: 0
   Bio::Graphics::Browser2::DataLoader::gff:
     file: lib/Bio/Graphics/Browser2/DataLoader/gff.pm
-    version: 0
   Bio::Graphics::Browser2::DataLoader::gff3:
     file: lib/Bio/Graphics/Browser2/DataLoader/gff3.pm
-    version: 0
   Bio::Graphics::Browser2::DataLoader::sam:
     file: lib/Bio/Graphics/Browser2/DataLoader/sam.pm
-    version: 0
+  Bio::Graphics::Browser2::DataLoader::useq:
+    file: lib/Bio/Graphics/Browser2/DataLoader/useq.pm
   Bio::Graphics::Browser2::DataLoader::wig2bigwig:
     file: lib/Bio/Graphics/Browser2/DataLoader/wig2bigwig.pm
-    version: 0
   Bio::Graphics::Browser2::DataLoader::wiggle:
     file: lib/Bio/Graphics/Browser2/DataLoader/wiggle.pm
-    version: 0
   Bio::Graphics::Browser2::DataSource:
     file: lib/Bio/Graphics/Browser2/DataSource.pm
-    version: 0
   Bio::Graphics::Browser2::ExternalData:
     file: lib/Bio/Graphics/Browser2/ExternalData.pm
-    version: 0
   Bio::Graphics::Browser2::GFFhelper:
     file: lib/Bio/Graphics/Browser2/GFFhelper.pm
-    version: 0
   Bio::Graphics::Browser2::I18n:
     file: lib/Bio/Graphics/Browser2/I18n.pm
-    version: 0
   Bio::Graphics::Browser2::Markup:
     file: lib/Bio/Graphics/Browser2/Markup.pm
-    version: 0
   Bio::Graphics::Browser2::MetaDB:
     file: lib/Bio/Graphics/Browser2/RegionSearch.pm
-    version: 0
   Bio::Graphics::Browser2::MetaSegment:
     file: lib/Bio/Graphics/Browser2/RegionSearch.pm
-    version: 0
   Bio::Graphics::Browser2::MetaSegment::Iterator:
     file: lib/Bio/Graphics/Browser2/RegionSearch.pm
-    version: 0
   Bio::Graphics::Browser2::OptionPick:
     file: lib/Bio/Graphics/Browser2/OptionPick.pm
-    version: 0
   Bio::Graphics::Browser2::PadAlignment:
     file: lib/Bio/Graphics/Browser2/PadAlignment.pm
-    version: 0
   Bio::Graphics::Browser2::Plugin:
     file: lib/Bio/Graphics/Browser2/Plugin.pm
-    version: 0.30
+    version: '0.30'
   Bio::Graphics::Browser2::Plugin::AuthPlugin:
     file: lib/Bio/Graphics/Browser2/Plugin/AuthPlugin.pm
-    version: 0
   Bio::Graphics::Browser2::PluginSet:
     file: lib/Bio/Graphics/Browser2/PluginSet.pm
-    version: 0
   Bio::Graphics::Browser2::Realign:
     file: lib/Bio/Graphics/Browser2/Realign.pm
-    version: 0
   Bio::Graphics::Browser2::Region:
     file: lib/Bio/Graphics/Browser2/Region.pm
-    version: 0
   Bio::Graphics::Browser2::RegionSearch:
     file: lib/Bio/Graphics/Browser2/RegionSearch.pm
-    version: 0
   Bio::Graphics::Browser2::RemoteSet:
     file: lib/Bio/Graphics/Browser2/RemoteSet.pm
-    version: 0
   Bio::Graphics::Browser2::Render:
     file: lib/Bio/Graphics/Browser2/Render.pm
-    version: 0
   Bio::Graphics::Browser2::Render::HTML:
     file: lib/Bio/Graphics/Browser2/Render/HTML.pm
-    version: 0
   Bio::Graphics::Browser2::Render::HTML::TrackListing:
     file: lib/Bio/Graphics/Browser2/Render/HTML/TrackListing.pm
-    version: 0
   Bio::Graphics::Browser2::Render::HTML::TrackListing::Categories:
     file: lib/Bio/Graphics/Browser2/Render/HTML/TrackListing/Categories.pm
-    version: 0
   Bio::Graphics::Browser2::Render::Login:
     file: lib/Bio/Graphics/Browser2/Render/Login.pm
-    version: 0
   Bio::Graphics::Browser2::Render::Slave:
     file: lib/Bio/Graphics/Browser2/Render/Slave.pm
-    version: 0
+  Bio::Graphics::Browser2::Render::Slave::AWS_Balancer:
+    file: lib/Bio/Graphics/Browser2/Render/Slave/AWS_Balancer.pm
+  Bio::Graphics::Browser2::Render::Slave::StagingServer:
+    file: lib/Bio/Graphics/Browser2/Render/Slave/AWS_Balancer.pm
   Bio::Graphics::Browser2::Render::Slave::Status:
     file: lib/Bio/Graphics/Browser2/Render/Slave/Status.pm
-    version: 0
   Bio::Graphics::Browser2::Render::SnapshotManager:
     file: lib/Bio/Graphics/Browser2/Render/SnapshotManager.pm
-    version: 0
   Bio::Graphics::Browser2::Render::TrackConfig:
     file: lib/Bio/Graphics/Browser2/Render/TrackConfig.pm
-    version: 0
   Bio::Graphics::Browser2::RenderPanels:
     file: lib/Bio/Graphics/Browser2/RenderPanels.pm
-    version: 0
   Bio::Graphics::Browser2::SendMail:
     file: lib/Bio/Graphics/Browser2/SendMail.pm
-    version: 0
   Bio::Graphics::Browser2::Session:
     file: lib/Bio/Graphics/Browser2/Session.pm
-    version: 0
   Bio::Graphics::Browser2::Shellwords:
     file: lib/Bio/Graphics/Browser2/Shellwords.pm
-    version: 0
   Bio::Graphics::Browser2::SubtrackTable:
     file: lib/Bio/Graphics/Browser2/SubtrackTable.pm
-    version: 0
   Bio::Graphics::Browser2::TrackDumper:
     file: lib/Bio/Graphics/Browser2/TrackDumper.pm
-    version: 0
   Bio::Graphics::Browser2::TrackDumper::RichSeqMaker:
     file: lib/Bio/Graphics/Browser2/TrackDumper/RichSeqMaker.pm
-    version: 0
   Bio::Graphics::Browser2::UserConf:
     file: lib/Bio/Graphics/Browser2/UserTracks.pm
-    version: 0
   Bio::Graphics::Browser2::UserDB:
     file: lib/Bio/Graphics/Browser2/UserDB.pm
-    version: 0.5
+    version: '0.5'
   Bio::Graphics::Browser2::UserTracks:
     file: lib/Bio/Graphics/Browser2/UserTracks.pm
-    version: 0
   Bio::Graphics::Browser2::UserTracks::Database:
     file: lib/Bio/Graphics/Browser2/UserTracks/Database.pm
-    version: 0
   Bio::Graphics::Browser2::UserTracks::Filesystem:
     file: lib/Bio/Graphics/Browser2/UserTracks/Filesystem.pm
-    version: 0
   Bio::Graphics::Browser2::Util:
     file: lib/Bio/Graphics/Browser2/Util.pm
-    version: 0
   Bio::Graphics::GBrowseFeature:
     file: lib/Bio/Graphics/GBrowseFeature.pm
-    version: 0
   Bio::Graphics::Karyotype:
     file: lib/Bio/Graphics/Karyotype.pm
-    version: 0
   Bio::Graphics::Wiggle::Loader::Nosample:
     file: lib/Bio/Graphics/Browser2/DataLoader/wiggle.pm
-    version: 0
   CGI::Toggle:
     file: lib/CGI/Toggle.pm
-    version: 0
   FakeHomol:
     file: lib/Bio/Graphics/Browser2/GFFhelper.pm
-    version: 0
   LRUCache:
     file: lib/Bio/Graphics/Browser2/DataBase.pm
-    version: 0
   Legacy::DB::SyntenyBlock:
     file: lib/Legacy/DB/SyntenyBlock.pm
-    version: 0
   Legacy::DB::SyntenyIO:
     file: lib/Legacy/DB/SyntenyIO.pm
-    version: 0
   Legacy::Graphics::Browser:
     file: lib/Legacy/Graphics/Browser.pm
-    version: 1.17
+    version: '1.17'
   Legacy::Graphics::Browser::I18n:
     file: lib/Legacy/Graphics/Browser/I18n.pm
-    version: 1.01
+    version: '1.01'
   Legacy::Graphics::Browser::PageSettings:
     file: lib/Legacy/Graphics/Browser/PageSettings.pm
-    version: 0
   Legacy::Graphics::Browser::Synteny:
     file: lib/Legacy/Graphics/Browser/Synteny.pm
-    version: 0.01
+    version: '0.01'
   Legacy::Graphics::Browser::Util:
     file: lib/Legacy/Graphics/Browser/Util.pm
-    version: 0
   Legacy::Graphics::BrowserConfig:
     file: lib/Legacy/Graphics/Browser.pm
-    version: 0
   MyFeatureFileLoader:
     file: lib/Bio/Graphics/Browser2/DataLoader/featurefile.pm
-    version: 0
 recommends:
-  Bio::DB::BigFile: 1
-  Bio::DB::Sam: 1.36
-  Bio::Das: 0
-  Crypt::SSLeay: 0
-  DBD::Pg: 0
-  DBD::SQLite: 0
-  DBD::mysql: 0
-  DBI: 0
-  DB_File::Lock: 0
-  Digest::SHA: 0
-  FCGI: 0
-  File::NFSLock: 0
-  GD::SVG: 0
-  Math::BigInt: 0
-  Net::OpenID::Consumer: 0
-  Net::SMTP::SSL: 0
-  Parse::Apache::ServerStatus: 0
-  Template: 2.2
-  Term::ReadKey: 0
-  VM::EC2: 1.19
+  Bio::DB::BigFile: '1'
+  Bio::DB::Sam: '1.36'
+  Bio::Das: '0'
+  Crypt::SSLeay: '0'
+  DBD::Pg: '0'
+  DBD::SQLite: '0'
+  DBD::mysql: '0'
+  DBI: '0'
+  DB_File::Lock: '0'
+  Digest::SHA: '0'
+  FCGI: '0'
+  File::NFSLock: '0'
+  GD::SVG: '0'
+  Math::BigInt: '0'
+  Net::OpenID::Consumer: '0'
+  Net::SMTP::SSL: '0'
+  Parse::Apache::ServerStatus: '0'
+  Template: '2.2'
+  Term::ReadKey: '0'
+  VM::EC2: '1.19'
 requires:
-  Bio::Graphics: 2.31
-  Bio::Root::Version: 1.0069
-  CGI::Session: 4.02
-  Date::Parse: 0
-  Digest::MD5: 0
-  Digest::SHA: 0
-  ExtUtils::CBuilder: 0
-  File::Temp: 0
-  GD: 2.07
-  IO::String: 0
-  JSON: 0
-  LWP: 0
-  Statistics::Descriptive: 0
-  Storable: 0
-  Term::ReadKey: 0
-  Text::ParseWords: 3.27
-  Time::HiRes: 0
-  perl: 5.008
+  Bio::Graphics: '2.34'
+  Bio::Root::Version: '1.007001'
+  CGI::Session: '4.02'
+  Date::Parse: '0'
+  Digest::MD5: '0'
+  Digest::SHA: '0'
+  ExtUtils::CBuilder: '0'
+  File::Temp: '0'
+  GD: '2.50'
+  HTTP::Daemon: '0'
+  IO::String: '0'
+  JSON: '0'
+  LWP: '0'
+  Statistics::Descriptive: '0'
+  Storable: '0'
+  Term::ReadKey: '0'
+  Text::ParseWords: '3.27'
+  Time::HiRes: '0'
+  parent: '0'
+  perl: '5.008'
 resources:
   license: http://dev.perl.org/licenses/
-version: 2.54
+version: '2.56'
diff --git a/Makefile.PL b/Makefile.PL
index 5e91a08..fae41a3 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -1,4 +1,4 @@
-# Note: this file was auto-generated by Module::Build::Compat version 0.3800
+# Note: this file was auto-generated by Module::Build::Compat version 0.4216
 require 5.008;
 
     unless (eval "use Module::Build::Compat 0.02; 1" ) {
diff --git a/README b/README
index 64cd43e..be4acca 100644
--- a/README
+++ b/README
@@ -45,7 +45,7 @@ that. What follows here is a brief synopsis.
     Module                  Version
     ---------------------+----------
     perl                    5.008   
-    Bio::Perl               1.0069
+    Bio::Perl               1.007001
     CGI::Session            4.02    
     Bio::Graphics           2.09
     GD                      2.07    
@@ -74,7 +74,7 @@ that. What follows here is a brief synopsis.
   -or-
     DBD::SQLite             any
 
-- You should install BioPerl from CPAN (currently version 1.6.9) or, to
+- You should install BioPerl from CPAN (currently version 1.7.1) or, to
   take advantage of additional features and performance boosts, get a
   snapshot of the current developer version:
 
diff --git a/README.IF.GBROWSE.ISNT.WORKING b/README.IF.GBROWSE.ISNT.WORKING
index ae1bbdf..299b027 100644
--- a/README.IF.GBROWSE.ISNT.WORKING
+++ b/README.IF.GBROWSE.ISNT.WORKING
@@ -1,3 +1,10 @@
+==NOTE THIS FILE OUT OF DATE==
+
+This file appears out of data since the code is now in Github.
+
+
+==Old Message ==
+
 If you got an svn checkout of GBrowse and it isn't working, it is probably 
 because you checked out from the svn HEAD, which is the main development 
 branch.  Unfortunately, that branch is often unstable due to some 
diff --git a/bin/gbrowse_aws_balancer.pl b/bin/gbrowse_aws_balancer.pl
new file mode 100755
index 0000000..5131390
--- /dev/null
+++ b/bin/gbrowse_aws_balancer.pl
@@ -0,0 +1,562 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+gbrowse_aws_balancer.pl  Load balance GBrowse using Amazon Web Service instances
+
+=head1 SYNOPSIS
+
+Launch the balancer in the foreground
+
+ % gbrowse_aws_balancer.pl --conf         /etc/gbrowse2/aws_balancer.conf \
+                           --access_key   XYZZY \
+                           --secret_key   Plugh
+
+Launch the balancer in the background as a daemon:
+
+ % gbrowse_aws_balancer.pl --background \
+                           --conf         /etc/gbrowse2/aws_balancer.conf \
+                           --access_key   XYZZY \
+                           --secret_key   Plugh \
+                           --logfile      /var/log/gbrowse/aws_balancer.log \
+                           --pidfile      /var/run/aws_balancer.pid \
+                           --user         nobody
+
+Kill a running balancer daemon:
+
+ % gbrowse_aws_balancer.pl --kill \
+                           --conf         /etc/gbrowse2/aws_balancer.conf \
+                           --access_key   XYZZY \
+                           --secret_key   Plugh \
+                           --logfile      /var/log/gbrowse/aws_balancer.log \
+                           --pidfile      /var/run/aws_balancer.pid \
+                           --user         nobody
+
+Use the init script:
+
+ % sudo /etc/init.d/gbrowse-aws-balancer start
+ % sudo /etc/init.d/gbrowse-aws-balancer restart
+ % sudo /etc/init.d/gbrowse-aws-balancer stop
+ % sudo /etc/init.d/gbrowse-aws-balancer status
+
+Synchronize the master with the slave image:
+
+ % sudo gbrowse_sync_aws_slave.pl -c /etc/gbrowse2/aws_balancer.conf
+ syncing data....done
+ data stored in snapshot(s) snap-12345
+ updated conf file, previous version in /etc/gbrowse2/aws_balancer.conf.bak
+
+=head1 DESCRIPTION
+
+This script launches a process that monitors the load on the local
+GBrowse instance. If the load exceeds certain predefined levels, then
+it uses Amazon web services to launch one or more GBrowse slave
+instances.  The work of rendering tracks is then handed off to these
+instances, reducing the load on the local instance. Slave instances
+are implemented using Amazon's spot instance mechanism, which allows
+you to run EC2 instances at a fraction of the price of a standard
+on-demand instance.
+
+Load balancing is most convenient to run in conjunction with a GBrowse
+instance running within the Amazon Web Service EC2 cloud, but it can
+also be used to supplement an instance running on local hardware. The
+sections below describe the configuration needed for these two
+scenarios.
+
+Note that this script requires you to have an Amazon Web Services
+account, and for the VM::EC2 Perl module to be installed on the
+machine that is running this script.
+
+=head1 COMMAND-LINE OPTIONS
+
+Options can be abbreviated.  For example, you can use -a for
+--access_key:
+
+      --access_key   EC2 access key
+      --secret_key   EC2 secret key
+      --conf         Path to balancer configuration file
+      --pidfile      Path to file that holds daemon process ID
+      --logfile      Path to file that records log messages
+      --user         User to run daemon under (script must be
+                         started as root)
+      --verbosity    Logging verbosity. 0=least, 3=most.
+      --background   Go into the background and run as daemon.
+      --kill         Kill a previously-launched daemon. Must provide
+                         the same --pidfile argument as used when
+                         the daemon was started.
+      --ssh_key      Enable ssh login on the slave(s) using the specified
+                         AWS ssh keypair. Login will only be available
+                         from the host this script is run on.
+
+=head1 PREREQUISITES
+
+1. You must have the Perl modules VM::EC2 (v1.21 or later), and
+Parse::Apache::ServerStatus installed on the machine you intend to run
+the balancer on. The balancer must run on the same machine that
+GBrowse is running on. To install these modules, run:
+
+ perl -MCPAN -e 'install VM::EC2; install Parse::Apache::ServerStatus'
+
+2. You must have an account on Amazon Web Services and must be
+familiar with using the AWS Console to launch and terminate EC2
+instances. If you run GBrowse on local hardware, then you will need to
+provide the script with your access key and secret access key when
+launching it. It may be safer to create and use an IAM user (Identity
+and Access Management) who has more limited privileges. See
+L<CONFIGURATION> below for some suggestions.
+
+3. GBrowse must be running under Apache.
+
+4. Apache must be configured to enable the mod_status module and to
+allow password-less requests to this module from localhost
+(http://httpd.apache.org/docs/2.2/mod/mod_status.html). This is the
+recommended configuration:
+
+<IfModule mod_status.c>
+ ExtendedStatus on
+ <Location /server-status>
+    SetHandler server-status
+    Order deny,allow
+    Deny from all
+    Allow from 127.0.0.1 ::1
+ </Location>
+</IfModule>
+
+5. If you are running GBrowse on local hardware, the local hardware
+must be connected to the Internet or have a Virtual Private Cloud
+(VPC) connection to Amazon.
+
+=head1 THE CONFIGURATION FILE
+
+The balancer requires a configuration file, ordinarily named
+aws_balancer.conf and located in the GBrowse configuration directory
+(e.g. /etc/gbrowse2). The configuration file has three sections:
+
+=head2 [LOAD TABLE]
+
+This section describes the number of slave instances to launch for
+different load levels. It consists of a three-column space-delimited
+table with the following columns:
+
+ <requests/sec>    <min instances>    <max instances>
+
+For example, the first few rows of the default table reads:
+
+ 0.1     0   1
+ 0.5     0   2
+ 1.0     1   3
+ 2.0     2   4
+
+This is read as meaning that when the number of requests per second on
+the GBrowse server is greater than 0.1 but less than 0.5, run at least
+0 slave servers but no more than 1 slave server. When the number of
+requests is between 0.5 and 1.0, run between 0 and 2 slave
+instances. When the rate is between 1.0 and 2.0, run at least 1 slave
+instance, but no more than 3. Load levels below the lowest value on
+the table (0.1 in this case) will run no slave servers, while levels
+above the highest value on the table (2.0) will launch the minimum and
+maximum number of slaves for that load value (between 2 and 4 in this
+case).
+
+The reason for having a range of instance counts for each load range
+is to avoid unecessarily launching and killing slaves repeatedly when
+the load fluctuates around the boundary. You may wish to tune the
+values in this table to maximize the performance of your GBrowse
+installation.
+
+Note that the server load includes both GBrowse requests and all other
+requests on the web server. If this is a problem, you may wish to run
+GBrowse on a separate Apache port or virtual host.
+
+=head2 [MASTER]
+
+The options in this sections configure the master GBrowse
+instance. Three options are recognized:
+
+=over 4
+
+=item external_ip (optional)
+
+This controls the externally-visible IP address of the GBrowse master,
+which is needed by the firewall rule for master/slave
+communications. This option can usually be left blank: when the master
+is running on EC2, then the IP address is known; when the master is
+running on a local machine, the externally-visible IP address is
+looked up using a web service. It is only in the rare case that this
+lookup is incorrect that you will need to configure this option
+yourself.
+
+The external IP that the balancer script finds can be seen in a log
+message when verbosity is 2 or higher.
+
+=item poll_interval (required)
+
+This is the interval, in minutes, that the balancer script will
+periodically check the Apache load and adjust the number of slave
+instances. The suggested value is 0.5 (30s intervals).
+
+=item server_status_url (required)
+
+This is the URL to call to fetch the server load from Apache's
+server_status module.
+
+=back
+ 
+=head2 [SLAVE]
+
+The options in this section apply to the render slaves launched by the
+balancer.
+
+=over 4
+
+=item instance_type (required)
+
+This is the EC2 instance type. Faster instances give better
+performance. High-IO instances give the best performance, but cost
+more.
+
+=item spot_bid (required)
+
+This is the maximum, in US dollars, that you are willing to pay per
+hour to run a slave spot instance. Typically you will pay less than
+the bid price. If the spot price increases beyond the maximum bid,
+then the spot instances will be terminated and the balancer will wait
+until the spot price decreases below the maximum bid before launching
+additional slaves.
+
+=item ports (required)
+
+This is a space-delimited list of TCP port numbers on which the render
+slaves should listen for incoming render requests from the
+master. Generally it is only necessary to listen on a single port;
+multiple ports were supported for performance reasons in earlier
+single-threaded versions of the slave.
+
+=item region (required for local masters)
+
+The Amazon region in which to launch slaves. When the master is
+running in EC2, this is automatically chosen to be the same as the
+master's region and can be left blank.
+
+=item image_id (required for local masters)
+
+This is the ID of the AMI that will be used to launch slaves. The
+correct value will be filled in when you run the
+gbrowse_sync_aws_slave.pl. You can leave this value blank if the
+GBrowse master is being run within an EC2 instance, in which case the
+slave will be launched using the same AMI that was used to launch the
+master.
+
+=item data_snapshots (required for local masters)
+
+Before launching the slave, attach EBS volumes created from one or
+more volume snapshots listed in this option. Multiple snapshots can be
+attached by providing a space-delimited list:
+
+ data_snapshots = snap-12345 snap-abcdef
+
+The gbrowse_sync_aws_slave.pl script will automatically maintain this
+option for you.
+
+=item availability_zone (optional)
+
+This option will force the slave into the named availability zone. If
+not specified, an availability zone in the current region will be
+chosen at random.
+
+=item subnet (optional)
+
+If you are in a VPC environment, then this option will force the slave
+into the named subnet. Ordinarily the balancer script will launch
+slaves into non-VPC instances if the master is running on local
+hardware or a non-VPC EC2 instance. The balancer will launch slaves
+into the same VPC subnet as the master if the master is running on a
+VPC instance.
+
+=item security_group (optional)
+
+This specifies the security group to assign the slaves to. If not
+specified, a properly-configured security group will be created as
+needed and destroyed when the balancer script exits. If you choose to
+manage the security group manually, be sure to configure the firewall
+ingress rule to allow access to the slave port(s) (see the "ports"
+option) from the master's group or IP address.
+
+=back
+
+=head1 CONFIGURING AWS CREDENTIALS
+
+To work, the balancer script must be able to make spot instance
+requests and to monitor and terminate instances. To perform these
+operations the script must have access to the appropriate AWS
+credentials (access key and secret key) on the command line or as
+environment variables. 
+
+While the script does its best to shield the credentials from prying
+eyes, there is still a chance that the credentials can be intercepted
+by another party with login access to the machine that the master runs
+on and use the credentials to run up your AWS bill. For this reason
+some people will prefer to create an EC2 account or role with limited
+access to AWS resources.
+
+=over 4
+
+=item 1. Your personal EC2 credentials
+
+You may provide the balancer script with --access_key and --secret_key
+command line arguments using your personal EC2 credentials or set the
+environment variables EC2_ACCESS_KEY and EC2_SECRET_KEY. If not
+provided, the script will interactively prompt for one or both of
+these values.
+
+This is the simplest method, but has the risk that if the credentials
+are intercepted by a malicious third party, he or she gains access to
+all your EC2 resources.
+
+=item 2. The credentials of a restricted IAM account
+
+You may use the Amazon AWS console to create an IAM (Identity Access
+and Management) user with restricted permissions, and provide that
+user's credentials to the script on the command line or with
+environment variables. The following IAM permission policy is the
+minimum needed for the balancer script to work properly:
+
+ {
+  "Statement": [
+    {
+      "Sid": "BalancerPolicy",
+      "Action": [
+        "ec2:AuthorizeSecurityGroupEgress",
+        "ec2:AuthorizeSecurityGroupIngress",
+        "ec2:CreateSecurityGroup",
+        "ec2:DeleteSecurityGroup",
+        "ec2:DescribeAvailabilityZones",
+        "ec2:DescribeImageAttribute",
+        "ec2:DescribeImages",
+        "ec2:DescribeInstances",
+        "ec2:DescribeInstanceAttribute",
+        "ec2:DescribeInstanceStatus",
+        "ec2:DescribeSecurityGroups",
+        "ec2:DescribeVolumes",
+        "ec2:DescribeSnapshots",
+        "ec2:DescribeSpotInstanceRequests",
+        "ec2:RequestSpotInstances",
+        "ec2:CreateKeyPair",
+        "ec2:DescribeKeyPairs",
+        "ec2:DeleteKeyPair",
+        "ec2:RunInstances",
+        "ec2:TerminateInstances",
+        "ec2:CreateSnapshot",
+        "ec2:CreateVolume",
+        "ec2:CreateTags",
+        "ec2:DeleteTags"
+      ],
+      "Effect": "Allow",
+      "Resource": [
+        "*"
+      ]
+    }
+  ]
+ }
+
+Note that even with these restrictions, an unauthorized user with
+access to the credentials could still launch a large number of spot
+instances or terminate bona fide instances. This is just a fundamental
+limitation of the granularity of EC2's permissions system.
+
+=item 3. Create an IAM role
+
+If the master is running on an EC2 instance, then the most convenient
+way to pass credentials is by assigning the instance an IAM role. The
+balancer script can then obtain temporary credentials by making
+internal EC2 calls. The credentials do not need to be provided on the
+command line or in environment variables, and are only valid for short
+periods of time, limiting the effect of theft.
+
+First, create an IAM role using the Amazon Console. Select
+IAM->Roles->Create New Role, and give the role the name
+"GBrowseMaster" (or whatever you prefer).
+
+Next, when prompted for the role type, select AWS Service
+Roles->Amazon EC2.
+
+On the Select Role Permissions screen, choose "Custom Policy". Give
+the policy a name like "GBrowseBalancer" and cut and paste into the
+Policy Document text field the permission policy listed above in the
+instructions for creating a restriced IAM account. Be sure to remove
+the whitespace before the beginning of the first curly brace, or the
+console will complain about an invalid policy.
+
+You only need to do this once. After this, whenever you launch an
+instance that will run the GBrowse master (typically from a GBrowse
+AMI), specify the "GBrowseMaster" IAM role name. This can be done from
+the AWS console's instance launch wizard, or by passing the -p option
+to the ec2-run-instances command-line tool.
+
+=back
+
+=head1 USING THE INIT SCRIPT
+
+The gbrowse-aws-balancer init script can be used on Ubuntu and
+Debian-based systems to simplify launching the balancer at boot
+time. It can be found in /etc/init.d by default, and is called in the
+following manner:
+
+start the service
+ % sudo /etc/init.d/gbrowse-aws-balancer start
+
+stop the service
+ % sudo /etc/init.d/gbrowse-aws-balancer stop
+
+stop and restart the service
+ % sudo /etc/init.d/gbrowse-aws-balancer restart
+
+show the status of the service (running, stopped)
+ % sudo /etc/init.d/gbrowse-aws-balancer status
+
+The various script options are all set in a single configuration file
+named /etc/default/gbrowse-aws-balancer. The distribution contents of
+this file looks like this:
+
+ DAEMON=/usr/local/bin/gbrowse_aws_balancer.pl
+ USER=www-data
+ RUNDIR=/var/run/gbrowse
+ LOGDIR=/var/log/gbrowse
+ CONFFILE=/etc/gbrowse2/aws_balancer.conf
+ ACCESS_KEY=YOUR_EC2_ACCESS_KEY_HERE
+ SECRET_KEY=YOUR_EC2_SECRET_KEY_HERE
+ VERBOSITY=3
+
+The variables in this file set the location of the balancer script,
+the location of its configuration file, the verbosity to run with, and
+where to write the script's process ID and log information. In
+addition, you can place your (or another authorized user's) EC2 access
+and secret key in this file. Please make sure that this file is only
+readable by root.
+
+=head1 DEBUGGING SLAVE PROBLEMS
+
+If slaves are returning track renderinge errors, then there is likely
+an issue with data synchronization. This typically happens when the
+data on the master differs from the data on the slave, or path names
+are different on the two systems.
+
+To debug this, launch the script with the -ssh_key option:
+
+  % gbrowse_aws_balancer.pl --conf         /etc/gbrowse2/aws_balancer.conf \
+                           --access_key   XYZZY \
+                           --secret_key   Plugh \
+                           --ssh_key      John_Doe_default
+
+You may then ssh into the slave using the specified ssh key and the
+username "admin". A useful thing to do is to tail the slave log file:
+
+ ssh -i .ssh/John_Doe_default admin at 54.280.19.203 \
+        tail -f /var/log/gbrowse/gbrowse_slave
+
+Replace the IP number with the correct IP number of one of the running
+slaves, which you can find in /etc/gbrowse2/renderfarm.conf.
+
+=head1 THE GBROWSE_SYNC_AWS_SLAVE.PL SCRIPT
+
+The gbrowse_sync_aws_script.pl script should be run on the master each
+time you add a new database to an existing data source, or if you add
+a whole new data source. What it does is to prepare a new Amazon EBS
+snapshot containing a copy of all the data needed for the GBrowse
+slave to run. This snapshot is then attached to new slave instances.
+
+After running, it updates the conf file with the current versions of
+the slave AMI and the data snapshot.
+
+ % sudo gbrowse_sync_aws_script.pl --conf     /etc/gbrowse2/aws_balancer.conf \
+                                   --mysql    /var/lib/mysql \
+                                   --postgres /var/lib/postgresql
+
+The --conf argument is required. The script will create a snapshot of
+the appropriate size, mount it on a temporary staging instance, and
+rsync a copy of your gbrowse databases directory
+(e.g. /var/lib/gbrowse2/databases) to the snapshot. If you have
+created mysql or postgres databases, you must also give the paths to
+their database file directories, as shown in the example.
+
+Note that ALL your mysql and postgres data files located on the server
+will be copied; not just those used for track display.
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables are used if the corresponding
+command line options are not present:
+
+ EC2_ACCESS_KEY     AWS EC2 access key
+ EC2_SECRET_KEY     AWS EC2 secret key
+
+=head1 SEE ALSO
+
+L<VM::EC2>, L<VM::EC2::Staging::Manager>
+
+=head1 AUTHOR
+
+Lincoln Stein, lincoln.stein at gmail.com
+
+Copyright (c) 2012 Ontario Institute for Cancer Research
+                                                                                
+This package and its accompanying libraries is free software; you can
+redistribute it and/or modify it under the terms of the GPL (either
+version 1, or at your option, any later version) or the Artistic
+License 2.0.  Refer to LICENSE for the full license text. In addition,
+please see DISCLAIMER.txt for disclaimers of warranty.
+
+=cut
+
+use strict;
+use Getopt::Long;
+use GBrowse::ConfigData;
+use File::Spec;
+use Bio::Graphics::Browser2::Render::Slave::AWS_Balancer;
+
+my $balancer;
+
+# this obscures the AWS secrets from ps; it is not 100% effective
+my $program = $0;
+($0 = "$0 @ARGV") =~ s/(\s--?[as]\S*?)(=|\s+)\S+/$1$2xxxxxxxxxx/g;
+
+$SIG{TERM} = sub {exit 0};
+$SIG{INT}  = sub {exit 0};
+
+my($ConfFile,$AccessKey,$SecretKey,$PidFile,$LogFile,$Daemon,$User,$Verbosity,$Kill,$SshKey);
+GetOptions(
+    'access_key=s'  => \$AccessKey,
+    'secret_key=s'  => \$SecretKey,
+    'conf=s'        => \$ConfFile,
+    'pidfile=s'     => \$PidFile,
+    'logfile=s'     => \$LogFile,
+    'user=s'        => \$User,
+    'verbosity=i'   => \$Verbosity,
+    'kill'          => \$Kill,
+    'background'    => \$Daemon,
+    'ssh_key'       => \$SshKey,
+    ) or exec 'perldoc',$program;
+
+$ConfFile  ||= File::Spec->catfile(GBrowse::ConfigData->config('conf'),'aws_balancer.conf');
+
+$balancer = Bio::Graphics::Browser2::Render::Slave::AWS_Balancer->new(
+    -conf       => $ConfFile,
+    -access_key => $AccessKey||'',
+    -secret_key => $SecretKey||'',
+    -logfile    => $LogFile||'',
+    -pidfile    => $PidFile||'',
+    -user       => $User||'',
+    -daemon     => $Daemon||0,
+    -ssh_key    => $SshKey||undef,
+    );
+
+$Verbosity = 3 unless defined $Verbosity;
+$balancer->verbosity($Verbosity);
+if ($Kill) {
+    $balancer->stop_daemon();
+} else {
+    $balancer->run();
+}
+
+exit 0;
+
diff --git a/bin/gbrowse_configure_slaves.pl b/bin/gbrowse_configure_slaves.pl
index 596afb5..bdc8363 100755
--- a/bin/gbrowse_configure_slaves.pl
+++ b/bin/gbrowse_configure_slaves.pl
@@ -67,7 +67,7 @@ if ($changed) {
     $use_renderfarm = keys %remote_renderers > 0 ? 1 : 0;
 
     open my $f,'>',"$render_conf.new" or die "Couldn't open $conf.new: $!";
-    print $f "[GENERAl]\n";
+    print $f "[GENERAL]\n";
     print $f "renderfarm = $use_renderfarm\n";
     print $f "remote renderer = \n";
     for my $s (keys %remote_renderers) {
diff --git a/bin/gbrowse_launch_aws_slaves.pl b/bin/gbrowse_launch_aws_slaves.pl
deleted file mode 100755
index 5d88cd5..0000000
--- a/bin/gbrowse_launch_aws_slaves.pl
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/usr/bin/perl
-
-# Script to launch additional render slaves when running under Amazon AWS
-# Will monitor the load level and launch a graded series of spot instances
-# to deal with it.
-#
-# All values are hard-coded as constants during this testing phase
-#
-# Need following security groups:
-# GBrowseMaster
-#   allow inbound on 22 from all
-#   allow inbound on 80 from all
-#
-# GBrowseSlave
-#   allow inbound on 8101-8105 from GBrowseMaster group
-#   (nothing else)
-#
-# Master server must be configured to allow http://localhost/server-status requests
-# from localhost. The "Satisfy any" step ensures that no password will
-# be required on this URL.
-#
-#<Location /server-status>
-#    SetHandler server-status
-#    Order deny,allow
-#    Deny from all
-#    Allow from 127.0.0.1
-#    Satisfy any
-#</Location>
-# ExtendedStatus On
-#
-
-
-use strict;
-use Getopt::Long;
-use Parse::Apache::ServerStatus;
-use FindBin '$Bin';
-use VM::EC2;
-use VM::EC2::Instance::Metadata;
-use Parse::Apache::ServerStatus;
-
-$SIG{TERM} = sub {exit 0};
-$SIG{INT}  = sub {exit 0};
-END {  terminate_instances()  }
-
-# load averages:
-# each item represents requests per second, lower and upper bounds
-use constant LOAD_TABLE => [
-    #load  min  max
-    [ 0.01,  0,   1 ],
-    [ 0.5,   0,   2 ],
-    [ 1.0,   1,   4 ],
-    [ 5.0,   3,   6 ],
-    [ 10.0,  6,   8 ]
-    ];
-
-use constant IMAGE_TYPE       => 'm1.large';
-use constant POLL_INTERVAL    => 0.5;  # minutes
-use constant SPOT_PRICE       => 0.08;  # dollars/hour
-use constant SECURITY_GROUP   => 'GBrowseSlave';
-use constant CONFIGURE_SLAVES => "$Bin/gbrowse_configure_slaves.pl";
-use constant SERVER_STATUS    => 'http://localhost/server-status';
-
-my($Access_key,$Secret_key);
-GetOptions(
-	   'access_key=s'  => \$Access_key,
-	   'secret_key=s'  => \$Secret_key,
-    ) or exec 'perldoc',$0;
-
-#setup defaults
-$ENV{EC2_ACCESS_KEY} = $Access_key if defined $Access_key;
-$ENV{EC2_SECRET_KEY} = $Secret_key if defined $Secret_key;
-
-my $meta       = VM::EC2::Instance::Metadata->new();
-my $imageId    = $meta->imageId;
-my $instanceId = $meta->instanceId;
-my $zone       = $meta->availabilityZone;
-my $subnet     = eval {(values %{$meta->interfaces})[0]{subnetId}};
-my $vpcId      = eval {(values %{$meta->interfaces})[0]{vpcId}};
-my @groups     = $meta->securityGroups;
-
-die "This instance needs to belong to the GBrowseMaster security group in order for this script to run correctly"
-    unless "@groups" =~ /GBrowseMaster/;
-
-warn "slave imageId=$imageId, zone=$zone, subnet=$subnet, vpcId=$vpcId\n";
-
-(my $region = $zone)       =~ s/[a-z]$//;  #  zone=>region
-my $ec2     = VM::EC2->new(-region=>$region);
-
-my (@slave_security_groups) = $ec2->describe_security_groups({'group-name' => SECURITY_GROUP});
-my $slave_security_group;
-if ($vpcId) {
-    ($slave_security_group)  = grep {$vpcId eq $_->vpcId} @slave_security_groups; 
-} else {
-    $slave_security_group = $slave_security_groups[0];
-}
-
-$slave_security_group or die "Could not find a security group named ",SECURITY_GROUP," in current region or VPC";
-
-my $pr      = Parse::Apache::ServerStatus->new(url=>SERVER_STATUS);
-
-while (1) { # main loop
-    my $load = get_load();
-    warn "current load = $load\n";
-    my @instances = adjust_spot_requests($load);
-    adjust_configuration(@instances);
-    sleep (POLL_INTERVAL * 60);
-}
-
-terminate_instances();
-
-exit 0;
-
-sub get_load {
-    if (-e '/tmp/gbrowse_load') {
-	open my $fh,'/tmp/gbrowse_load';
-	chomp (my $load = <$fh>);
-	return $load;
-    }
-    my $stats = $pr->get or die $pr->errstr;
-    return $stats->{rs};
-}
-
-sub adjust_spot_requests {
-    my $load = shift;
-
-    # first find out how many spot instances we want to have
-    my ($min_instances,$max_instances) = (0,0);
-    my $lt = LOAD_TABLE;
-    for my $i (@$lt) {
-	my ($load_limit,$min,$max) = @$i;
-	if ($load > $load_limit) {
-	    $min_instances = $min;
-	    $max_instances = $max;
-	}
-    }
-
-    warn "load=$load: min=$min_instances, max=$max_instances\n";
-
-    # count the realized and pending 
-    my @spot_requests = $ec2->describe_spot_instance_requests({'tag:Requestor' => 'gbrowse_launch_aws_slaves'});
-    my @potential_instances;
-    for my $sr (@spot_requests) {
-	my $state    = $sr->state;
-	my $instance = $sr->instance;
-	if ($state eq 'open' or ($instance && $instance->instanceState =~ /running|pending/)) {
-	    $instance->add_tag(Name => 'GBrowse Slave')      if $instance;
-	    $instance->add_tag(GBrowseMaster => $instanceId) if $instance;  # we'll use this to terminate all slaves sometime later
-	    push @potential_instances,$instance || $sr;
-	}
-    }
-
-    warn "current active and pending spot instances = ",scalar @potential_instances;
-    
-    # what to do if there are too many spot requests for the current load
-    # either cancel spot requests or shut instances down
-    while (@potential_instances > $max_instances) {
-	my $i = shift @potential_instances;
-	if ($i->isa('VM::EC2::Instance')) {
-	    warn "terminating $i";
-	    $i->terminate();
-	} else {
-	    warn "cancelling spot request $i";
-	    $ec2->cancel_spot_instance_requests($i);
-	}
-    }
-
-    # what to do if there are too few
-    if (@potential_instances < $min_instances) {
-	warn "launching a new spot request";
-	my @requests = $ec2->request_spot_instances(
-	    -image_id             => $imageId,
-	    -instance_type        => IMAGE_TYPE,
-	    -instance_count       => 1,
-	    -security_group_id    => $slave_security_group,
-	    -spot_price           => SPOT_PRICE,
-	    $subnet? (-subnet_id  => $subnet) : (),
-	    -user_data         => "#!/bin/sh\nexec /opt/gbrowse/etc/init.d/gbrowse-slave start",
-	    );
-	@requests or warn $ec2->error_str;
-	$_->add_tag(Requestor=>'gbrowse_launch_aws_slaves') foreach @requests;
-	push @potential_instances, at requests;
-    }
-    return @potential_instances;
-}
-
-sub adjust_configuration {
-    # this is a heterogeneous list of running instances and spot instance requests
-    my @potential_instances = @_;
-    warn "adjust_configuration(@potential_instances)";
-
-    my @instances = grep {$_->isa('VM::EC2::Instance')} @potential_instances;
-    if (@instances) {
-	my @addresses = grep {$_} map  {$_->privateDnsName||$_->privateIpAddress}    @instances;
-	return unless @addresses;
-	warn "Adding slaves at address @addresses";
-	my @a         = map {("http://$_:8101",
-			      "http://$_:8102",
-			      "http://$_:8103")} @addresses;
-	my @args      = map  {('--set'=> "$_") } @a;
-	system 'sudo',CONFIGURE_SLAVES, at args;
-    } else {
-	system 'sudo',CONFIGURE_SLAVES,'--set','';
-    }
-}
-
-sub terminate_instances {
-    $ec2 or return;
-    warn "terminating all slave instances";
-    my @spot_requests = $ec2->describe_spot_instance_requests({'tag:Requestor' => 'gbrowse_launch_aws_slaves'});
-    my @instances     = $ec2->describe_instances({'tag:GBrowseMaster'=>$instanceId});
-    my %to_terminate = map {$_=>1} @instances;
-    foreach (@spot_requests) {
-	$to_terminate{$_->instance}++;
-	$ec2->cancel_spot_instance_requests($_);
-    }
-    my @i = grep {/^i-/} keys %to_terminate;
-    warn "instances to terminate = @i";
-    $ec2->terminate_instances(@i);
-    system 'sudo',CONFIGURE_SLAVES,'--set','';
-}
-
-END { terminate_instances() }
diff --git a/bin/gbrowse_syn_load_alignments_msa.pl b/bin/gbrowse_syn_load_alignments_msa.pl
index ca15882..432299c 100755
--- a/bin/gbrowse_syn_load_alignments_msa.pl
+++ b/bin/gbrowse_syn_load_alignments_msa.pl
@@ -1,9 +1,8 @@
 #!/usr/bin/perl -w
-# $Id: load_alignments_msa.pl,v 1.1.2.2 2009-07-19 09:15:43 sheldon_mckay Exp $
 # This script will load the gbrowse_syn alignment database directly from a
 # multiple sequence alignment file.
 BEGIN {
-  #Check for DBI before running the script
+  # Check for DBI before running the script
   # Doing this here will allow the "compile" tests to pass for GBrowse
   # even if DBI is not installed.
   eval {
@@ -21,7 +20,6 @@ use strict;
 use Bio::AlignIO;
 use List::Util 'sum';
 use Getopt::Long;
-#use DBI;
 use Bio::DB::GFF::Util::Binning 'bin';
 
 use Data::Dumper;
@@ -72,8 +70,9 @@ while (my $infile = shift) {
       my $seqid = $seq->id;
       my ($species,$ref,$strand) = check_name_format($seqid,$seq);
       next if $seq->seq =~ /^-+$/;
+      $strand ||= $seq->start < $seq->end ? '+' : '-';
       # We have to tell the sequence object what its strand is
-      $seq->strand($strand eq '-' ? -1 : 1);
+      $seq->strand($strand eq '-' ? -1 : 1) unless $seq->strand;
       $seq{$species} = [$ref, $seq->display_name, $seq->start, $seq->end, $strand, $seq->seq, $seq]; 
     }
     
@@ -202,31 +201,34 @@ sub check_name_format {
 
   my $nogood = <<"  END";
 
-I am sorry, I do not like the sequence name: $name
+  Problem with sequence name $name
+  The Sequence name needs to contain some meta-data to identify
+      the species, reference sequence and coordinates.
 
-This will not work unless you use the name format described below for each
-sequence in the alignment.  
 
-We need the species, sequence name, strand, start and end for
-each sequence in the alignment.
+  Supported Sequence Name formats:
 
-  Name format:
-    species-sequence(strand)/start-end
-  
-    where species   = name of species, genome, strain, etc (string with no '-' characters)
-          sequence  = name of reference sequence (string with no '/' characters)
-          (strand)  = orientation of the alignment (relative to the reference sequence; + or -)
-          start     = start coordinate of the alignment relative to the reference sequence (integer)
-          end       = end coordinate of the alignment relative to the reference sequence   (integer)
+  # Downloaded via Ensembl Compara API
+  species/seqid/start-end
+      where species   = name of species, genome, strain, etc (string with no '-' characters)
+            sequence  = name of reference sequence (string with no '/' characters)
+            start     = start coordinate of the alignment relative to the reference sequence (integer)
+            end       = end coordinate of the alignment relative to the reference sequence   (integer)
+            in this format, the strand is + unless end < start
+
+  # Legacy gbrowse_syn format
+  species-seqid(strand)/start..end
+      where (strand)  = orientation of the alignment (relative to the reference sequence; + or -)
 
   Examples:
+    homo_sapiens/1/100000-200000
     c_elegans-I(+)/1..2300
-    myco_bovis-chr1(-)/15000..25000
 
   END
   ;
 
-  die $nogood unless $name =~ /^([^-]+)-([^\(]+)\(([+-])\)$/;
+  die $nogood unless $name =~ /^([^-]+)-([^\(]+)\(([+-])\)$/  # Why did I do this?
+              ||     $name =~ m!^([^/]+)/([^/]+)!;            # from Bio::LocatableSeq
   die $nogood unless $seq->start && $seq->end;
   return ($1,$2,$3);
 }
diff --git a/bin/gbrowse_sync_aws_slave.pl b/bin/gbrowse_sync_aws_slave.pl
new file mode 100755
index 0000000..81923c3
--- /dev/null
+++ b/bin/gbrowse_sync_aws_slave.pl
@@ -0,0 +1,176 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+gbrowse_sync_aws_slave.pl  Synchronize local file system to GBrowse slave volume.
+
+=head1 SYNOPSIS
+
+ % sudo gbrowse_sync_aws_slave.pl --conf     /etc/gbrowse2/aws_balancer.conf \
+                                  --mysql    /var/lib/mysql \
+                                  --postgres /var/lib/postgresql
+
+ syncing data....done
+ data stored in snapshot(s) snap-12345
+ updated conf file, previous version in /etc/gbrowse2/aws_balancer.conf.bak
+
+=head1 DESCRIPTION
+
+This script is run in conjunction with Amazon Web Server-based GBrowse
+render slave load balancing, which is described in more detail in the
+manual page for gbrowse_aws_balancer.pl.
+
+The gbrowse_sync_aws_script.pl script should be run on the GBrowse
+master machine each time you add a new database to an existing data
+source, or if you add a whole new data source. What it does is to
+prepare a new Amazon EBS snapshot containing a copy of all the data
+needed for the GBrowse slave to run. This snapshot is then attached to
+new slave instances.
+
+After running, it updates the conf file with the current versions of
+the slave AMI and the data snapshot(s).
+
+ % sudo gbrowse_sync_aws_script.pl --conf     /etc/gbrowse2/aws_balancer.conf \
+                                   --mysql    /var/lib/mysql \
+                                   --postgres /var/lib/postgresql
+
+The --conf argument is required. The script will create a snapshot of
+the appropriate size, mount it on a temporary staging instance, and
+rsync a copy of your gbrowse databases directory
+(e.g. /var/lib/gbrowse2/databases) to the snapshot. If you have
+created mysql or postgres databases, you must also give the paths to
+their database file directories, as shown in the example.
+
+Note that ALL your mysql and postgres data files located on the master
+machine will be copied; not just those used for track display.
+
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables are used if the corresponding
+command line options are not present:
+
+ EC2_ACCESS_KEY AWS EC2 access key EC2_SECRET_KEY AWS EC2 secret key
+
+=head1 SEE ALSO
+
+L<VM::EC2>, L<VM::EC2::Staging::Manager>
+
+=head1 AUTHOR
+
+Lincoln Stein, lincoln.stein at gmail.com
+
+Copyright (c) 2013 Ontario Institute for Cancer Research
+                                                                                
+This package and its accompanying libraries is free software; you can
+redistribute it and/or modify it under the terms of the GPL (either
+version 1, or at your option, any later version) or the Artistic
+License 2.0.  Refer to LICENSE for the full license text. In addition,
+please see DISCLAIMER.txt for disclaimers of warranty.
+
+=cut
+
+use strict;
+use FindBin '$Bin';
+use lib "$Bin/../lib";
+use lib '/home/lstein/projects/LibVM-EC2-Perl/lib';
+
+use Getopt::Long;
+use GBrowse::ConfigData;
+use File::Spec;
+use Bio::Graphics::Browser2::Render::Slave::AWS_Balancer;
+
+use constant GB => 1_073_741_824;
+use constant DEBUG => 0;
+
+my ($balancer,$slave);
+my $program = $0;
+
+# this obscures the AWS secrets from ps; it is not 100% effective
+($0 = "$program @ARGV") =~ s/(\s--?[as]\S*?)(=|\s+)\S+/$1$2xxxxxxxxxx/g;
+
+$SIG{TERM} = sub {exit 0};
+$SIG{INT}  = sub {exit 0};
+
+my($ConfFile,$AccessKey,$SecretKey,$MySqlPath,$PostGresPath,$Verbosity);
+GetOptions(
+	   'access_key=s'  => \$AccessKey,
+	   'secret_key=s'  => \$SecretKey,
+	   'conf=s'        => \$ConfFile,
+	   'mysql=s'       => \$MySqlPath,
+	   'postgres=s'    => \$PostGresPath,
+           'verbosity=i'   => \$Verbosity,
+    ) or exec 'perldoc',$program;
+
+$ConfFile  ||= File::Spec->catfile(GBrowse::ConfigData->config('conf'),'aws_balancer.conf');
+unless (DEBUG || $< == 0) {
+    my @argv;
+    push @argv,('--access_key'=>$AccessKey)                 if $AccessKey;
+    push @argv,('--secret_key'=>$SecretKey)                 if $SecretKey;
+    push @argv,('--conf'      =>File::Spec->rel2abs($ConfFile))     if $ConfFile;
+    push @argv,('--mysql'     =>File::Spec->rel2abs($MySqlPath))    if $MySqlPath;
+    push @argv,('--postgres'  =>File::Spec->rel2abs($PostGresPath)) if $PostGresPath;
+    push @argv,('--verbosity' =>$Verbosity)                 if defined $Verbosity;
+    $program = File::Spec->rel2abs($program);
+
+    print STDERR <<END;
+This script needs root privileges in order to access all directories
+needed for synchronization.  It will now invoke sudo to become the
+'root' user temporarily.  You may be prompted for your login password
+now.
+END
+;
+
+    exec 'sudo','-E','-u','root',$program, at argv;
+}
+
+$balancer = Bio::Graphics::Browser2::Render::Slave::AWS_Balancer->new(
+    -conf       => $ConfFile,
+    -access_key => $AccessKey||'',
+    -secret_key => $SecretKey||'',
+    );
+
+$Verbosity = 3 unless defined $Verbosity;
+$balancer->verbosity($Verbosity);
+
+# run the remote staging server
+print STDERR "[info] Launching a slave server for staging...\n";
+$slave = $balancer->launch_staging_server();
+
+$slave->shell if DEBUG;
+$slave->stop_services();
+
+# figure out total size needed on destination volume
+my $DataBasePath = GBrowse::ConfigData->config('databases');
+
+my $gig_needed = tally_sizes($DataBasePath,$MySqlPath,$PostGresPath);  # keep an extra 10 G free
+my $gig_have   = $slave->volume_size;
+ if ($gig_needed + 5 > $gig_have) {
+     $gig_needed = $gig_have + 10;  # grow by 10 G increments
+     $slave->info("Increasing size of slave data volume...\n");
+     $slave->grow_volume($gig_needed);
+}
+
+$slave->info("Syncing files...\n");
+$slave->put("$DataBasePath/",'/opt/gbrowse/databases');
+$slave->put("$MySqlPath/",   '/opt/gbrowse/lib/mysql')       if $MySqlPath;
+$slave->put("$PostGresPath/",'/opt/gbrowse/lib/postgresql')  if $PostGresPath;
+my @snapshots = $slave->snapshot_data_volumes;
+$balancer->update_data_snapshots(@snapshots);
+$slave->info("Updating $ConfFile.\n");
+$slave->info("Synchronization done. New data is in snapshot(s) @snapshots.\n");
+
+exit 0;
+
+sub tally_sizes {
+    my @dirs  = @_;
+    my $out   = `sudo du -scb @dirs`;
+    my ($bytes) = $out=~/^(\d+)\s+total/m;
+    return int($bytes/GB)+1;
+}
+
+END {
+    if ($slave) { $slave->terminate }
+    undef $slave;
+}
+
+__END__
diff --git a/bin/split_wig.pl b/bin/split_wig.pl
new file mode 100755
index 0000000..eb4c4ab
--- /dev/null
+++ b/bin/split_wig.pl
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+use strict;
+use warnings;
+use File::Temp qw/tempdir/;
+use Getopt::Long;
+
+##
+##  Splits a wig file (variable or fixed step format) in different wig files with a maximum of 900 scaffolds/each
+##  and runs the wiggel2gff.pl script to upload these files to GBrowse2.
+##  Usage:  split_wig.pl -w FILE.wig -p DATABASE_PATH
+##  The whole path is needed, since the gff files will point to their respective wib files.
+##  After running this script, you can run it again for a different wig file, and all gff files will be pooled
+##  together in the same folder. To upload the data to GBrowse2, the MySQL Backend is recommended:
+##  bp_seqfeature_load.pl -f -a DBI::mysql -d DATABASE gff3_files/*.gff3
+##  The data track should be configured in your DATABASE.conf file, setting the 'feature' field with the name of
+##  your original wig file (without extension).
+##
+##  Juan J. Tena, CABD 2013
+##  jjtenagu at upo.es
+##
+
+my ($wig,$path)=('','');
+GetOptions
+(
+    "w=s" => \$wig,
+    "p=s" => \$path,
+);
+
+
+if (!$wig || !$path) {die "Usage: split_wig.pl -w FILE.wig -p DATABASE_PATH\n";}
+
+mkdir "$path/wib_files";
+mkdir "$path/gff3_files";
+
+my $count=0;
+my $chr_old='';
+my $dir=tempdir(CLEANUP => 1);
+my $out=File::Temp->new(DIR => $dir, UNLINK => 0, SUFFIX => '.dat');
+my $header=`head -n 1 $wig`;
+open IN, $wig or die "Cannot open $wig: $!\n";
+while (<IN>) {
+    my $line=$_;
+    chomp $line;
+    if ($line=~/chrom/) {
+        if ($count>=900) {
+            $out=File::Temp->new(DIR => $dir, UNLINK => 0, SUFFIX => '.dat');
+            print $out $header;
+            $count=0;
+        }
+        my @fields=split /\s/,$line;
+        my $chr=$fields[1];
+        $chr=~s/chrom=//;
+        if ($chr ne $chr_old) {
+            $count++;            
+        }
+        $chr_old=$chr;
+    }
+    print $out "$line\n";
+}
+close IN;
+
+my @files=<$dir/*.dat>;
+my @filepath=split /\//,$wig;
+my @filename=split /\./,$filepath[-1];
+my $suf=1;
+foreach (@files) {
+    my $tmpout=File::Temp->new();
+    my $outfile="$path/gff3_files/$filename[0]_$suf.gff3";
+    system("wiggle2gff3.pl --path=$path/wib_files $_ > $tmpout");
+    system ("sed 's/microarray_oligo/$filename[0]/' $tmpout > $outfile");
+    $suf++;
+}
+
+exit;
diff --git a/bin/wiggle2gff3.pl b/bin/wiggle2gff3.pl
index 2735dda..87c7625 100755
--- a/bin/wiggle2gff3.pl
+++ b/bin/wiggle2gff3.pl
@@ -155,6 +155,14 @@ The output will look like this:
  chr19	example	example	59304701	59308020	.	.	.	Name=variableStep;wigfile=/var/gbrowse/db/track002.chr19.1199828298.wig
  chr19	example	example	59307401	59310400	.	.	.	Name=fixedStep;wigfile=/var/gbrowse/db/track003.chr19.1199828298.wig
 
+=head1 PROBLEMS
+
+This script has trouble with wig files from very fragmented genomes
+(>100K scaffolds). In this case, you may wish to run split_wig.pl,
+which splits the original wig file into a series of smaller files with
+a maximum of 900 scaffolds each. It then runs wiggle2gff3.pl for each
+subfile and stores the results in separate folders.
+
 =head1 SEE ALSO
 
 L<Bio::DB::GFF>, L<bp_bulk_load_gff.pl>, L<bp_fast_load_gff.pl>,
diff --git a/cgi-bin/das b/cgi-bin/das
old mode 100755
new mode 100644
index 415b90f..4819108
--- a/cgi-bin/das
+++ b/cgi-bin/das
@@ -6,6 +6,12 @@ eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
 eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
     if 0; # not running under some shell
 
+eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
+eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
 use strict;
 use warnings;
 
diff --git a/cgi-bin/gbgff b/cgi-bin/gbgff
old mode 100755
new mode 100644
index ed2cc86..1f467e4
--- a/cgi-bin/gbgff
+++ b/cgi-bin/gbgff
@@ -2,6 +2,12 @@
 
 eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
     if 0; # not running under some shell
+
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
 # $Id: gbgff,v 1.2 2009-08-27 19:13:18 idavies Exp $
 
 # just redirect to gbrowse in same directory
diff --git a/cgi-bin/gbrowse b/cgi-bin/gbrowse
old mode 100755
new mode 100644
index 18e1730..89843b5
--- a/cgi-bin/gbrowse
+++ b/cgi-bin/gbrowse
@@ -1,7 +1,4 @@
-#!/usr/bin/perl 
-
-eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
-    if 0; # not running under some shell
+#!/usr/bin/perl
 
 use strict;
 use CGI;
@@ -23,11 +20,24 @@ if ($fcgi) {
     };
 
     my %sys_env = %ENV;
+
+    warn "[$$] FastCGI initializing and loading default data sources...\n";
+    my $globals = Bio::Graphics::Browser2->open_globals;
+    if($globals->preload_datasources) {
+      foreach ($globals->data_sources) {
+        warn "[$$] Loading $_\n";
+        my $source = $globals->create_data_source($_);
+        $source->open_database();  # cache default database in memory
+      }
+    }
+    else {
+      warn "[$$] Preload disabled";
+    }
+
     while (!$FCGI_DONE) {
 	my $status = $fcgi->Accept;
 	next unless $status >= 0;
         %ENV = (%sys_env, %ENV);
-	my $globals = Bio::Graphics::Browser2->open_globals;
 	CGI->initialize_globals();
 	my $render = Bio::Graphics::Browser2::Render::HTML->new($globals);
 	eval {
@@ -42,7 +52,7 @@ if ($fcgi) {
     kill TERM => -$pgrp;
 
 } else {
-    $SIG{TERM} = sub { warn "gbrowse going down..."; CORE::exit 0 };
+    $SIG{TERM} = sub { warn "[$$] gbrowse going down..."; CORE::exit 0 };
     my $globals = Bio::Graphics::Browser2->open_globals;
     Bio::Graphics::Browser2::Render::HTML->new($globals)->run();
 }
diff --git a/cgi-bin/gbrowse_details b/cgi-bin/gbrowse_details
old mode 100755
new mode 100644
index ccdc109..9d494c1
--- a/cgi-bin/gbrowse_details
+++ b/cgi-bin/gbrowse_details
@@ -6,6 +6,12 @@ eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
 eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
     if 0; # not running under some shell
 
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
 use strict;
 use Bio::Graphics::Browser2;
 use Bio::Graphics::Browser2::RegionSearch;
diff --git a/cgi-bin/gbrowse_gmap b/cgi-bin/gbrowse_gmap
old mode 100755
new mode 100644
index 0375cba..933382b
--- a/cgi-bin/gbrowse_gmap
+++ b/cgi-bin/gbrowse_gmap
@@ -6,6 +6,12 @@ eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
 eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
     if 0; # not running under some shell
 
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
 =pod
 
 =head1 GBrowse/GMap Mashup
diff --git a/cgi-bin/gbrowse_img b/cgi-bin/gbrowse_img
old mode 100755
new mode 100644
index bcfa458..2168183
--- a/cgi-bin/gbrowse_img
+++ b/cgi-bin/gbrowse_img
@@ -1,5 +1,11 @@
 #!/usr/bin/perl -w
 
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
 # $Id: gbrowse_img,v 1.7 2009-08-31 19:46:38 lstein Exp $
 
 use strict;
@@ -15,6 +21,7 @@ use Digest::MD5 'md5_hex';
 # type      list of feature mnemonics
 # options   track options, in format mnemonic+option+mnemonic+option...
 # name      landmark or range to display, in format Name:start..stop
+# dbid      database name
 # width     desired width of image, in pixels (height cannot be set)
 # add       a feature to superimpose on top of the image
 #             in format: reference+type+name+start..stop,start..stop,start..stop
@@ -49,11 +56,24 @@ if ($fcgi) {
     };
 
     my %sys_env = %ENV;
+
+    warn "[$$] FastCGI initializing and loading default data sources...\n";
+    my $globals = Bio::Graphics::Browser2->open_globals;
+    if($globals->preload_datasources) {
+      foreach ($globals->data_sources) {
+        warn "[$$] Loading $_\n";
+        my $source = $globals->create_data_source($_);
+        $source->open_database();  # cache default database in memory
+      }
+    }
+    else {
+      warn "[$$] Preload disabled";
+    }
+
     while (!$FCGI_DONE) {
 	my $status = $fcgi->Accept;
 	next unless $status >= 0;
         %ENV = ( %sys_env, %ENV );
-	my $globals = Bio::Graphics::Browser2->open_globals;
 	CGI->initialize_globals();
 	my $gbi = GBrowse_img->new($globals);
 	$gbi->run();
@@ -63,7 +83,7 @@ if ($fcgi) {
 
 else {
     my $globals = Bio::Graphics::Browser2->open_globals;
-    GBrowse_img->new($globals)->run();    
+    GBrowse_img->new($globals)->run();
 }
 
 
@@ -101,10 +121,10 @@ sub run {
 	return;
     }
 
-    $render->set_source() && exit;  # may cause a redirect and exit 
+    $render->set_source() && exit;  # may cause a redirect and exit
     $render->init();
 
-    if ($render->data_source->must_authenticate 
+    if ($render->data_source->must_authenticate
 	&& !$self->session->private) {
 	my $base = $render->globals->gbrowse_url;
 	print $self->header();
@@ -207,19 +227,18 @@ sub render_image {
 
 
     my $convert_to_pdf;
-    if ($format eq 'PDF' && `which inkscape`) {
-	$convert_to_pdf++;
+    if ($format eq 'PDF' && ($convert_to_pdf = `which svg2pdf` || `which inkscape`)) {
 	$format = 'GD::SVG';
     }
 
     $format = 'GD::SVG' if $format eq 'SVG';
     $format      = 'GD' if $embed;
 
-    my ($img_data,$map) = $render->region->feature_count > 1 
+    my ($img_data,$map) = $render->region->feature_count > 1
                                  ? $self->render_multiple($renderer,$format,$flip,$embed)
 	                         : $self->render_tracks  ($renderer,$format,$flip,$embed);
 
- 
+
     my $seg   = $render->segment;
     my $fname = $seg ? $seg->seq_id.':'.$seg->start.'..'.$seg->end : "NA";
 
@@ -227,7 +246,7 @@ sub render_image {
         my $url    = $renderer->source->generate_image($img_data);
 	my $js = $render->data_source->globals->js_url;
 	my @scripts = map { {src=>"$js/$_"} }
-	                  qw(balloon.js balloon.config.js yahoo-dom-event.js);
+	                  qw(balloon.js balloon.config.js prototype.js GBox.js);
 	print $self->header(-type=>'text/html');
 	print start_html(-script=>\@scripts),
 	      $render->render_balloon_settings,
@@ -242,9 +261,9 @@ sub render_image {
 
     elsif ($format eq 'GD') {
 	print $self->header(-type=>'image/png',
-			    -content_disposition => "filename=$fname.png");	
+			    -content_disposition => "filename=$fname.png");
 	print $img_data->png;
-    } 
+    }
     elsif ($format eq 'GD::SVG' && $convert_to_pdf) {
 	print $self->header(-type=>'application/pdf',
 			    -content_disposition => "filename=$fname.pdf");
@@ -254,8 +273,11 @@ sub render_image {
 
 	print $infh $img_data or die "$in: $!";
 	close $infh;
-
-	system "inkscape -z --without-gui --export-pdf=$out $in 3<&1 1>&2 2>&3 | grep -v GDK_IS_DISPLAY";
+    if ($convert_to_pdf =~ /svg2pdf$/) {
+       system "svg2pdf $in $out 3<&1 1>&2 2>&3";
+    } else {
+	    system "inkscape -z --without-gui --export-pdf=$out $in 3<&1 1>&2 2>&3 | grep -v GDK_IS_DISPLAY";
+    }
 	open (my $fh,'<',$out) or die "$out: $!";
 	while (<$fh>) {print $_}
 	close $fh;
@@ -265,8 +287,8 @@ sub render_image {
 	print $self->header(-type => 'application/svg+xml',
 			    -content_disposition => "filename=$fname.svg");
 	print $img_data;
-    } 
-    else { 
+    }
+    else {
 	print $self->header('text/plain');
 	print "unknown format $format\n";
     }
@@ -305,7 +327,7 @@ sub render_tracks {
     my $h_callback = make_hilite_callback(param('h_feat'));
 
     # If no tracks specified, we want to see all tracks with this feature
-    if (!@track_types) { @track_types = @labels; } 
+    if (!@track_types) { @track_types = @labels; }
     unshift @track_types,'_scale';
 
     my $result   = $renderer->render_track_images(
@@ -321,7 +343,7 @@ sub render_tracks {
 						      -suppress_key     => 0,
 						      }
 						  );
-    
+
     warn "returned labels = ",join ',',%$result if DEBUG;
 
     # Previously - @labels (caused drawing more tracks than asked for)
@@ -359,7 +381,7 @@ sub calculate_composite_bounds {
 	    $width    += ($g->getBounds)[0];
 	}
     }
-    
+
     return ($width,$height);
 }
 
@@ -368,12 +390,12 @@ sub consolidate_images {
     my ($gds,$width,$height,$orientation,$labels) = @_;
     $orientation ||= 'vertical';
 
-    ($width,$height) = $self->calculate_composite_bounds($gds,$orientation) 
+    ($width,$height) = $self->calculate_composite_bounds($gds,$orientation)
 	unless defined $width && defined $height;
 
     warn "consolidating ",scalar @$gds," GD objects" if DEBUG;
 
-    return $gds->[0]->isa('GD::SVG::Image') 
+    return $gds->[0]->isa('GD::SVG::Image')
 	                         ? $self->_consolidate_svg($width,$height,$gds,$orientation,$labels)
                                  : $self->_consolidate_gd ($width,$height,$gds,$orientation,$labels);
 }
@@ -492,7 +514,7 @@ sub _consolidate_svg {
 	    $offset += $current_height;
 	}
 	$svg   .= qq(</svg>\n);
- 
+
 
    } else {
 	my $offset = 0;
@@ -520,8 +542,8 @@ sub _consolidate_svg {
 
     # munge fonts slightly for systems that don't have Helvetica installed
     $svg    =~ s/font="Helvetica"/font="san-serif"/gi;
-    $svg    =~ s/font-size="11"/font-size="9"/gi;  
-    $svg    =~ s/font-size="13"/font-size="12"/gi;  
+    $svg    =~ s/font-size="11"/font-size="9"/gi;
+    $svg    =~ s/font-size="13"/font-size="12"/gi;
     return $svg;
 }
 
@@ -556,13 +578,13 @@ sub usage {
 
 <pre>
 
-  <img src="http://www.wormbase.org/db/gb2/gbrowse_img/c_elegans?name=mec-3;width=400">
+  <img src="http://www.wormbase.org/tools/genome/gbrowse_img/c_elegans?name=mec-3;width=400">
 <blockquote>
   <i>Will generate this picture:</i>
- <img src="http://www.wormbase.org/db/gb2/gbrowse_img/c_elegans?name=mec-3;width=400">
+ <img src="http://www.wormbase.org/tools/genome/gbrowse_img/c_elegans?name=mec-3;width=400">
 </blockquote>
 
-  <a href="http://www.wormbase.org/db/gb2/gbrowse_img?list=sources">list</a>
+  <a href="http://www.wormbase.org/tools/genome/gbrowse_img?list=sources">list</a>
 <blockquote>
   <i>Will return this document:</i>
   ## Sources
@@ -584,19 +606,19 @@ sub usage {
   yeast
 </blockquote>
 
-  <a href="http://www.wormbase.org/db/gb2/gbrowse_img/c_elegans?list=types">types</a>
+  <a href="http://www.wormbase.org/tools/genome/gbrowse_img/c_elegans?list=types">types</a>
 <blockquote>
   <i>Will return this document:</i>
   ## Feature types for source c_elegans
   LOCI:overview	Landmarks	default
-  RNAZ	RNAz non-coding RNA genes	
+  RNAZ	RNAz non-coding RNA genes
   CG	Gene Models	default
-  CDS	Coding Segments	
-  RNA	Predicted non-coding RNAs	
-  HISTORICAL	Obsolete gene models	
-  GENEFINDER	GeneFinder Predictions	
-  TWINSCAN	Twinscan Predictions	
-  GENEMARKHMM	GeneMarkHMM Predictions	
+  CDS	Coding Segments
+  RNA	Predicted non-coding RNAs
+  HISTORICAL	Obsolete gene models
+  GENEFINDER	GeneFinder Predictions
+  TWINSCAN	Twinscan Predictions
+  GENEMARKHMM	GeneMarkHMM Predictions
   mSPLICER_TRANSCRIPT	mSplicer
   ...
 </blockquote>
@@ -612,7 +634,7 @@ can be used as the destination of an <img> tag like this:
 </p>
 
 <blockquote><pre>
-<img src="http://www.wormbase.org/db/gb2/gbrowse_img/c_elegans?name=III:1..1000">
+<img src="http://www.wormbase.org/tools/genome/gbrowse_img/c_elegans?name=III:1..1000">
 </pre></blockquote>
 
 <p>
@@ -637,6 +659,7 @@ lengths.
 <table border="1">
 <tr><th>Argument</th><th>Alias</th><th>Description</th></tr>
 <tr> <td>name</td>    <td>q</td>   <td>genomic landmark or range</td></tr>
+<tr> <td>dbid</td>    <td> </td>   <td>database name for disambiguating landmarks</td></tr>
 <tr> <td>type</td>    <td>t</td>   <td>tracks to include in image</td></tr>
 <tr> <td>width</td>   <td>w</td>   <td>desired width of image</td></tr>
 <tr> <td>options</td> <td>o</td>   <td>list of track options (compact, labeled, etc)</td></tr>
@@ -675,12 +698,23 @@ lengths.
       image showing the position of each landmark.  The alias "q" can be used to
       shorten the length of the URL.
       <p>
+
+  <dt><b>dbid</b></dt>
+
+  <dd>If the data source contains multiple defined databases, this
+  argument is required to uniquely identify landmarks that may appear
+  in multiple databases under different names. If not present, then
+  the standard search algorithm is used. Use the symbolic database
+  name indicated in the source configuration file. For example if the
+  database stanza is "[scaffolds:database]" then pass
+  "dbid=scaffolds".  </dd>
+
   <dt><b>type</b> (Aliases: <b>t</b>, <b>track</b>)
   <dd>This argument lists the feature types to display.  The value of this argument is
       a list of track names separated by spaces ("+" characters when URL-escaped).  For example:
       <p>
       <pre>
-      <img src="http://www.wormbase.org/db/gb2/gbrowse_img/c_elegans?name=mec-3;
+      <img src="http://www.wormbase.org/tools/genome/gbrowse_img/c_elegans?name=mec-3;
                    type=tRNA+NG+WABA+CG+ESTB">
       </pre>
       Multiple <i>type=</i> arguments will be combined to form a single space-delimited list.
@@ -740,13 +774,13 @@ lengths.
   <dt><b>style</b>
   <dd>The style argument can be used to control the rendering of additional features added
       with "add".  It is a flattened version of the style configuration sections described
-      in <a href="http://www.wormbase.org/db/gb2/gbrowse?help=annotation">this document</a>
+      in <a href="http://www.wormbase.org/tools/genome/gbrowse?help=annotation">this document</a>
       For example, if you have added a "Blast Hit" annotation, then you can tell the
       renderer to use a red arrow for this glyph in this way:
       style=%22Blast%20Hit%22+glyph=arrow+fgcolor=red
       <p>
   <dt><b>keystyle</b> (Alias: <b>k</b>)
-  <dd>Controls the positioning of the track key. One of "right", "left", "between" (default) 
+  <dd>Controls the positioning of the track key. One of "right", "left", "between" (default)
       or "bottom"
       <p>
   <dt><b>overview</b>
@@ -806,14 +840,14 @@ lengths.
        <blockquote></pre>
         h_region=Chr3:200000..250000 at wheat
         </pre></blockquote>
-        You may omit "@color", in which case the highlighted region 
+        You may omit "@color", in which case the highlighted region
         will default to
         lightgrey. You can specify multiple h_region arguments in order to
         highlight several regions with distinct colors.
 </dl>
 <p>
 Putting it all together, here's a working (very long) URL:
-<pre><a href="http://www.wormbase.org/db/gb2/gbrowse_img/c_elegans?name=B0001;add=B0001+pcr+pcr1+20000..333000;add=B0001+%22cool%20knockout%22+kn2+30000..20000,10000..5000;type=add+CG+WTP;style=pcr+glyph=primers;style=%22cool%20knockout%22+glyph=transcript2+bgcolor=orange;abs=1">http://www.wormbase.org/db/gb2/gbrowse_img/c_elegans?name=B0001;add=B0001+pcr+pcr1+20000..333000;add=B0001+%22cool%20knockout%22+kn2+30000..20000,10000..5000;type=add+CG+WTP;style=pcr+glyph=primers;style=%22cool% [...]
+<pre><a href="http://www.wormbase.org/tools/genome/gbrowse_img/c_elegans?name=B0001;add=B0001+pcr+pcr1+20000..333000;add=B0001+%22cool%20knockout%22+kn2+30000..20000,10000..5000;type=add+CG+WTP;style=pcr+glyph=primers;style=%22cool%20knockout%22+glyph=transcript2+bgcolor=orange;abs=1">http://www.wormbase.org/tools/genome/gbrowse_img/c_elegans?name=B0001;add=B0001+pcr+pcr1+20000..333000;add=B0001+%22cool%20knockout%22+kn2+30000..20000,10000..5000;type=add+CG+WTP;style=pcr+glyph=primers;st [...]
 </pre>
 
 <p>
diff --git a/cgi-bin/gbrowse_key_img b/cgi-bin/gbrowse_key_img
old mode 100755
new mode 100644
index 4315272..15b3f65
--- a/cgi-bin/gbrowse_key_img
+++ b/cgi-bin/gbrowse_key_img
@@ -3,6 +3,12 @@
 eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
     if 0; # not running under some shell
 
+eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
+eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
 use strict;
 use GD;
 use Bio::Graphics;
diff --git a/cgi-bin/gbrowse_login b/cgi-bin/gbrowse_login
old mode 100755
new mode 100644
index fd944d2..e889e41
--- a/cgi-bin/gbrowse_login
+++ b/cgi-bin/gbrowse_login
@@ -6,6 +6,12 @@ eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
 eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
     if 0; # not running under some shell
 
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
+eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
+    if 0; # not running under some shell
+
 use strict;
 use Bio::Graphics::Browser2::UserDB;
 use Bio::Graphics::Browser2::Render;
diff --git a/cgi-bin/gbrowse_syn b/cgi-bin/gbrowse_syn
old mode 100755
new mode 100644
index b5e398e..418af33
--- a/cgi-bin/gbrowse_syn
+++ b/cgi-bin/gbrowse_syn
@@ -3,14 +3,11 @@
 eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
     if 0; # not running under some shell
 
-eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
-    if 0; # not running under some shell
 
-eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
-    if 0; # not running under some shell
+use constant DEFAULT_CONF   => '/etc/gbrowse2/synteny';
 
-our $CONF_DIR  = "$ENV{GBROWSE_CONF}/synteny";
-our $VERSION   = '$Id: gbrowse_details,v 1.7 2009-08-27 19:13:18 idavies Exp $';
+our $CONF_DIR  = $ENV{GBROWSE_CONF} ? "$ENV{GBROWSE_CONF}/synteny" : DEFAULT_CONF;
+our $VERSION   = 2.55;
 our $BIOGRAPHICS_VERSION   = 1.8;
 
 use strict;
@@ -32,7 +29,7 @@ use Legacy::DB::SyntenyIO;
 use Legacy::DB::SyntenyBlock;
 
 
-use constant OVERVIEW_RATIO     => 0.9;
+use constant OVERVIEW_RATIO     => 1.0;
 use constant OVERVIEW_BGCOLOR   => 'gainsboro';
 use constant IMAGE_WIDTH        => 800;
 use constant INTERIMAGE_PAD     => 5;
@@ -85,7 +82,7 @@ unless ($go) {
   print p('Please consult '.a({-href=>'http://gmod.org/wiki/GBrowse_syn'},'the documentation'));
 
   print <<END;
-<iframe style="frameborder:0;width:800px;height:2000px" src="/gbrowse2/gbrowse_syn_help.html">
+<iframe style="frameborder:0;width:1280px;height:2000px" src="/gbrowse2/gbrowse_syn_help.html">
 </iframe>
 END
 ;
@@ -253,7 +250,7 @@ sub species_search {
   my $values  = [sort {$MAP->{$a}{desc} cmp $MAP->{$b}{desc}} grep {$MAP->{$_}{desc}} keys %labels];
   unshift @$values, '';
 
-  my $onchange = "document.mainform.submit()";
+  my $onchange = "document.searchform.submit()";
   return b(wiki_help('Reference_Species',$CONF->tr('Genome to Search'))) . ':' . br .
       popup_menu(
 		 -onchange => $onchange,
@@ -267,7 +264,7 @@ sub species_search {
 
 sub search_form {
   my $segment = shift;
-  print start_form(-name=>'mainform',-method => 'post');
+  print start_form(-name=>'searchform',-method => 'post');
   navigation_table($segment);
 }
 
@@ -1160,7 +1157,7 @@ sub source_menu {
   my $sources = $show_sources && @sources > 1;
   my $source = $CONF->get_source;
   return $sources ? b(wiki_help('Data Source',$CONF->tr('Data Source')), ': ') . br.
-      popup_menu(-onchange => 'document.mainform.submit()',
+      popup_menu(-onchange => 'document.searchform.submit()',
 		 -name   => 'source',
 		 -values => \@sources,
 		 -labels => { map {$_ => $CONF->description($_)} $CONF->sources},
@@ -1235,14 +1232,25 @@ sub overview_panel {
   return '' if $SCONF->section_setting('overview') eq 'hide';
   my $image = overview($whole_segment,$segment);
   my $ref = $MAP->{$CONF->page_settings("search_src")}->{desc};
+
+  my $width = $CONF->page_settings('imagewidth') || $CONF->setting("imagewidth") || IMAGE_WIDTH;
+  my $ip = $SCONF->setting('image_padding') || $CONF->image_padding || param('image_padding') || 0;
+  my $pl = $SCONF->setting('pad_left')  || 0;
+  my $pr = $SCONF->setting('pad_right') || 0;
+  $width += ($pl || $ip) + ($pr || $ip);
+
   return toggle('Overview',
                 table({-border=>0,-width=>'100%'},
 		      TR(th("<center>Reference genome: <i>$ref</i></center>")),
                       TR({-class=>'databody'},
-                         td({-align=>'center'},$image)
-                        )
-                     )
-		);
+			 td({-align=>'center'},
+			    div({ id => 'overview_panels',
+				  style => "position:relative;width:${width}px"},
+				$image)
+		            )
+                    )
+		)
+      );
 }
 
 sub overview {
diff --git a/conf/GBrowse.conf b/conf/GBrowse.conf
index ac82877..44abedd 100644
--- a/conf/GBrowse.conf
+++ b/conf/GBrowse.conf
@@ -1,5 +1,5 @@
 # This is the global configuration for gbrowse
-# It contains setting common to all data sources as well 
+# It contains setting common to all data sources as well
 # as the various constants formerly scattered amongst scripts and libraries
 
 [GENERAL]
@@ -77,6 +77,8 @@ slave_timeout          = 45
 global_timeout         = 60
 search_timeout         = 15
 max_render_processes   = 4   # try double number of CPU/cores
+preload data sources   = 0   # when true & f[ast]cgi loads all datasources at startup,
+                             # can be bad if many or ds have many tracks
 
 # Renderfarm settings
 #include renderfarm.conf
@@ -88,9 +90,10 @@ expire uploads  = 6w  # expire uploaded data if unused for >6 weeks
 
 # Appearance settings
 truecolor     =  1   # better appearance at the expense of larger image files
+# truetype    =  1   # turn on vector fonts in tracks. Requires Bio::Graphics 2.33 or higher, and truetype support on the server.
 
 # The #include line following this one defines a transparent theme.
-# Replace "transparent_colors" with "solid_gray_colors" 
+# Replace "transparent_colors" with "solid_gray_colors"
 # or "warm_colors" for different themes.
 
 #include "themes/warm_colors"
@@ -119,7 +122,7 @@ details multiplier = 3
 link          = AUTO
 
 # HTML to insert inside the <head></head> section
-head = 
+head =
 
 # At the top of the HTML...
 header =
@@ -129,17 +132,17 @@ footer = <hr />
          <p style="font-size:small">The Generic Genome Browser. For questions about the data
          at this site, please contact its webmaster. For support of the
          browser software <i>only</i>, send email to
-         <a href="mailto:gmod-gbrowse at lists.sourceforge.net">gmod-gbrowse at lists.sourceforge.net</a> 
+         <a href="mailto:gmod-gbrowse at lists.sourceforge.net">gmod-gbrowse at lists.sourceforge.net</a>
          or visit the <a href="http://www.gmod.org">GMOD Project</a> web pages.
          </p>
 
 # Various places where you can insert your own HTML -- see configuration docs
-html1 = 
-html2 = 
-html3 = 
-html4 = 
-html5 = 
-html6 = 
+html1 =
+html2 =
+html3 =
+html4 =
+html5 =
+html6 =
 
 # Limits on genomic regions (can be overridden in datasource config files)
 region segment         = 200000
diff --git a/conf/aws_balancer.conf b/conf/aws_balancer.conf
new file mode 100644
index 0000000..f502f7b
--- /dev/null
+++ b/conf/aws_balancer.conf
@@ -0,0 +1,28 @@
+[LOAD TABLE]
+#req/s  min max
+0.1     0   1
+0.5     0   2
+1.0     1   3
+2.0     2   4
+5.0     3   6
+6.0     4   6
+7.0     5   7
+10.0    6   8
+15.0    8   8
+
+[MASTER]
+external_ip       =                # optional; will figure it out if needed
+poll_interval     = 0.5            # minutes between polling steps
+server_status_url = http://localhost/server-status    # URL on master that invokes Apache server-status
+
+[SLAVE]
+instance_type     = m1.medium      # pretty nice performance, might also try medium
+spot_bid          = 0.10           # the pricing will cost no more than $0.10/hr
+ports             = 8101           # can be several space-delimited port numbers
+region            = us-west-2      # needed only when run from a non-AWS computer
+image_id          = GBrowse-2.54   # AMI of the slave
+data_snapshots    =                # EBS snapshot(s) to attach to the slave
+availability_zone =                # optional
+subnet            =                # optional
+security_group    =                # optional; will manage own security group if needed
+
diff --git a/conf/languages/POSIX.pm b/conf/languages/POSIX.pm
index 2a832bd..9754b47 100644
--- a/conf/languages/POSIX.pm
+++ b/conf/languages/POSIX.pm
@@ -168,6 +168,8 @@ END
 
    SELECT_SUBTRACKS   => 'showing %d/%d subtracks',
 
+   TRACK_ID   => 'Track ID=<i>%s</i>',
+
    EDIT       => 'Edit File...',
 
    DELETE     => 'Delete File',
@@ -490,6 +492,8 @@ END
 
  CONFIGURE_THIS_TRACK   => '<b>Configure this track</b>',
 
+ POP_OUT               => '<b>Pop out/in</b>',
+
  DOWNLOAD_THIS_TRACK   => '<b>Download this track</b>',
 
  ABOUT_THIS_TRACK   => '<b>About this track</b>',
diff --git a/conf/plugins/Blat.pm b/conf/plugins/Blat.pm
index 5dc7f87..7f49d14 100644
--- a/conf/plugins/Blat.pm
+++ b/conf/plugins/Blat.pm
@@ -4,10 +4,9 @@ package Bio::Graphics::Browser2::Plugin::Blat;
 use strict;
 use Bio::Graphics::Browser2::Plugin;
 use Bio::Graphics::Feature;
-use Text::Shellwords;
-use File::Temp qw/ tempfile tempdir /;
-use Bio::SearchIO;
+use File::Temp qw/ tempfile /;
 use CGI qw(:standard *table);
+use CGI::Carp qw(fatalsToBrowser);
 use vars '$VERSION','@ISA','$blat_executable','$twobit_dir','$host','$port';
 
 
@@ -58,14 +57,14 @@ $twobit_dir = "";
 $host = "";
 $port = "";
 
-$VERSION = '0.01';
+$VERSION = '0.02';
 
 @ISA = qw(Bio::Graphics::Browser2::Plugin);
 
 sub name { "BLAT Alignment" }
 
 sub description {
-  p("This plugin will take an input sequence and run BLAT's gfClient (a Blat client to a local server) against the human or mouse genomes. It obviously requires a pre-installed local BLAT server(gfServer) and client(gfClient).");
+  p("This plugin will take an input DNA sequence and run BLAT's gfClient (a Blat client to a local server).");
 }
 
 sub type { 'finder' }
@@ -91,6 +90,9 @@ sub configure_form {
   my $form .= h3("Enter parameters below for alignment of sequences using a Client to a local BLAT Server:")
   .start_table({-border => 0})
   .TR([
+    td(b("Input sequence type:"), popup_menu(-align=>'center', -name=>$self->config_name('q'),-values=>['dna', 'rna']))
+  ])
+  .TR([
     td([b("Input Sequence To Align:"), textarea(-align=>'center', -name=>$self->config_name('sequence_to_blat'),-rows=>10,-cols=>80,-value=>$current_config->{'sequence_to_blat'})])
   ]);
 
@@ -98,6 +100,9 @@ sub configure_form {
   $form .= start_table({-border => 0}) . Tr(td(p())) . Tr(td(p())) . Tr(td(p())) . end_table();
   $form .= start_table({-border => 0})
   .TR([
+    td(b("Minimum Percent Identity:"), textfield(-align=>'center', -name=>$self->config_name('minIdentity'),-size=>10, -value=>'90'))
+  ])
+  .TR([
     td(b("Number of Hits to Return:"), textfield(-align=>'center', -name=>$self->config_name('hits'),-size=>10, -value=>$current_config->{'hits'}))
   ]);
   $form .= end_table();
@@ -107,47 +112,85 @@ sub configure_form {
   
 sub find {
   my $self = shift;
-  my ($i, at hit_starts, at block_sizes, at results, at front);
+  my ($i, at hit_starts, at block_sizes, at results);
   my $query = $self->config_param('sequence_to_blat');  
-  my $hits = $self->config_param('hits');
+  my $hits = int($self->config_param('hits'));
+  my $minIdentity = int($self->config_param('minIdentity'));
+  my $q = ($self->config_param('q') eq 'rna') ? 'rna' : 'dna';
   my ($i_f, $in_file) = tempfile();
   my ($o_f, $out_file) = tempfile();
 
-  $query =~ s/[\s]//g;				# remove whitespace
-  my $query_type = check_seq($query) or return; # check for dna or protein (dna queries must be compared against dna databases only & vice versa)
-  print $i_f ">segment\n$query\n";		# print it to a temp file
+  if ($query !~ /^\s*>/) { print $i_f ">segment\n";} # add FASTA defline if needed
+  print $i_f $query; # print it to a temp file
+
+  my $error = `$blat_executable $host $port $twobit_dir -minIdentity=$minIdentity -nohead -q=$q $in_file $out_file 2>&1 > /dev/null`;
+  die "$error" if $error;
 
-  system("$blat_executable $host $port $twobit_dir -nohead -q=$query_type $in_file $out_file > /dev/null");
-  
   open (IN, "$out_file") || die "couldn't open $out_file $!\n";
+  my $hit_count = 0;
+  my @blat_hits;
+  # indexes each blat hit array of @blat_hits
+  use constant {
+    MATCHES       => 0,
+    MISMATCHES    => 1,
+    REP_MATCHES   => 2,
+    N_COUNT       => 3,
+    Q_NUM_INSERT  => 4,
+    Q_BASE_INSERT => 5,
+    T_NUM_INSERT  => 6,
+    T_BASE_INSERT => 7,
+    STRAND        => 8,
+    Q_NAME        => 9,
+    Q_LENGTH      => 10,
+    Q_START       => 11,
+    Q_END         => 12,
+    T_NAME        => 13,
+    T_LENGTH      => 14,
+    T_START       => 15,
+    T_END         => 16,
+    BLOCK_COUNT   => 17,
+    BLOCK_SIZES   => 18,
+    Q_STARTS      => 19,
+    T_STARTS      => 20,
+  };
+
   while(<IN>) {
-    # this could probably be done better but ...
-    my ( $matches,$mismatches,$rep_matches,$n_count,$q_num_insert,$q_base_insert,$t_num_insert, $t_base_insert,
-         $strand,$q_name,$q_length,$q_start,$q_end,$t_name,$t_length,$t_start,$t_end,$block_count,$block_sizes,
-         $q_starts,$t_starts) = split;
+    push (@blat_hits, [ split ]);
+  }
+
+  for my $hit (
+    # sort hits in descending order by calculated score
+    sort {
+      ($b->[MATCHES]+$b->[MISMATCHES]+$b->[REP_MATCHES])/$b->[Q_LENGTH]
+      <=>
+      ($a->[MATCHES]+$a->[MISMATCHES]+$a->[REP_MATCHES])/$a->[Q_LENGTH]
+    } @blat_hits
+  ) {
+    last if ++$hit_count > $hits;
     
-    $block_sizes =~ s/\,$//;	      # remove trailing comma from block_sizes string     
-    $t_starts	 =~ s/\,$//;	      # .. and from t_starts string
-    my $score = sprintf "%.2f", ( 100 * ( $matches + $mismatches + $rep_matches ) / $q_length );
-    my $percent_id = sprintf "%.2f", ( 100 * ($matches + $rep_matches)/( $matches + $mismatches + $rep_matches ));
-    my $alignment = Bio::Graphics::Feature->new(-start=>$t_start,
-						-end  =>$t_end,
-						-ref => $t_name,
+    $hit->[BLOCK_SIZES] =~ s/\,$//;      # remove trailing comma from block_sizes string     
+    $hit->[T_STARTS]    =~ s/\,$//;      # .. and from t_starts string
+    my $score = sprintf "%.2f", ( 100 * ( $hit->[MATCHES] + $hit->[MISMATCHES] + $hit->[REP_MATCHES] ) / $hit->[Q_LENGTH] );
+    my $percent_id = sprintf "%.2f", ( 100 * ($hit->[MATCHES] + $hit->[REP_MATCHES]) /
+                                             ($hit->[MATCHES] + $hit->[MISMATCHES] + $hit->[REP_MATCHES]));
+    my $alignment = Bio::Graphics::Feature->new(-start=>$hit->[T_START]+1,
+						-end  =>$hit->[T_END],
+						-ref => $hit->[T_NAME],
 						-type=>'BLAT',
-						-name => 'Alignment',
-						-strand => $strand,
+						-name => "Alignment$hit_count",
+						-strand => ($hit->[STRAND] eq '+') ? 1 : -1,
 						-score => $score
 					       );
     
-    @hit_starts = split(",", $t_starts);
-    @block_sizes = split(",", $block_sizes);
-    for($i=0;$i<$block_count;$i++){	# if multihit alignments (block_count > 1), aggregate.
+    @hit_starts = map { $_ + 1 } split(",", $hit->[T_STARTS]);
+    @block_sizes = split(",", $hit->[BLOCK_SIZES]);
+    for($i=0;$i<$hit->[BLOCK_COUNT];$i++){      # if multihit alignments (block_count > 1), aggregate.
       my $sub_alignment = Bio::Graphics::Feature->new(-start=>$hit_starts[$i],
         				      -end  =>($hit_starts[$i]+$block_sizes[$i]),
-        				      -ref => $t_name,
+        				      -ref => $hit->[T_NAME],
         				      -type=>'BLAT',
         				      -name => 'Alignment',
-        				      -strand => $strand,
+        				      -strand => ($hit->[STRAND] eq '+') ? 1 : -1,
         				      -score => $percent_id
         				     );
       $alignment->add_segment($sub_alignment);
@@ -157,14 +200,7 @@ sub find {
   
   unlink $in_file;
   unlink $out_file;
-  @front = splice(@results,0,$hits);	# Remove the required number of hits from the front of the array and return them.
-  return \@front;			# If all hits are required, then return \@results and remove the splicing.
-}
-
-sub check_seq{
-  my $query = shift;
-  if ($query =~ /^[gatcn]/i){return "dna";}
-  if ($query =~ /^[SFLY_WLPHQRIMTNKSRVADEG]/i) {return "prot";}
+  return (\@results, @results ? '' : 'No alignments found');
 }
 
 1;
diff --git a/conf/plugins/FilterTest.pm b/conf/plugins/FilterTest.pm
index bfa1cef..abf99ef 100644
--- a/conf/plugins/FilterTest.pm
+++ b/conf/plugins/FilterTest.pm
@@ -38,7 +38,7 @@ sub new
 
 sub name
 {
-    'ORFs';
+    'Genes';
 }
 
 sub type
diff --git a/conf/plugins/PrimerDesigner.pm b/conf/plugins/PrimerDesigner.pm
index ddecca2..bcece8f 100644
--- a/conf/plugins/PrimerDesigner.pm
+++ b/conf/plugins/PrimerDesigner.pm
@@ -79,8 +79,8 @@ use Bio::Graphics::Browser2::Util;
 use Bio::Graphics::Feature;
 use Bio::Graphics::FeatureFile;
 use CGI qw/:standard escape/;
-use CGI::Pretty 'html3';
-use CGI::Carp 'fatalsToBrowser';
+#use CGI::Pretty 'html3';
+#use CGI::Carp 'fatalsToBrowser';
 use CGI::Toggle;
 use Math::Round 'nearest';
 use Carp qw/cluck/;
@@ -102,8 +102,9 @@ use vars qw/@ISA $PNG $CALL/;
 
 # Arg, modperl
 END {
-	
-  CGI::Delete_all();
+  if ($ENV{'MOD_PERL'}) {
+    CGI::Delete_all();
+  }
 }
 
 
diff --git a/conf/plugins/RestrictionAnnotator.pm b/conf/plugins/RestrictionAnnotator.pm
index 75d43ad..871a62c 100644
--- a/conf/plugins/RestrictionAnnotator.pm
+++ b/conf/plugins/RestrictionAnnotator.pm
@@ -5,11 +5,10 @@ use strict;
 use Bio::Graphics::Browser2::Plugin;
 use CGI qw(:standard *table);
 
-use vars '$VERSION','@ISA';
+use vars '$VERSION';
 $VERSION = '0.25';
 
- at ISA = qw(Bio::Graphics::Browser2::Plugin);
-
+use base 'Bio::Graphics::Browser2::Plugin';
 my %SITES;
 
 my @COLORS = qw(red green blue orange cyan black 
diff --git a/conf/plugins/Submitter.pm b/conf/plugins/Submitter.pm
index 90bd5d0..f3ff792 100644
--- a/conf/plugins/Submitter.pm
+++ b/conf/plugins/Submitter.pm
@@ -103,6 +103,7 @@ sub dump {
     || fatal_error('Error: a url for the external website is required');
   my $seq_label = $config->{seq_label} 
     || fatal_error('Error: a label is required for the sequence submission');
+  my $region_label = $config->{region_label};
 
   # Other form elements to include
   my $extra_html = unescape($config->{extra_html});
@@ -122,7 +123,7 @@ sub dump {
   # pass-thru arguments -- to be sent to the extertnal web-site
   my %args;
   for my $arg (keys %$config) {
-    next if $arg =~ /^seq_label$|^confirm$|^url$|^fasta$|^extra_html$/;
+    next if $arg =~ /^seq_label$|^region_label$|^confirm$|^url$|^fasta$|^extra_html$/;
     $args{$arg} = unescape($config->{$arg});
   }
 
@@ -135,6 +136,9 @@ sub dump {
   }
   print hidden($seq_label => $seq);
 
+  if (defined $region_label){
+    print hidden($region_label => $name);
+  }
   if ($extra_html || $confirm) {
     my @rows = th({-colspan => 2, -style => "background:lightsteelblue"}, 
 		  b("The following data will be submitted to $url"),
@@ -143,6 +147,7 @@ sub dump {
     
     for my $arg (keys %args) {
       next if $arg eq $seq_label;
+      next if $arg eq $region_label;
       $arg =~ s/extra_html/Additional options/;
       push @rows, td({-width => 100, -style => 'background:lightyellow'},
 		     [b("$arg:"), unescape($args{$arg})]); 
@@ -154,6 +159,8 @@ sub dump {
     }
     push @rows, td({-width => 100, -style => 'background:lightyellow'},
 		   [b($seq_label), pre($fasta)]);
+    push @rows, td({-width => 100, -style => 'background:lightyellow'},
+                   [b($region_label), pre($name)]) if defined $region_label;
 
     print table({-border=> 1}, Tr({-valign => 'top'}, \@rows));
   }
diff --git a/conf/renderfarm.conf b/conf/renderfarm.conf
index 05f299f..a2ac030 100644
--- a/conf/renderfarm.conf
+++ b/conf/renderfarm.conf
@@ -3,6 +3,3 @@
 renderfarm = 0
 
 remote renderer = 
-       http://localhost:8101 
-       http://localhost:8102 
-       http://localhost:8103
\ No newline at end of file
diff --git a/conf/slave_preload.conf b/conf/slave_preload.conf
index 1edacaa..e68dc60 100644
--- a/conf/slave_preload.conf
+++ b/conf/slave_preload.conf
@@ -1,9 +1,12 @@
-[yeast12]
-db_adaptor    = Bio::DB::SeqFeature::Store
-db_args       = -adaptor memory
-                -dir    $PERSISTENT/databases/yeast_chr1+2
+# use this to preload one or more databases into slave memory
+# see the examples below
 
-[scaffolds]
-db_adaptor    = Bio::DB::SeqFeature::Store
-db_args       = -adaptor memory
-                -dir    $PERSISTENT/databases/yeast_scaffolds
+#[yeast12]
+#db_adaptor    = Bio::DB::SeqFeature::Store
+#db_args       = -adaptor memory
+#                -dir    $PERSISTENT/databases/yeast_chr1+2
+
+#[scaffolds]
+#db_adaptor    = Bio::DB::SeqFeature::Store
+#db_args       = -adaptor memory
+#                -dir    $PERSISTENT/databases/yeast_scaffolds
diff --git a/conf/synteny/oryza.synconf.disabled b/conf/synteny/oryza.synconf.disabled.conf
similarity index 84%
rename from conf/synteny/oryza.synconf.disabled
rename to conf/synteny/oryza.synconf.disabled.conf
index d7e972d..11dd49a 100644
--- a/conf/synteny/oryza.synconf.disabled
+++ b/conf/synteny/oryza.synconf.disabled.conf
@@ -2,7 +2,7 @@
 description =  BLASTZ alignments for Oryza sativa 
 
 # The synteny database
-join        = dbi:mysql:database=rice_synteny;host=localhost
+join        = dbi:mysql:database=rice_synteny;host=localhost;user='www-data'
 
 # This option maps the relationship between the species data sources, names and descriptions
 # The value for "name" (the first column) is the symbolic name that gbrowse_syn users to identify each species.
@@ -15,7 +15,7 @@ join        = dbi:mysql:database=rice_synteny;host=localhost
 source_map =      rice          rice_synteny          "Domesic Rice (O. sativa)"
                   wild_rice     wild_rice_synteny     "Wild Rice"
 
-tmpimages     = /tmp/gbrowse2
+tmpimages     = /gbrowse2/tmp/gbrowse_syn
 imagewidth    = 800
 stylesheet    = /gbrowse2/css/gbrowse_transparent.css
 cache time    = 1
@@ -23,10 +23,10 @@ cache time    = 1
 config_extension = conf
 
 # example searches to display
-examples = rice 3:200000..280000
+examples = rice 3:337601..383524
            wild_rice 3:1..400000
 
-zoom levels = 5000 10000 25000 50000 100000 200000 400000
+zoom levels = 5000 10000 25000 50000 100000 200000 400000 1000000
 
 # species-specific databases
 [rice_synteny]
diff --git a/conf/synteny/rice_synteny.conf b/conf/synteny/rice_synteny.conf
index 80d5930..24d703f 100644
--- a/conf/synteny/rice_synteny.conf
+++ b/conf/synteny/rice_synteny.conf
@@ -2,11 +2,11 @@
 description   = Domestic rice chromosome 3
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-                -dir    /var/lib/gbrowse2/databases/gbrowse_syn/rice
+                -dir    $DATABASES/gbrowse_syn/rice
 
 
 # Web site configuration info
-tmpimages   = /tmp/gbrowse2
+tmpimages   = /gbrowse2/tmp/gbrowse_syn
 
 [EG]
 feature      = gene:ensembl
diff --git a/conf/synteny/wild_rice_synteny.conf b/conf/synteny/wild_rice_synteny.conf
index 86011a4..6065b98 100644
--- a/conf/synteny/wild_rice_synteny.conf
+++ b/conf/synteny/wild_rice_synteny.conf
@@ -2,11 +2,11 @@
 description   = Wild rice chromosome 3
 db_adaptor    = Bio::DB::SeqFeature::Store 
 db_args       = -adaptor memory
-                -dir    /var/lib/gbrowse2/databases/gbrowse_syn/wild_rice
+                -dir    $DATABASES/gbrowse_syn/wild_rice
 
 
 # Web site configuration info
-tmpimages   = /tmp/gbrowse2
+tmpimages   = /gbrowse2/tmp/gbrowse_syn
 
 ################## TRACK CONFIGURATION ####################
 # the remainder of the sections configure individual tracks
diff --git a/conf/yeast_chr1+2.conf b/conf/yeast_chr1+2.conf
index a4bfa09..f2c10c9 100644
--- a/conf/yeast_chr1+2.conf
+++ b/conf/yeast_chr1+2.conf
@@ -158,7 +158,7 @@ balloon click = <table>
       This gene brought to you by <a href="http://www.yeastgenome.org">SGD</a>.</th>
           <th bgcolor="cyan">Gene $name</th>
       </tr>
-      <tr align='left'><th><a href="/cgi-bin/gb2/gbrowse_details/yeast?name=$name" target="_new">See gene details</a></th></tr>
+      <tr align='left'><th><a href="/cgi-bin/gb2/gbrowse_details/yeast_advanced?feature_id=$id" target="_new">See gene details</a></th></tr>
       <tr align='left'><th><a href="http://db.yeastgenome.org/cgi-bin/locus.pl?locus=$name" target="_new">Ask SGD about $name</a></th></tr>
       <tr align='left'><th><a href="http://en.wikipedia.org/wiki/Special:Search?search=$name" target="_new">Ask Wikipedia about $name</a></th></tr>
       <tr align='left'><th><a href="http://www.google.com/search?q=$name" target="_new">Ask Google about $name</a></th></tr>
diff --git a/etc/default/gbrowse-aws-balancer b/etc/default/gbrowse-aws-balancer
new file mode 100644
index 0000000..244e706
--- /dev/null
+++ b/etc/default/gbrowse-aws-balancer
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# configuration file for gbrowse-aws-balancer init script
+# If this file contains AWS secrets, please make it read-only
+# to root.
+
+export PERL5LIB=$PERL5LIB
+
+DAEMON=$INSTALLSCRIPT/gbrowse_aws_balancer.pl
+USER=$WWWUSER
+RUNDIR=/var/run/gbrowse2
+LOGDIR=/var/log/gbrowse2
+CONFFILE=$CONF/aws_balancer.conf
+ACCESS_KEY=YOUR_EC2_ACCESS_KEY_HERE
+SECRET_KEY=YOUR_EC2_SECRET_KEY_HERE
+VERBOSITY=3
diff --git a/etc/default/gbrowse-slave b/etc/default/gbrowse-slave
index 4301cff..4397732 100644
--- a/etc/default/gbrowse-slave
+++ b/etc/default/gbrowse-slave
@@ -5,8 +5,8 @@ export PERL5LIB=$PERL5LIB
 DAEMON=$INSTALLSCRIPT/gbrowse_slave
 USER=$WWWUSER
 PRELOAD=$CONF/slave_preload.conf
-RUNDIR=/var/run/gbrowse
-LOGDIR=/var/log/gbrowse
+RUNDIR=/var/run/gbrowse2
+LOGDIR=/var/log/gbrowse2
 PREFORK=3
 PORT="8101 8102 8103"
 VERBOSITY=2
diff --git a/etc/init.d/gbrowse-aws-balancer b/etc/init.d/gbrowse-aws-balancer
new file mode 100755
index 0000000..7ac798d
--- /dev/null
+++ b/etc/init.d/gbrowse-aws-balancer
@@ -0,0 +1,74 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides:          gbrowse_aws_balancer
+# Required-Start:    $local_fs
+# Required-Stop:     $local_fs
+# Default-Start:     28
+# Default-Stop:      S
+# Short-Description: Start/Stop the gbrowse Amazon Web Services-based slave rendering farm
+### END INIT INFO
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=$INSTALLSCRIPT/gbrowse_aws_balancer.pl
+NAME="gbrowse-aws-balancer"
+DESC="GBrowse Amazon Web Services-based renderfarm load balancer"
+
+test -x $DAEMON || exit 0
+set -e
+
+USER=www-data
+RUNDIR=/var/run/gbrowse
+LOGDIR=/var/log/gbrowse
+CONFFILE=$CONF/aws_balancer.conf
+ACCESS_KEY=''
+SECRET_KEY=''
+VERBOSITY=3
+NICE=0
+
+COMMAND=$1
+
+if [ -f /etc/default/gbrowse-aws-balancer ]; then
+  . /etc/default/gbrowse-aws-balancer
+fi
+
+mkdir -p $RUNDIR
+chown -R $USER $RUNDIR
+mkdir -p $LOGDIR
+chown -R $USER $LOGDIR
+
+PID="$RUNDIR/$NAME.pid"
+LOG="$LOGDIR/gbrowse_aws_balancer"
+
+case "$COMMAND" in
+  start)
+	echo "Starting $DESC: $NAME "
+	ARGS="--background --conf $CONFFILE --verbosity $VERBOSITY --log $LOG --pid $PID --user $USER --access '$ACCESS_KEY' --secret '$SECRET_KEY'"
+	/bin/sh -c "nice -n $NICE $DAEMON $ARGS"
+	;;
+  stop)
+	echo "Stopping $DESC: $NAME "
+	ARGS="--conf $CONFFILE --pid $PID --access '$ACCESS_KEY' --secret '$SECRET_KEY' -k"
+	$DAEMON $ARGS
+	;;
+  status)
+	if test -e $PID ; then
+	    kill -0 `cat $PID`
+            if [ "$?" -eq 0 ]; then
+                echo "$NAME is running."
+            fi
+	else
+	    echo "$NAME is not running."
+	fi
+       ;;
+  restart|force-reload)
+	$0 stop
+	$0 start
+	;;
+  *)
+	N=/etc/init.d/$NAME
+	echo "Usage: $N {start|stop|restart|force-reload|status}" >&2
+	exit 1
+	;;
+esac
+
+exit 0
diff --git a/etc/init.d/gbrowse-slave b/etc/init.d/gbrowse-slave
index f2567ad..299d07a 100755
--- a/etc/init.d/gbrowse-slave
+++ b/etc/init.d/gbrowse-slave
@@ -24,6 +24,13 @@ PORT='8101 8102 8103'
 VERBOSITY=1
 NICE=0
 
+COMMAND=$1
+if [ $# -gt 0 ] ; then shift; fi
+
+if [ "$*" != "" ]; then
+    PORT=$*
+fi
+
 if [ -f $ETC/default/gbrowse-slave ]; then
   . $ETC/default/gbrowse-slave
 fi
@@ -33,7 +40,7 @@ chown -R $USER $RUNDIR
 mkdir -p $LOGDIR
 chown -R $USER $LOGDIR
 
-case "$1" in
+case "$COMMAND" in
   start)
 	echo -n "Starting $DESC: $NAME "
 	for port in $PORT; do
diff --git a/htdocs/annotation_help.html b/htdocs/annotation_help.html
index 822ec1b..305807d 100644
--- a/htdocs/annotation_help.html
+++ b/htdocs/annotation_help.html
@@ -12,6 +12,7 @@
     <th><a href="#featurefile format">Feature File Format</a></th>
     <th><a href="#customize">Customizing Appearance</a></th>
     <th><a href="#numeric">Displaying Intensity Plots & Other Numeric Data</a></th>
+    <th><a href="#binary">Binary data formats</a></th>
     <th><a href="#remote">Importing Custom Tracks via the Internet</a></th>
   </tr>
 </table>
@@ -43,8 +44,18 @@ Accepted formats include:
 
   <li> <a href="http://genome.ucsc.edu/goldenPath/help/wiggle.html" target="_blank">Wiggle (WIG)</a> (for dense quantitative data)
 
+  <li><a href="http://genome.ucsc.edu/goldenPath/help/bigWig.html" target="_blank">bigWig</a>
+      (indexed binary wiggle file)
+  
+  <li><a href="http://genome.ucsc.edu/goldenPath/help/bigBed.html" target="_blank">bigBed</a>
+      (indexed binary BED file)
+  
   <li><a href="http://samtools.sourceforge.net/"
 target="_blank">BAM or SAM</a> (for sequence alignment data)
+  
+  <li><a href="http://useq.sourceforge.net/useqArchiveFormat.html"
+target="_blank">useq archive</a> (either quantitative or segment data)
+  
 </ul>
 
 <p>
@@ -1103,6 +1114,106 @@ for the xyplot glyph options.
 
 <hr>
 
+<H2><a name="binary">Binary data file formats</a></H2>
+
+<p>
+
+Binary data file formats provide a convenient, efficient, and rapid access 
+to genomic data, either segments or quantitative data. They are indexed, 
+allowing for immediate random access to any location, and compressed 
+for disk space savings. The following file types are accepted:
+
+<p>
+
+<dl>
+  
+  <dt><b>Bam file</b>
+  <dd>The <a href="http://samtools.sourceforge.net">Bam file</a>
+  is a binary version of the text SAM format, which is a sequence alignment 
+  file for next generation sequencing technologies. Bam files usually 
+  contain millions of alignments of short sequence reads. They may be 
+  displayed in one of two ways: as a collection of segments with or without 
+  the DNA sequence of the read (depending on zoom level), or as a 
+  quantitative xyplot representing the coverage or depth of sequencing. Bam 
+  files may be generated, sorted, and/or indexed from Sam files using 
+  <a href="http://samtools.sourceforge.net">samtools</a> or 
+  <a href="http://picard.sourceforge.net">Picard</a>. Bam files have a ".bam" 
+  extension. Displaying Bam files requires the installation of the 
+  Bio::DB::Sam Perl module.
+
+  <dt><b>bigWig file</b>
+  <dd>The <a href="http://genome.ucsc.edu/goldenPath/help/bigWig.html">bigWig file</a>
+  is a binary version of the text Wiggle format for displaying dense 
+  quantitative data. BigWig files are generated from text wiggle files, 
+  either fixedStep, variableStep, or bedGraph variants, using either the 
+  wigToBigWig or bedGraphToBigWig utilities available from 
+  <a href=http://hgdownload.soe.ucsc.edu/admin/exe/>UCSC</a>. 
+  File conversion requires a file 
+  with the name and lengths of the chromosomes for your genome version. 
+  Such a file may be obtained by selecting the <i>Download Chrom Sizes</i> 
+  from the "File" menu in the upper left corner of the genome browser page. 
+  BigWig files usually have a ".bw" extension. Displaying bigWig files 
+  requires the installation of the Bio::DB::BigWig Perl module.
+  
+  <dt><b>Archive of bigWig files</b>
+  <dd>Two or more bigWig files may be combined as a BigWigSet collection, 
+  providing a fast, convenient method of transferring a related collection 
+  of genomic data files and grouping them into a single track, with each 
+  bigWig file represented as a subtrack selectable by display name. Supported 
+  archive formats include TAR and ZIP files. Two or more bigWig files (with 
+  a .bw extension) and optionally a metadata text file may be included. Extraneous 
+  files and directory paths are ignored.
+  
+  <dt><b>bigBed file</b>
+  <dd>The <a href="http://genome.ucsc.edu/goldenPath/help/bigBed.html">bigBed file</a>
+  is a binary version of the text BED format for displaying dense 
+  genomic regions or intervals. Data from bigBed files may be displayed as 
+  either segments or as a quantitative xyplot representing coverage (the 
+  depth of segments at any given locus). BigBed files are generated from text 
+  BED files using the bedToBigBed utility available from 
+  <a href=http://hgdownload.soe.ucsc.edu/admin/exe/>UCSC</a>. File conversion 
+  requires a file with the name and lengths of the chromosomes for your genome 
+  version. Such a file may be obtained by selecting the <i>Download Chrom Sizes</i> 
+  from the "File" menu in the upper left corner of the genome browser page. 
+  BigBed files usually have a ".bb" extension. Displaying bigBed files 
+  requires the installation of the Bio::DB::BigBed Perl module.
+  
+  <dt><b>useq file</b>
+  <dd>The <a href="http://useq.sourceforge.net/useqArchiveFormat.html">useq file</a>
+  is a compressed archive of either genomic intervals (with optional scores and/or text) 
+  or quantitative data. It is generated using utilities from the 
+  <a href="http://useq.sourceforge.net/">USeq analysis package</a>. Upon upload, 
+  the useq file is automatically converted to either a 
+  <a href="http://genome.ucsc.edu/goldenPath/help/bigWig.html">bigWig file</a> or 
+  <a href="http://genome.ucsc.edu/goldenPath/help/bigBed.html">bigBed file</a> 
+  depending on context. Processing useq files requires the installation of the 
+  USeq package, wigToBigWig and bedToBigBed 
+  <a href=http://hgdownload.soe.ucsc.edu/admin/exe/>utilities</a>, and the 
+  Bio::DB::BigWig Perl module. 
+
+</dl>
+
+<p>
+
+To import binary files, generate the files using the appropriate utility and 
+ensure they have the appropriate file extension. Under the "Custom Tracks" 
+tab, select the "From a file" link, click the "Choose file" button, and 
+navigate to your file using the Dialog box. Click "Upload" to upload the 
+file. Status reports may be printed as the file is uploaded and processed. 
+Successfully uploaded files will be displayed under the "Custom Tracks" and 
+"Select Tracks" tabs. 
+
+<p>
+
+A basic configuration will be generated appropriate for the file type uploaded. 
+The configuration may be edited by selecting the "edit" link adjacent to the 
+configuration file.
+  
+<hr>  
+
+
+
+
 <H2><a name="remote">Importing Custom Tracks via the Internet</a></H2>
 
 <p>
@@ -1121,24 +1232,25 @@ the URL of an annotation file. The following URL types are allowed:
       displayed. When you update the file, the updated version will be
       mirrored automatically.
 
-  <dt><b>The URL of a BigWig (.bw) file</b> <dd>This is a format that
-  allows you to import UCSC Wiggle tracks into the browser in a fast
-  and efficient manner.  If you haven't already done so, obtain the
-  BigWig tools from the <a
-  href="http://genome.ucsc.edu/goldenPath/help/bigWig.html">UCSC
-  Genome Browser</a> and run the wigToBigWig utility as described
-  there.  Name the file with a ".bw" extension (important!) and place
-  it on an FTP or Web server that can be accessed via this
-  browser. Finally paste the full URL of the BigWig file into the
-  "Import a track URL" field and press "upload". Note: To create the
-  BigWig file you will need a "chrom.sizes" file corresponding to the
-  chromosome lengths of the genome. To obtain a suitable file from the
-  browser, select <i>Download Chrom Sizes</i> from the "File" menu in
-  the upper left corner of the genome browser page.
-
-  <dt><b>The URL of a sorted, indexed BAM file</b>
-  <dd>Sort a BAM sequence alignment file and index it with
-      SamTools. Place the BAM file and its associated .bai index file on
+  <dt><b>The URL of a BigWig (.bw) file</b> 
+  <dd>The URL of a remote bigWig file may be provided. 
+  The file must have a ".bw" extension
+  and be located on an FTP or Web server that can be accessed via this
+  browser. Paste the full URL of the BigWig file into the
+  "Import a track URL" field and press "upload". 
+
+  <dt><b>The URL of a BigBed (.bb) file</b> 
+  <dd>The URL of a remote bigBed file may be provided. 
+  The file must have a ".bb" extension
+  and be located on an FTP or Web server that can be accessed via this
+  browser. Paste the full URL of the bigBed file into the
+  "Import a track URL" field and press "upload". 
+
+  <dt><b>The URL of a BAM file</b>
+  <dd>A sorted, indexed Bam file may be accessed remotely. Both the Bam file 
+  with a ".bam" extension and its corresponding ".bai" index file must located 
+  on Web or FTP server. Paste the full URL of the Bam file (NOT the .bai file) 
+  into the address field "From a URL" link Place the BAM file and its associated .bai index file on
       a web or FTP-accessible server and paste its URL into the "Import a
       track URL" field.
       The information in the file
@@ -1159,6 +1271,10 @@ the URL of an annotation file. The following URL types are allowed:
       locate DAS servers with useful genomic data. Then cut and paste the
       DAS server URL into the remote track URL box as before.
 </dl>
+
+Please ensure that genome versions, including chromosome names and lengths, match 
+between what is present in the file and the browser. 
+
   <hr>
 
 $Id$
diff --git a/htdocs/cloud_index.html b/htdocs/cloud_index.html
index d305ffb..53a5b34 100644
--- a/htdocs/cloud_index.html
+++ b/htdocs/cloud_index.html
@@ -33,6 +33,7 @@ A series of starter databases have been preconfigured for you, consisting of:
 <ul>
 <li><a href="/fgb2/gbrowse/sacCer3">S. cerevisiae genome (April 11, sacCer3), Genes and DNA only</a></li>
 <li><a href="/fgb2/gbrowse/yeast_advanced">S. cerevisiae, Chromosomes 1+2, (more tracks)</a></li>
+<li><a href="/fgb2/gbrowse/yeast_chado">S. cerevisiae, Chromosomes 1+2, (Chado database)</a></li>
 <li><a href="/fgb2/gbrowse/ce10">C. elegans genome (WS220, ce10), Genes and DNA only</a></li>
 <li><a href="/fgb2/gbrowse/hg19">H. sapiens genome (hg19, GRCh37), Genes and DNA only</a></li>
 </ul>
diff --git a/htdocs/css/karyotype.css b/htdocs/css/karyotype.css
index 3b25a76..d15c529 100644
--- a/htdocs/css/karyotype.css
+++ b/htdocs/css/karyotype.css
@@ -1,9 +1,9 @@
 div.hilite {
     position:     absolute;
-    background:   lightblue;
+    background:   blue;
     cursor:       pointer;
     filter:       alpha(opacity=50);
-    opacity:      0.5;
+    opacity:      0.7;
 }
 div.nohilite {
     position:   absolute;
@@ -11,9 +11,9 @@ div.nohilite {
 }
 span.hilite {
     position:     absolute;
-    background:   lightblue;
+    background:   blue;
     filter:       alpha(opacity=50);
-    opacity:      0.5;
+    opacity:      0.7;
 }
 span.nohilite {
     position:   absolute;
diff --git a/htdocs/css/tracks.css b/htdocs/css/tracks.css
index 7501f07..fabe1d1 100644
--- a/htdocs/css/tracks.css
+++ b/htdocs/css/tracks.css
@@ -16,6 +16,15 @@ IMG.trackimg      {
                        margin-top: -10px;
                        text-align: center;
                   }
+.titlebar_pinned  {
+                     background:#FFD39B;
+		     display: inline-block;
+		     text-align:left;
+		     width:100%;
+		     cursor: move;
+                     font-size: 9pt;
+                     font-weight: bold;
+                  }
 .titlebar         {  background-color: #FFD39B;
                      cursor: move;
                      z-index: 10;
@@ -39,3 +48,7 @@ IMG.trackimg      {
     font-weight:bold;
     font-size: 9pt;
 }
+.pin_button {
+    float:right;
+    cursor:pointer;
+}
diff --git a/htdocs/gbrowse_syn_help.html b/htdocs/gbrowse_syn_help.html
index 115d617..dd58c8d 100644
--- a/htdocs/gbrowse_syn_help.html
+++ b/htdocs/gbrowse_syn_help.html
@@ -14,9 +14,9 @@ The oryza data source contains genome annotations in GFF3 flat files
 for two rice species and blastz-derived whole genome aligment data between
  the two species.
 <ul>
-<li>/var/lib/gbrowse2/databases/gbrowse_syn/rice/rice.gff3
-<li>/var/lib/gbrowse2/databases/gbrowse_syn/wild_rice/wild_rice.gff3
-<li>/var/lib/gbrowse2/databases/gbrowse_syn/alignments/rice.aln
+<li>$DATABASES/gbrowse_syn/rice/rice.gff3
+<li>$DATABASES/gbrowse_syn/wild_rice/wild_rice.gff3
+<li>$DATABASES/gbrowse_syn/alignments/rice.aln
 </ul>
 
 
@@ -27,9 +27,9 @@ Configuration files for the two species can be found at the locations
  species. They are already set up to use the in-memory adapter and the GFF3 flat files listed
  above.
 <ul>
-<li>/etc/gbrowse2/synteny/oryza.synconf.disabled</li>
-<li>/etc/gbrowse2/synteny/rice_synteny.conf</li>
-<li>/etc/gbrowse2/synteny/wild_rice_synteny.conf</li>
+<li>$CONF/synteny/oryza.synconf.disabled</li>
+<li>$CONF/synteny/rice_synteny.conf</li>
+<li>$CONF/synteny/wild_rice_synteny.conf</li>
 </ul>
 
 <h2>Activate the Example Data Source</h2>
@@ -42,128 +42,102 @@ $ mysql -uuser -ppass
 mysql> create database rice_synteny;
 Query OK, 1 row affected (0.00 sec)
 
-mysql> grant SELECT on rice_synteny.* to 'www-data'@'localhost';
+mysql> grant SELECT on *.* to 'www-data'@'localhost';
 Query OK, 0 rows affected (0.02 sec)
 
 mysql> quit
 Bye
 </pre>
 
-<p>
 2: populate the database using the <a href="http://gmod.org/wiki/GBrowse_syn_Scripts#load_alignments_msa.pl">
  gbrowse_syn_load_alignments_msa.pl script</a> (pre-installed with GBrowse).  This will load the 
  CLUSTALW-formated alignment file above into the database.
 
 <pre>
-<<<<<<< HEAD
-$ cd /var/www/gbrowse2/databases/gbrowse_syn/alignments
-$ sudo gunzip rice.aln.gz
-$ gbrowse_syn_load_alignments_msa.pl -u user -p pass -d rice_synteny -c -v rice.aln
-=======
-$ cd /var/lib/gbrowse2/databases/gbrowse_syn/alignments
-$ gunzip -c rice.aln.gz | gbrowse_syn_load_alignments_msa.pl -u user -p pass -d rice_synteny -c -v -
->>>>>>> master
+$ zcat $DATABASES/gbrowse_syn/alignments/rice.aln.gz | gbrowse_syn_load_alignments_msa.pl -u user -p pass -d rice_synteny -v -c -
 </pre>
-<i>Where 'user' and 'pass' correspond to a mysql account with root-level privileges</i> 
 
-<p>
-3: activate the oryza gbrwose_syn configuration file by renaming it (root-level acess may be required).
+3: activate the oryza gbrwose_syn configuration file by renaming it (root-level acess required).
 
 <pre>
-<<<<<<< HEAD
 # as a sudoer
-$ sudo mv /etc/gbrowse2/synteny/oryza.synconf.disabled /etc/gbrowse2/synteny/oryza.synconf
+$ sudo mv $CONF/synteny/oryza.synconf.disabled $CONF/synteny/oryza.synconf
 
 # or as root
-% mv /etc/gbrowse2/synteny/oryza.synconf.disabled /etc/gbrowse2/synteny/oryza.synconf
-=======
-$ mv /etc/gbrowse2/synteny/oryza.synconf.disabled /etc/gbrowse2/synteny/oryza.synconf
->>>>>>> master
+% mv $CONF/synteny/oryza.synconf.disabled $CONF/synteny/oryza.synconf
 </pre>
 
-<p>
-Now reload this page and view the result!
 
 <h1> Advanced (optional) </h1>
 You can speed up the image loading time by putting your species' GFF3 data into relational MySQL databases.
 
-1: create a database for each of the GFF<p>
- data files (rice.gff3 and wild_rice.gff3).
+1: create a database for each of the GFF data files (rice.gff3 and wild_rice.gff3).
 <pre>
 # create a mysql database for the rice data
-$ mysql -uuser -ppass
+$ mysql -uuser -ppass  
 
 mysql> create database rice;
 Query OK, 1 row affected (0.00 sec)
 
-mysql> grant SELECT on rice.* to 'www-data'@'localhost';
-Query OK, 0 rows affected (0.00 sec)
-
 mysql> create database wild_rice;
 Query OK, 1 row affected (0.00 sec)
 
-mysql> grant SELECT on wild_rice.* to 'www-data'@'localhost';
-Query OK, 0 rows affected (0.00 sec)
 </pre>
 
 
-2: populate the database using the <a
+2: populate the database using the <a 
  href="http://code.open-bio.org/svnweb/index.cgi/bioperl/view/bioperl-live/trunk/scripts/Bio-SeqFeature-Store/bp_seqfeature_load.PLS">
  bp_seqfeature_load.pl</a> (pre-installed as part of BioPerl with GBrowse).  This will load the
-<<<<<<< HEAD
  GFF3 data into a mySQL relational database.
 
 <b>Note the mySQL user will need CREATE and INSERT privileges.</b>
 <pre>
-$ bp_seqfeature_load.pl -u user -p pass -d rice -c -f /var/www/gbrowse2/$GBROWSE_ROOT/databases/gbrowse_syn/rice/rice.gff3
+$ bp_seqfeature_load.pl -u user -p pass -d rice -c -f /var/www/html/gbrowse/databases/gbrowse_syn/rice/rice.gff3
 loading /var/www/html/gbrowse/databases/gbrowse_syn/rice/rice.gff3...
-Building object tree... 1.05s7s
+Building object tree... 1.05s7s                                                                                                          
 Loading bulk data into database... 0.67s
 load time: 31.40s
 
-$ bp_seqfeature_load.pl -u user -p pass -d wild_rice -c -f /var/www/gbrowse2/$GBROWSE_ROOT/databases/gbrowse_syn/wild_rice/wild_rice.gff3
+$ bp_seqfeature_load.pl -u user -p pass -d wild_rice -c -f /var/www/html/gbrowse/databases/gbrowse_syn/wild_rice/wild_rice.gff3
 loading /var/www/html/gbrowse/databases/gbrowse_syn/wild_rice/wild_rice.gff3...
-Building object tree... 1.15s9s
+Building object tree... 1.15s9s                                                                                                          
 Loading bulk data into database... 0.69s
 load time: 31.93s
 
 </pre>
 
-3: Modify the following stanza in each configurations file (rice_syntency.conf and wild_rice_synteny),
+3: Modify the following stanza in each configurations file (rice_syntency.conf and wild_rice_synteny), 
 changing the dsn argumant as required for each data source.
-=======
- GFF3 data into a MySQL relational database.
->>>>>>> master
 
-<b>Note the MySQL user will need CREATE and INSERT privileges.</b>
+You will need to set the permissions so you can edit the files
 <pre>
-<<<<<<< HEAD
-
+$ sudo chmod 644 $CONF/synteny/*
+</pre>
 
-<b>Note the mySQL user will need CREATE and INSERT privileges.</b>
+Change:
 <pre>
-$ bp_seqfeature_load.pl -u user -p pass -d rice -c -f /var/www/gbrowse2/$GBROWSE_ROOT/databases/gbrowse_syn/rice/rice.gff3
-=======
-$ bp_seqfeature_load.pl -u user -p pass -d rice -c -f /var/lib/gbrowse2/databases/gbrowse_syn/rice/rice.gff3
->>>>>>> master
-loading /var/www/html/gbrowse/databases/gbrowse_syn/rice/rice.gff3...
-Building object tree... 1.05s7s
-Loading bulk data into database... 0.67s
-load time: 31.40s
+# from
+db_adaptor    = Bio::DB::SeqFeature::Store
+db_args       = -adaptor memory
+                -dir    $DATABASES/gbrowse_syn/rice
 
-<<<<<<< HEAD
-$ bp_seqfeature_load.pl -u user -p pass -d wild_rice -c -f /var/www/gbrowse2/$GBROWSE_ROOT/databases/gbrowse_syn/wild_rice/wild_rice.gff3
-=======
-$ bp_seqfeature_load.pl -u user -p pass -d wild_rice -c -f /var/lib/gbrowse2/databases/gbrowse_syn/wild_rice/wild_rice.gff3
->>>>>>> master
-loading /var/www/html/gbrowse/databases/gbrowse_syn/wild_rice/wild_rice.gff3...
-Building object tree... 1.15s9s
-Loading bulk data into database... 0.69s
-load time: 31.93s
+# to 
+db_adaptor    = Bio::DB::SeqFeature::Store
+db_args       = -adaptor DBI::mysql
+                -dsn dbi:mysql:rice
+                -user www-data
 
 </pre>
 
-3: Modify the following stanza in each configurations file (rice_syntency.conf and wild_rice_synteny),
-changing the dsn argumant as required for each data source.
+
+<h1> Usage example </h1>
 
 <pre>
+An example over sugarcane and sorghum genome synteny:
+</pre>
+<center>
+<img src="images/help/sugarcane_sorghum_synteny.png" class="figure">
+</center>
+
+
+
diff --git a/htdocs/images/buttons/pop_in.png b/htdocs/images/buttons/pop_in.png
new file mode 100644
index 0000000..0168d65
Binary files /dev/null and b/htdocs/images/buttons/pop_in.png differ
diff --git a/htdocs/images/buttons/pop_out.png b/htdocs/images/buttons/pop_out.png
new file mode 100644
index 0000000..d9a2581
Binary files /dev/null and b/htdocs/images/buttons/pop_out.png differ
diff --git a/htdocs/images/help/sugarcane_sorghum_synteny.png b/htdocs/images/help/sugarcane_sorghum_synteny.png
new file mode 100644
index 0000000..8b6589e
Binary files /dev/null and b/htdocs/images/help/sugarcane_sorghum_synteny.png differ
diff --git a/htdocs/index.html b/htdocs/index.html
index 73a7258..26a5ebb 100644
--- a/htdocs/index.html
+++ b/htdocs/index.html
@@ -61,17 +61,31 @@ configuration, you can use the alias /gb2 as an alternative to
 
 <h3>Accelerated Demos</h3>
 
-If you have FastCGI and/or ModPerl installed, you will have access to
+If you have mod_fcgid, mod_fastcgi and/or ModPerl installed, you will have access to
 an accelerated version of gbrowse at these URLs:
 
 <ul>
-  <li>FastCGI
+  <li>mod_fcgid
       <ul>
 	<li><a href="/fgb2/gbrowse/yeast">http://your.host/fgb2/gbrowse/yeast</a></li>
 	<li><a href="/fgb2/gbrowse/yeast_advanced">http://your.host/fgb2/gbrowse/yeast_advanced</a></li>
 	<li><a href="/fgb2/gbrowse/yeast_renderfarm">http://your.host/fgb2/gbrowse/yeast_renderfarm</a></li>
       </ul>
   </li>
+  <li>mod_fastcgi
+      <ul>
+	<li><a href="/fast/gbrowse/yeast">http://your.host/fast/gbrowse/yeast</a></li>
+	<li><a href="/fast/gbrowse/yeast_advanced">http://your.host/fast/gbrowse/yeast_advanced</a></li>
+	<li><a href="/fast/gbrowse/yeast_renderfarm">http://your.host/fast/gbrowse/yeast_renderfarm</a></li>
+      </ul>
+  </li>
+  <li>mod_perl
+      <ul>
+	<li><a href="/mgb2/gbrowse/yeast">http://your.host/mgb2/gbrowse/yeast</a></li>
+	<li><a href="/mgb2/gbrowse/yeast_advanced">http://your.host/mgb2/gbrowse/yeast_advanced</a></li>
+	<li><a href="/mgb2/gbrowse/yeast_renderfarm">http://your.host/mgb2/gbrowse/yeast_renderfarm</a></li>
+      </ul>
+  </li>
 </ul>
 
 
diff --git a/htdocs/js/buttons.js b/htdocs/js/buttons.js
index cbe3038..1fd2bad 100644
--- a/htdocs/js/buttons.js
+++ b/htdocs/js/buttons.js
@@ -91,6 +91,8 @@ function ShowHideTrack(track_name,visible) {
               element.style.display="none";
               Controller.set_track_visibility(gbtrack.track_id, 0);
           }
+	  if (element.style.position=='absolute')
+	      Controller.popin_ghost(element);
        }
      });
 }
diff --git a/htdocs/js/controller.js b/htdocs/js/controller.js
index e974e11..8f01ccf 100644
--- a/htdocs/js/controller.js
+++ b/htdocs/js/controller.js
@@ -35,7 +35,7 @@ var userdata_table_id       = 'userdata_table_div';
 var custom_tracks_id        = 'custom_tracks';
 var community_tracks_id     = 'community_tracks';
 var snapshot_table_id 	    = 'snapshots_page';
-var GlobalDrag;
+var GlobalDrag              = {};
 
 //  Sorta Constants
 var expired_limit  = 1;
@@ -108,6 +108,7 @@ var GBrowseController = Class.create({
 		}
 		TrackPan.update_draggables();
 		updateRuler();
+		Controller.update_ghosts();
 	},
   
     register_track:
@@ -865,12 +866,10 @@ var GBrowseController = Class.create({
 
     plugin_go:
     function(plugin_base,plugin_type,plugin_action,source) {
+        var select_box = document.pluginform.plugin;
+        var track_name = 'plugin:'+select_box.options[select_box.selectedIndex].value;
         if (plugin_type == 'annotator'){
-            var select_box = document.pluginform.plugin;
-            var track_name = select_box.options[select_box.selectedIndex].attributes.getNamedItem('track_name').value;
-
-            this.add_track(track_name);
-            Controller.update_sections(new Array(track_listing_id),null,null,false);
+	    ShowHideTrack(track_name,true);
         } else if (plugin_type == 'dumper') {
             var loc_str = "?plugin="+plugin_base+";plugin_action="+encodeURI(plugin_action);
             loc_str += ';view_start=' + TrackPan.get_start();
@@ -882,12 +881,25 @@ var GBrowseController = Class.create({
                 window.open(loc_str);
             }
         } else if (plugin_type == 'filter'){
-            // Go doesn't do anything for filter
-            return false; 
+	    $('configure_plugin_button').click();
+//	    this.reconfigure_plugin(this.translate('CONFIGURE_PLUGIN'),
+//				    track_name,
+//				    plugin_configure_div,
+//				    'filter');
         } else if (plugin_type == 'finder'){
-            document.searchform.plugin_find.value  = $F('plugin');
-            document.searchform.force_submit.value = 1;
-            document.searchform.submit();
+            input = document.createElement("input");
+            input.setAttribute("type", "hidden");
+            input.setAttribute("name", "plugin_action");
+            input.setAttribute("id","plugin_action");
+            input.setAttribute("value", plugin_action);
+            document.getElementById("configure_plugin").appendChild(input);
+            plugin = document.createElement("input");
+            plugin.setAttribute("type", "hidden");
+            plugin.setAttribute("name", "plugin");
+            plugin.setAttribute("id","plugin");
+            plugin.setAttribute("value", plugin_base);
+            document.getElementById("configure_plugin").appendChild(plugin);
+            document.configure_plugin.submit();
         }
     }, // end plugin_go
 
@@ -1297,6 +1309,102 @@ show_info_message:
       $('dialog_123').remove();
   },
 
+  ghost_track:
+	function (el) {
+	    var d = el.ancestors().find(function (a) {return a.hasClassName("track")});
+	    if (d.style.position == 'absolute') {
+		this.popin_ghost(d);
+	    } else {
+		this.popout_ghost(d);
+	    }
+	},
+
+   popin_ghost:
+	function(d) {
+	    // ghost track pops back in...
+	    var current_top = d.cumulativeOffset().top;
+	    Sortable.destroy(d.id);
+	    d._draggable.destroy();
+	    d.style.position='relative';
+	    d.style.left='0px';
+	    d.style.top='0px';
+	    d.style.height='auto';
+	    d.style.outlineStyle="";
+	    d.style.opacity=1.0;
+	    d.select('img.pin_button').each(function(a) {
+		    a.src=Controller.button_url('pop_out.png');
+		});
+	    d.select('span.titlebar_pinned').each(function(a) {
+		    a.removeClassName('titlebar_pinned');
+		    a.addClassName('titlebar');
+		});
+	    d.removeClassName('ghost');
+	    var list   = d.parentNode.select('div.track');
+	    var tracks = list[0].select('div.track');
+	    var overlapping_element = tracks[0];
+	    var direction = {before:d};
+	    tracks.each(function(a) { 
+		    var el_top    = a.cumulativeOffset().top;
+		    var el_bottom = el_top + a.getHeight();
+		    if (el_top <= current_top) {  // pops in here
+			var middle = (el_top+el_bottom)/2;
+			var dir    = middle < current_top ? 'after' : 'before';
+			direction[dir]=d;
+			overlapping_element=a;
+		    }
+		});
+	    overlapping_element.insert(direction);
+	    var container = d.parentNode;
+	    var drag = create_drag(container);
+	    Sortable.sortables[container.id].onUpdate();
+	},
+
+   popout_ghost:
+	function(d) {
+
+	    // ghost track pops out
+	    var container = d.parentNode;
+	    var left  = d.cumulativeOffset().left;
+	    var top   = d.cumulativeOffset().top;
+
+	    console.log(container.id);
+	    d.absolutize();
+	    d.style.left=left+'px';
+	    d.style.top =top+'px';
+	    d.addClassName('ghost');
+
+	    Sortable.destroy(container.id);
+	    container.parentNode.insert(d);
+	    create_drag(container.id,'track');
+
+	    d.select('span.titlebar').each(function(a) {
+		    a.removeClassName('titlebar');
+		    a.addClassName('titlebar_pinned');
+		    d.style.height = (d.getHeight() + a.getHeight()) + 'px';
+		});
+	    d.style.outlineStyle="double";
+	    d.style.opacity=0.90;
+	    d.select('img.pin_button').each(function(a) {
+			a.src=Controller.button_url('pop_in.png');
+		});
+	    d._draggable= new Draggable(d,{constraint:'vertical',
+					   scroll: window,
+					   zindex: 1000});
+	    return true;
+	},
+
+  update_ghosts: 
+	function()  {
+	    $$('div.ghost').each(function(d) {
+		    d.select('span.titlebar').each(function(a) {
+			    a.removeClassName('titlebar');
+			    a.addClassName('titlebar_pinned');
+			    d.style.height='auto';
+			});
+		});
+	},
+
+	
   // Looks up a key in the language table. If not found, checks the defaults table.
   // If the translation contains %s, substitutes additional parameters for each occurance of %s (in order)
   // Usage: Controller.translate(key, [...])
@@ -1467,8 +1575,8 @@ function actually_remove (element_name) {
 }
 
 function create_drag (div_name) {
-   GlobalDrag = div_name;
-   Sortable.create(
+   GlobalDrag[div_name] = div_name;
+   return Sortable.create(
 		  div_name,
 		  {
 		      tag:     'div',
diff --git a/htdocs/js/dragdrop.js b/htdocs/js/dragdrop.js
index 2889653..e831c25 100644
--- a/htdocs/js/dragdrop.js
+++ b/htdocs/js/dragdrop.js
@@ -798,7 +798,8 @@ var Sortable = {
         dropon.parentNode.insertBefore(element, dropon);
         if(dropon.parentNode!=oldParentNode)
           Sortable.options(oldParentNode).onChange(element);
-        Sortable.options(dropon.parentNode).onChange(element);
+	if (Sortable.options(dropon.parentNode) != null)
+	    Sortable.options(dropon.parentNode).onChange(element);
       }
     } else {
       Sortable.mark(dropon, 'after');
@@ -866,7 +867,7 @@ var Sortable = {
     Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
 
     if(position=='after')
-      if(sortable.overlap == 'horizontal')
+      if(sortable != null && sortable.overlap == 'horizontal')
         Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
       else
         Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
diff --git a/htdocs/js/login.js b/htdocs/js/login.js
index d49d09d..94dff44 100644
--- a/htdocs/js/login.js
+++ b/htdocs/js/login.js
@@ -275,6 +275,8 @@ function login_page_change(page) {
         $('loginTitle').innerHTML = Controller.translate('REGISTER');
         $('loginSubmit').value    = Controller.translate('REGISTER');
 	login_show_rows('loginNorm',new Array('loginERow','loginFRow','loginP2Row'));
+	$('loginSubmit').show();
+	$('loginCancel').show();
 	// $('loginERow').show();
 	// $('loginFRow').show();
 	// $('loginP2Row').show();
@@ -508,6 +510,7 @@ function add_user() {
 	    } else if (results == 'Success') {
 		$('loginWarning').innerHTML = Controller.translate('CONFIRMATION_EMAIL_SENT');
                 UsingOpenID = false;
+		$('loginCancel').hide();
                 login_user(username);
             } else {
                 $('loginWarning').innerHTML = results;
@@ -569,10 +572,11 @@ function edit_confirmation(resend) {
 		    $('loginWarning').innerHTML = Controller.translate('CANNOT_CONNECT_MAIL');
 		} else {
 		    $('loginWarning').style.color = 'blue';
-		    $('loginWarning').innerHTML   = Controller.translate('CONFIRMATION_EMAIL_SENT');
+		    $('loginWarning').innerHTML = transport.responseText;
 		}
-		$('loginURow').hide(); $('loginERow').hide();  $('loginBreak').hide();
-		$('loginPRow').hide(); $('loginP2Row').hide(); $('loginSubmit').hide();
+		$('loginURow').hide();    $('loginERow').hide();  $('loginBreak').hide();
+	        $('loginPRow').hide();    $('loginP2Row').hide(); $('loginSubmit').hide();
+	        $('loginOptions').hide(); $('loginOpenID').hide();
 		$('loginFRow').hide()
 		login_loading(false);
 		$('loginWarning').show();
diff --git a/htdocs/js/overviewSelect.js b/htdocs/js/overviewSelect.js
index 2e6bd74..7286e8f 100644
--- a/htdocs/js/overviewSelect.js
+++ b/htdocs/js/overviewSelect.js
@@ -32,7 +32,7 @@ Overview.prototype.initialize = function() {
   var self = new Overview;
 
   // not ready for non drag and drop implementation
-  //var dnd = document.mainform.drag_and_drop;
+  //var dnd = document.searchform.drag_and_drop;
   //if (!dnd || !dnd.checked) return false;
 
   var i = $(self.imageId);
@@ -49,26 +49,7 @@ Overview.prototype.initialize = function() {
   var p = i.parentNode.parentNode;
   i = self.replaceImage(i);
 
-  self.selectLayer = p.parentNode.parentNode;
-
-
-//   try {
-//       overviewBalloon = new Balloon();
-//       overviewBalloon.vOffset  = 1;
-//       overviewBalloon.showOnly = 2; // just show twice
-//       var helpFunction = function(event) {
-// 	  if (!event) {
-// 	      event = window.event;
-// 	  }
-// 	  var help = '<b>Overview:</b> Click here to recenter or click and drag left or right to select a region';
-// 	  overviewBalloon.showTooltip(event,help,0,250);
-//       }
-//       i.onmouseover = helpFunction;
-//   }
-//   catch(e) {
-//       i.setAttribute('title','click and drag to select a region');
-//   }
-
+  self.selectLayer = $('overview_panels');
   self.scalebar = i;
 
   self.addSelectMenu('overview');
@@ -83,19 +64,18 @@ Overview.prototype.startSelection = function(event) {
   SelectArea.prototype.startRubber(self,event);
 }
 
-
 Overview.prototype.getSegment = function(i) {
-  this.ref          = document.mainform.ref.value;
-  this.segmentStart = parseInt(document.mainform.overview_start.value);
-  this.segmentEnd   = parseInt(document.mainform.overview_stop.value);
-  this.detailStart  = parseInt(document.mainform.detail_start.value);
-  this.detailEnd    = parseInt(document.mainform.detail_stop.value);
-  this.padLeft      = parseInt(document.mainform.image_padding.value);
-  this.pixelToDNA   = parseFloat(document.mainform.overview_pixel_ratio.value);
+  this.ref          = document.searchform.ref.value;
+  this.segmentStart = parseInt(document.searchform.overview_start.value);
+  this.segmentEnd   = parseInt(document.searchform.overview_stop.value);
+  this.detailStart  = parseInt(document.searchform.detail_start.value);
+  this.detailEnd    = parseInt(document.searchform.detail_stop.value);
+  this.padLeft      = parseInt(document.searchform.image_padding.value);
+  this.pixelToDNA   = parseFloat(document.searchform.overview_pixel_ratio.value);
   this.flip         = 0;
 
   var actualWidth   = this.elementLocation(i,'width');
-  var expectedWidth = parseInt(document.mainform.overview_width.value);
+  var expectedWidth = parseInt(document.searchform.overview_width.value);
   if (actualWidth > expectedWidth) {
     this.padLeft     += actualWidth - expectedWidth;
   }
diff --git a/htdocs/js/toggle.js b/htdocs/js/toggle.js
new file mode 100644
index 0000000..83456e0
--- /dev/null
+++ b/htdocs/js/toggle.js
@@ -0,0 +1,58 @@
+function turnOn (element) {
+  element.style.display="inline";
+}
+function turnOff (element) {
+  element.style.display="none";
+}
+
+function setVisState (element_name,is_visible) {
+  var postData = 'div_visible_'+ element_name + '=' + is_visible;
+  new Ajax.Request(document.URL,{method:'post',postBody:postData});
+}
+
+function visibility (element_name,is_visible) {
+   var element = document.getElementById(element_name);
+   var show_control = document.getElementById(element_name + "_show");
+   var hide_control = document.getElementById(element_name + "_hide");
+   if (is_visible == 1) {
+      turnOn(element);
+      turnOff(show_control);
+      turnOn(hide_control);
+   } else {
+      turnOff(element);
+      turnOff(hide_control);
+      turnOn(show_control);
+   }
+   setVisState(element_name,is_visible);
+   return false;
+}
+
+function collapse(element_name) {
+   var control = document.getElementById(element_name+"_title");
+   var icon    = document.getElementById(element_name+"_icon");
+   var body    = document.getElementById(element_name+"_image");
+   var pad     = document.getElementById(element_name+"_pad");
+   var closeit = body.style.display != "none";
+   var src     = new String(icon.src);
+   if (closeit) {
+     icon.src = src.replace(/minus/,'plus');
+     body.style.display = 'none';
+     pad.style.display = 'inline';
+     control.className = 'titlebar_inactive';
+   } else {
+     icon.src = src.replace(/plus/,'minus');
+     body.style.display = 'inline';
+     pad.style.display = 'none';
+     control.className = 'titlebar';
+   }
+   var postData = 'track_collapse_'+ element_name + '=' + (closeit ? 1 : 0);
+   new Ajax.Request(document.URL,{method:'post',postBody:postData});
+   return false;
+}
+
+function enable_keypos (checkbox) {
+  var checked = checkbox.checked;
+  var ks      = document.getElementsByName('ks');
+  for (var i=0;i<ks.length;i++) {ks[i].disabled= checked}
+  document.getElementById('ks_label').style.color=checked ? 'lightgray' : 'black';
+}
diff --git a/htdocs/js/track_pan.js b/htdocs/js/track_pan.js
index 2348d61..f44bd86 100644
--- a/htdocs/js/track_pan.js
+++ b/htdocs/js/track_pan.js
@@ -74,7 +74,7 @@ var GBrowseTrackPan = Class.create({
 			    top:             '0px',
 			    borderLeft:      '1px solid ' + this.marker_outline,
 			    borderRight:     '1px solid ' + this.marker_outline,
-			    height:          '400px',
+		            height:          this.marker_height,
 			    cursor:          'text',
 			    zIndex:          5
 			    });
@@ -193,7 +193,7 @@ var GBrowseTrackPan = Class.create({
 		}
 		if (!Prototype.Browser.IE)
 		    gbtrack.get_image_div().setStyle({cursor: 'url('+Controller.button_url('cursor-ewmove.ico')+'), move'});
-		new Draggable(gbtrack.get_image_div(), {
+		    new Draggable(gbtrack.get_image_div(), {
 			constraint: 'horizontal',
 			zindex: 0, // defaults to 1000, which we don't want because it covers labels
 			starteffect: false,
@@ -251,6 +251,7 @@ var GBrowseTrackPan = Class.create({
 		this.length_label         = segment_info.length_label;
 		this.marker_fill          = segment_info.hilite_fill;
 		this.marker_outline       = segment_info.hilite_outline;
+	        this.marker_height        = segment_info.hilite_height;
 		this.flip                 = segment_info.flip;
 		this.initial_view_start   = parseInt(segment_info.initial_view_start);
 		this.initial_view_stop    = parseInt(segment_info.initial_view_stop);
diff --git a/htdocs/tutorial/conf_files/elegans_core.conf b/htdocs/tutorial/conf_files/elegans_core.conf
index 973ba36..4fefc06 100644
--- a/htdocs/tutorial/conf_files/elegans_core.conf
+++ b/htdocs/tutorial/conf_files/elegans_core.conf
@@ -1,7 +1,7 @@
 [GENERAL]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-	        -dir     "/var/www/gbrowse2/databases/elegans_core"
+	        -dir     "$DATABASES/elegans_core"
 
 plugins     = Aligner RestrictionAnnotator
 
diff --git a/htdocs/tutorial/conf_files/elegans_extra.conf b/htdocs/tutorial/conf_files/elegans_extra.conf
index 254c69f..d2d5f41 100644
--- a/htdocs/tutorial/conf_files/elegans_extra.conf
+++ b/htdocs/tutorial/conf_files/elegans_extra.conf
@@ -2,7 +2,7 @@
 description   = C. elegans Extra Annotations
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-	        -dir    "/var/www/gbrowse2/databases/elegans_extra"
+	        -dir    "$DATABASES/elegans_extra"
 
 # options
 drag and drop       = 1
diff --git a/htdocs/tutorial/conf_files/volvox.conf b/htdocs/tutorial/conf_files/volvox.conf
index bf50b47..3a44687 100644
--- a/htdocs/tutorial/conf_files/volvox.conf
+++ b/htdocs/tutorial/conf_files/volvox.conf
@@ -1,7 +1,7 @@
 [GENERAL]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-		-dir '/var/www/gbrowse2/databases/volvox'
+		-dir '$DATABASES/volvox'
 
 # just the basic track dumper plugin
 plugins     = TrackDumper
diff --git a/htdocs/tutorial/conf_files/volvox_final.conf b/htdocs/tutorial/conf_files/volvox_final.conf
index c52d744..e87b734 100644
--- a/htdocs/tutorial/conf_files/volvox_final.conf
+++ b/htdocs/tutorial/conf_files/volvox_final.conf
@@ -1,7 +1,7 @@
 [GENERAL]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-		-dir '/var/www/gbrowse2/databases/volvox'
+		-dir '$DATABASES/volvox'
 
 plugins     = Aligner RestrictionAnnotator TrackDumper
 
diff --git a/htdocs/tutorial/conf_files/volvox_final_withPhylo.conf b/htdocs/tutorial/conf_files/volvox_final_withPhylo.conf
index 8a5ec48..c206915 100644
--- a/htdocs/tutorial/conf_files/volvox_final_withPhylo.conf
+++ b/htdocs/tutorial/conf_files/volvox_final_withPhylo.conf
@@ -2,7 +2,7 @@
 description   = Volvox Example Database
 db_adaptor    = Bio::DB::GFF
 db_args       = -adaptor memory
-	        -dir     '/var/www/gbrowse2/databases/volvox'
+	        -dir     '$DATABASES/volvox'
 
 aggregators = match
 	      BAC{left_end_read,right_end_read/BAC}
diff --git a/htdocs/tutorial/conf_files/volvox_halfway.conf b/htdocs/tutorial/conf_files/volvox_halfway.conf
index e0131d0..1bbbe68 100644
--- a/htdocs/tutorial/conf_files/volvox_halfway.conf
+++ b/htdocs/tutorial/conf_files/volvox_halfway.conf
@@ -1,7 +1,7 @@
 [GENERAL]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-		-dir '/var/www/gbrowse2/databases/volvox'
+		-dir '$DATABASES/volvox'
 
 plugins     = Aligner RestrictionAnnotator TrackDumper
 
diff --git a/htdocs/tutorial/conf_files/volvox_quarter.conf b/htdocs/tutorial/conf_files/volvox_quarter.conf
index fc9d6e4..1037759 100644
--- a/htdocs/tutorial/conf_files/volvox_quarter.conf
+++ b/htdocs/tutorial/conf_files/volvox_quarter.conf
@@ -1,7 +1,7 @@
 [GENERAL]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-		-dir '/var/www/gbrowse2/databases/volvox'
+		-dir '$DATABASES/volvox'
 
 plugins     = Aligner RestrictionAnnotator TrackDumper
 
diff --git a/htdocs/tutorial/conf_files/volvox_refactored.conf b/htdocs/tutorial/conf_files/volvox_refactored.conf
index fd3e59e..a0501eb 100644
--- a/htdocs/tutorial/conf_files/volvox_refactored.conf
+++ b/htdocs/tutorial/conf_files/volvox_refactored.conf
@@ -1,5 +1,5 @@
 [GENERAL]
-databse     = basic
+database     = basic
 
 plugins     = Aligner RestrictionAnnotator BatchDumper TrackDumper
 
@@ -21,22 +21,22 @@ initial landmark = ctgA:5000..10000
 [basic:database]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-		-dir '/var/www/gbrowse2/databases/volvox_basic'
+		-dir '$DATABASES/volvox_basic'
 
 [genes:database]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-		-dir '/var/www/gbrowse2/databases/volvox_genes'
+		-dir '$DATABASES/volvox_genes'
 
 [alignments:database]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-		-dir '/var/www/gbrowse2/databases/volvox_alignments'
+		-dir '$DATABASES/volvox_alignments'
 
 [expression:database]
 db_adaptor    = Bio::DB::SeqFeature::Store
 db_args       = -adaptor memory
-		-dir '/var/www/gbrowse2/databases/volvox_expression'
+		-dir '$DATABASES/volvox_expression'
 
 ########################
 # Default glyph settings
diff --git a/htdocs/tutorial/data_files/volvox_all.gff3 b/htdocs/tutorial/data_files/volvox_all.gff3
index 3c879a9..3b53e6a 100644
--- a/htdocs/tutorial/data_files/volvox_all.gff3
+++ b/htdocs/tutorial/data_files/volvox_all.gff3
@@ -247,5 +247,5 @@ ctgA	est	EST_match	7000	7200	.	+	.	ID=Match5;Name=agt767.5;Target=agt767.5 853 1
 ctgA	est	EST_match	8000	9000	.	-	.	ID=Match6;Name=agt767.3;Target=agt767.3 1 1001
 
 
-ctgA	.	microarray_oligo	1	50000	.	.	.	Name=example;wigfile=/var/www/gbrowse2/databases/volvox/track001.ctgA.1202327456.wig
+ctgA	.	microarray_oligo	1	50000	.	.	.	Name=example;wigfile=$DATABASES/volvox/track001.ctgA.1202327456.wig
 ctgA	example	read	44401	45925	.	+	.	Name=trace;trace=volvox_trace.scf
\ No newline at end of file
diff --git a/htdocs/tutorial/data_files/volvox_microarray.gff3 b/htdocs/tutorial/data_files/volvox_microarray.gff3
index 2fe3798..e6e9f3e 100644
--- a/htdocs/tutorial/data_files/volvox_microarray.gff3
+++ b/htdocs/tutorial/data_files/volvox_microarray.gff3
@@ -1,3 +1,3 @@
 ##gff-version 3
 
-ctgA	.	microarray_oligo	1	50000	.	.	.	Name=example;wigfile=/var/www/gbrowse2/databases/volvox/track001.ctgA.1202327456.wig
\ No newline at end of file
+ctgA	.	microarray_oligo	1	50000	.	.	.	Name=example;wigfile=$DATABASES/volvox/track001.ctgA.1202327456.wig
\ No newline at end of file
diff --git a/install_util/GBrowseGuessDirectories.pm b/install_util/GBrowseGuessDirectories.pm
index d0d450f..4bf7148 100644
--- a/install_util/GBrowseGuessDirectories.pm
+++ b/install_util/GBrowseGuessDirectories.pm
@@ -81,6 +81,7 @@ sub apache_root {
     }
   } else {
       for (
+	  '/etc/apache2',
 	  '/usr/local/apache2',  # standard apache2 install
 	  '/usr/local/apache',   # standard apache install
 	  '/opt/apache2',
@@ -190,10 +191,11 @@ sub apachemodules {
 # to httpd.conf without modifying the main file
 sub apache_includes {
     my $self = shift;
-    return '/etc/apache2/other'  if -d '/etc/apache2/other'; # why cant macos do things like everybody else?
-    return '/etc/apache2/conf.d' if -d '/etc/apache2/conf.d';
-    return '/etc/apache/conf.d'  if -d '/etc/apache/conf.d';
-    return '/etc/httpd/conf.d'   if -d '/etc/httpd/conf.d';
+    return '/etc/apache2/other'          if -d '/etc/apache2/other'; # why cant macos do things like everybody else?
+    return '/etc/apache2/conf.d'         if -d '/etc/apache2/conf.d';
+    return '/etc/apache2/conf-enabled'   if -d '/etc/apache2/conf-enabled';
+    return '/etc/apache/conf.d'          if -d '/etc/apache/conf.d';
+    return '/etc/httpd/conf.d'           if -d '/etc/httpd/conf.d';
     return;
 }
 
diff --git a/install_util/GBrowseInstall.pm b/install_util/GBrowseInstall.pm
index 1c0bbf5..d0b2293 100644
--- a/install_util/GBrowseInstall.pm
+++ b/install_util/GBrowseInstall.pm
@@ -74,6 +74,7 @@ sub ACTION_demo {
 	    $self->copy_if_modified($_ => $dir);
 	} elsif (m!cgi-bin!) {
 	    $self->copy_if_modified(from => $_,to_dir => "$dir/cgi-bin/gb2",flatten=>1);
+	    chmod 0755,$_ foreach (glob "$dir/cgi-bin/gb2/*");
 	} elsif (m!^sample_data!) {
 	    chdir $self->base_dir();
 	    my ($subdir) = m!^sample_data/([^/]+)/!;
@@ -238,6 +239,7 @@ sub ACTION_config {
 	$_=>$self->config_data($_)
       } keys %$props;
 
+    my $dire_warning = 0;
     my @keys = @OK_PROPS;
     while (@keys) {
 	my $key = shift @keys;
@@ -255,6 +257,27 @@ sub ACTION_config {
 	if ($conf_dir) {
 	    my ($volume,$dir) = File::Spec->splitdir($opts{$key});
 	    my $top_level     = File::Spec->catfile($volume,$dir);
+
+            if ($opts{$key} =~ m!(/usr/local/apache2*)!) {
+                #it looks like there is no apache installed; let the user know
+                my $apachedir = $1;
+                if (!-d $apachedir and !$dire_warning) {
+                    print STDERR <<END
+
+******************************WARNING***********************************
+GBrowse is being configured to install in $apachedir, but that 
+directory doesn't exist, which means either Apache isn\'t installed
+or the installer couldn't find it.  If you continue with this
+installation there is a good chance it won't work if Apache isn't
+installed.
+******************************WARNING***********************************
+
+END
+;
+                   $dire_warning = 1;
+                }
+            }
+
 	    unless (-d $top_level) {
 		next if Module::Build->y_n("The directory $top_level does not exist. Use anyway?",'n');
 		redo;
@@ -353,6 +376,8 @@ sub apache_conf {
 	? "PerlSwitches ".join ' ',map{"-I$_"} split ':',$perl5lib
         : '';
 
+    my ($allow_all,$deny_all) = $self->auth_conf;
+
     return <<END;
 Alias        "/gbrowse2/i/" "$tmp/images/"
 Alias        "/gbrowse2"    "$dir"
@@ -361,8 +386,7 @@ ScriptAlias  "/gb2"      "$cgibin"
 <Directory "$dir">
   AllowOverride Options
   Options -Indexes -MultiViews +FollowSymLinks
-  Order allow,deny
-  Allow from all
+  $allow_all
 </Directory>
 
 <Directory "$dir/tutorial">
@@ -370,13 +394,11 @@ ScriptAlias  "/gb2"      "$cgibin"
 </Directory>
 
 <Directory "$tmp/images/">
-  Order allow,deny
-  Allow from all
+  $allow_all
 </Directory>
 
 <Directory "$databases">
-  Order allow,deny
-  Deny from all
+  $deny_all
 </Directory>
 
 <Directory "$cgibin">
@@ -394,19 +416,22 @@ ScriptAlias  "/gb2"      "$cgibin"
   # these directives prevent idle/busy timeouts and may need to be
   # adjusted up or down
   FcgidMinProcessesPerClass 6
-  FcgidIOTimeout   600
-  FcgidBusyTimeout 600
+  FcgidConnectTimeout  30
+  FcgidIOTimeout      600
+  FcgidBusyTimeout    600
+  # allow larger file uploads up to 128M under FastCGI (default is 128K)
+  FcgidMaxRequestLen 134217728
   $fcgid_inc
 </IfModule>
 
 <IfModule mod_fastcgi.c>
-  Alias /fgb2 "$cgibin"
-  <Location /fgb2>
+  Alias /fast "$cgibin"
+  <Location /fast>
     SetHandler   fastcgi-script
   </Location>
   # Note: you may need to increase -idle-timeout if file uploads are timing out and returning server
   # errors.
-  FastCgiConfig -idle-timeout 600 -maxClassProcesses 20 $fcgi_inc -initial-env GBROWSE_CONF=$conf 
+  FastCgiConfig -startDelay 30 -appConnTimeout 30 -idle-timeout 600 -maxClassProcesses 20 $fcgi_inc -initial-env GBROWSE_CONF=$conf 
 </IfModule>
 
 # Use of mod_perl is no longer supported. Use at your own risk.
@@ -452,6 +477,7 @@ sub ACTION_install {
     # fix some directories so that www user can write into them
     my $tmp = $self->config_data('tmp') || GBrowseGuessDirectories->tmp;
     mkpath($tmp);
+    
     my ($uid,$gid) = (getpwnam($user))[2,3];
 
     # taint check issues
@@ -466,11 +492,13 @@ sub ACTION_install {
 
     my $htdocs_i = File::Spec->catfile($self->install_path->{htdocs},'i');
     my $images   = File::Spec->catfile($tmp,'images');
+    my $gbs_tmp  = File::Spec->catfile($self->install_path->{htdocs},'tmp');
     my $htdocs = $self->install_path->{htdocs};
     chown $uid,-1,$htdocs;
     {
 	local $> = $uid;
 	symlink($images,$htdocs_i);  # so symlinkifowner match works!
+	symlink($tmp,$gbs_tmp);
     }
     chown $>,-1,$self->install_path->{htdocs};
 
@@ -486,6 +514,7 @@ sub ACTION_install {
     }
 
     chmod 0755,File::Spec->catfile($self->install_path->{'etc'},'init.d','gbrowse-slave');
+    chmod 0755,File::Spec->catfile($self->install_path->{'etc'},'init.d','gbrowse-aws-balancer');
     $self->fix_selinux;
 
     my $base = basename($self->install_path->{htdocs});
@@ -498,6 +527,18 @@ sub ACTION_install {
     system $perl, at inc,$metadb_script;
     system 'sudo','chown','-R',"$uid.$gid",$sessions,$userdata;
 
+    # make the gbrowse-aws-balancer file, which might contain secret keys, read-only to root
+    if ($self->config_data('installetc') =~ /^[yY]/) {
+	my $install_path = $self->install_path->{'etc'} || GBrowseGuessDirectories->etc;
+	system 'sudo','chmod','go-rwx',File::Spec->catfile($install_path,'default','gbrowse-aws-balancer');
+    }
+
+    # enable CGI scripts on 2.4 systems
+    if ($self->apache_version =~ /2\.4/) {
+	print STDERR "Enabling CGI scripts on your Apache2 system...\n";
+	system 'sudo','a2enmod','cgi';
+    }
+
     if (Module::Build->y_n(
 	    "It is recommended that you restart Apache. Shall I try this for you?",'y'
 	)) {
@@ -576,7 +617,7 @@ sub check_installed {
     if (-e $installed && (compare($staged,$installed) != 0)) {
 	my ($confirmed,$keep);
 
-	if ($ENV{AUTOMATED_TESTING}) {
+	if ($ENV{AUTOMATED_TESTING} || !(-t STDIN)) {
 	    $confirmed++;
 	    $keep++;
 	}
@@ -624,6 +665,15 @@ sub process_htdocs_files {
 	    $self->check_installed($install_path,$base) if $copied;
 	}
     }
+
+    # hacky thing for getting the cloud index.html right
+    if (eval "require Bio::Graphics::Browser2::Render::Slave::AWS_Balancer;1") {
+	if (Bio::Graphics::Browser2::Render::Slave::AWS_Balancer->running_as_instance) {
+	    warn "Cloud instance detected; renaming index.html";
+	    rename "blib/htdocs/index.html","blib/htdocs/index_default.html";
+	    rename "blib/htdocs/cloud_index.html","blib/htdocs/index.html";
+	}
+    }
 }
 
 sub process_cgibin_files {
@@ -734,23 +784,23 @@ sub substitute_in_place {
     $persistent ||= $databases;
 
     while (<$in>) {
-	s/\$INSTALLSCRIPT/$installscript/g;
-	s/\$ETC/$etc/g;
-	s/\$PERL5LIB/$perl5lib/g;
-	s/\$HTDOCS/$htdocs/g;
-	s/\$CONF/$conf/g;
-	s/\$CGIBIN/$cgibin/g;
-	s/\$CGIURL/$cgiurl/g;
-	s/\$WWWUSER/$wwwuser/g;
-	s/\$DATABASES/$databases/g;
-	s/\$PERSISTENT/$persistent/g;
-	s/\$VERSION/$self->dist_version/eg;
-	s/\$CAN_USER_ACCOUNTS_OPENID/$self->has_openid/eg;
-	s/\$CAN_USER_ACCOUNTS_REG/$self->has_smtp/eg;
-	s/\$CAN_USER_ACCOUNTS/$self->has_mysql_or_sqlite/eg;
-	s/\$USER_ACCOUNT_DB/$self->guess_user_account_db/eg;
-	s/\$SMTP_GATEWAY/$self->guess_smtp_gateway/eg;
-	s/\$TMP/$tmp/g;
+	s/\$INSTALLSCRIPT\b/$installscript/g;
+	s/\$ETC\b/$etc/g;
+	s/\$PERL5LIB\b/$perl5lib/g;
+	s/\$HTDOCS\b/$htdocs/g;
+	s/\$CONF\b/$conf/g;
+	s/\$CGIBIN\b/$cgibin/g;
+	s/\$CGIURL\b/$cgiurl/g;
+	s/\$WWWUSER\b/$wwwuser/g;
+	s/\$DATABASES\b/$databases/g;
+	s/\$PERSISTENT\b/$persistent/g;
+	s/\$VERSION\b/$self->dist_version/eg;
+	s/\$CAN_USER_ACCOUNTS_OPENID\b/$self->has_openid/eg;
+	s/\$CAN_USER_ACCOUNTS_REG\b/$self->has_smtp/eg;
+	s/\$CAN_USER_ACCOUNTS\b/$self->has_mysql_or_sqlite/eg;
+	s/\$USER_ACCOUNT_DB\b/$self->guess_user_account_db/eg;
+	s/\$SMTP_GATEWAY\b/$self->guess_smtp_gateway/eg;
+	s/\$TMP\b/$tmp/g;
 	$out->print($_);
     }
     $in->close;
@@ -809,11 +859,12 @@ sub httpd_conf {
 
     my $user    = $>;
     my ($group) = $) =~ /^(\d+)/;
+    my $lockfile  = $self->apache_version =~ /2\.4/ ? '' : "LockFile             \"$dir/locks/accept.lock\"";
 
     return <<END;
 ServerName           "localhost"
 ServerRoot           "$dir/conf"
-LockFile             "$dir/locks/accept.lock"
+$lockfile
 PidFile              "$dir/logs/apache2.pid"
 ErrorLog             "$dir/logs/error.log"
 LogFormat            "%h %l %u %t \\"%r\\" %>s %b" common
@@ -869,6 +920,22 @@ Include "$dir/conf/apache_gbrowse.conf"
 END
 }
 
+sub auth_conf {
+    my $self = shift;
+    my $new_auth  = $self->apache_version =~ /2\.4/;
+    my $allow_all = $new_auth ? "Require all granted" : "Order allow,deny\n  Allow from all";
+    my $deny_all  = $new_auth ? "Require all denied"  : "Order allow,deny\n  Deny from all";
+    return ($allow_all,$deny_all);
+}
+
+sub apache_version {
+    my $apache =  GBrowseGuessDirectories->apache
+	or die "Could not find apache executable on this system. Can't figure out version number for config file.";
+    my $version   = `$apache -v`;
+    my ($v)  = $version =~ m!Apache/(\S+)!;
+    return $v;
+}
+
 sub gbrowse_demo_conf {
     my $self = shift;
     my ($port,$dir) = @_;
@@ -877,23 +944,43 @@ sub gbrowse_demo_conf {
     my $more = $self->added_to_INC;
     $inc    .= ":$more" if $more;
 
+    my ($allow_all,$deny_all) = $self->auth_conf;
+
+    my $additional_config = '';
+    my $namevirtualhost   = "NameVirtualHost *:$port";
+
+    if ($self->apache_version =~ /2\.4/) {
+	$namevirtualhost   = '';
+	$additional_config = <<END;
+LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so
+LoadModule mpm_prefork_module /usr/lib/apache2/modules/mod_mpm_prefork.so
+<IfModule mpm_prefork_module>
+	StartServers			 5
+	MinSpareServers		  5
+	MaxSpareServers		 10
+	MaxRequestWorkers	  150
+	MaxConnectionsPerChild   0
+</IfModule>
+END
+    }
+
     return <<END;
-NameVirtualHost *:$port
+$namevirtualhost
+$additional_config
 <VirtualHost *:$port>
 	ServerAdmin webmaster\@localhost
 	Alias        "/i/"       "$dir/tmp/images/"
 	ScriptAlias  "/cgi-bin/" "$dir/cgi-bin/"
 	
-	DocumentRoot $dir/htdocs/
+	DocumentRoot "$dir/htdocs/"
 	<Directory />
 		Options FollowSymLinks
 		AllowOverride None
 	</Directory>
-	<Directory $dir/htdocs/>
+	<Directory "$dir/htdocs/">
 		Options Indexes FollowSymLinks MultiViews
 		AllowOverride None
-		Order allow,deny
-		allow from all
+		$allow_all
 	</Directory>
 
 	<Directory "$dir/cgi-bin/">
@@ -904,8 +991,7 @@ NameVirtualHost *:$port
                 SetEnv GBROWSE_ROOT   /
 		AllowOverride None
 		Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
-		Order allow,deny
-		Allow from all
+		$allow_all
 	</Directory>
 </VirtualHost>
 END
@@ -951,7 +1037,7 @@ sub added_to_INC {
 
 sub perl5lib {
     my $self = shift;
-    return $self->added_to_INC or undef;
+    return $self->added_to_INC || undef;
 }
 
 sub scriptdir {
diff --git a/lib/Bio/DB/SeqFeature/Store/LoadHelper.pm b/lib/Bio/DB/SeqFeature/Store/LoadHelper.pm
new file mode 100644
index 0000000..3cfb490
--- /dev/null
+++ b/lib/Bio/DB/SeqFeature/Store/LoadHelper.pm
@@ -0,0 +1,200 @@
+package Bio::DB::SeqFeature::Store::LoadHelper;
+
+# NOTE: This overwrites the version in bioperl-live!!
+
+=head1 NAME
+
+Bio::DB::SeqFeature::Store::LoadHelper -- Internal utility for Bio::DB::SeqFeature::Store
+
+=head1 SYNOPSIS
+
+  # For internal use only.
+
+=head1 DESCRIPTION
+
+For internal use only
+
+=head1 SEE ALSO
+
+L<bioperl>,
+L<Bio::DB::SeqFeature::Store>,
+L<Bio::DB::SeqFeature::Segment>,
+L<Bio::DB::SeqFeature::NormalizedFeature>,
+L<Bio::DB::SeqFeature::GFF2Loader>,
+L<Bio::DB::SeqFeature::Store::DBI::mysql>,
+L<Bio::DB::SeqFeature::Store::berkeleydb>
+
+=head1 AUTHOR
+
+Lincoln Stein E<lt>lstein at cshl.orgE<gt>.
+
+Copyright (c) 2006 Cold Spring Harbor Laboratory.
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use DB_File;
+use File::Path 'rmtree';
+use File::Temp 'tempdir';
+use File::Spec;
+use Fcntl qw(O_CREAT O_RDWR);
+
+our $VERSION = '1.10';
+
+my %DBHandles;
+
+sub new {
+    my $class   = shift;
+    my $tmpdir  = shift;
+
+    my $template = 'SeqFeatureLoadHelper_XXXXXX';
+
+    my @tmpargs = $tmpdir ? ($template,DIR=>$tmpdir) : ($template);
+    my $tmppath = tempdir(@tmpargs,CLEANUP=>1);
+    my $self    = $class->create_dbs($tmppath);
+    $self->{tmppath} = $tmppath;
+    return bless $self,$class;
+}
+
+sub DESTROY {
+    my $self = shift;
+    rmtree $self->{tmppath};
+#    File::Temp::cleanup() unless $self->{keep};
+}
+
+sub create_dbs {
+    my $self = shift;
+    my $tmp  = shift;
+    my %self;
+    # experiment with caching these handles in memory
+    my $hash_options           = DB_File::HASHINFO->new();
+    # Each of these hashes allow only unique keys
+    for my $dbname (qw(IndexIt TopLevel Local2Global)) {
+	unless ($DBHandles{$dbname}) {
+	    my %h;
+	    tie(%h,'DB_File',File::Spec->catfile($tmp,$dbname),
+		O_CREAT|O_RDWR,0666,$hash_options);
+	    $DBHandles{$dbname} = \%h;
+	}
+	$self{$dbname} = $DBHandles{$dbname};
+	%{$self{$dbname}} = ();
+    }
+
+    # The Parent2Child hash allows duplicate keys, so we
+    # create it with the R_DUP flag.
+    my $btree_options           = DB_File::BTREEINFO->new();
+    $btree_options->{flags}     = R_DUP;
+    unless ($DBHandles{'Parent2Child'}) {
+	my %h;
+	tie(%h,'DB_File',File::Spec->catfile($tmp,'Parent2Child'),
+	    O_CREAT|O_RDWR,0666,$btree_options);
+	$DBHandles{'Parent2Child'} = \%h;
+    }
+    $self{Parent2Child}    = $DBHandles{'Parent2Child'};
+    %{$self{Parent2Child}} = ();
+    return \%self;
+}
+
+sub indexit {
+    my $self = shift;
+    my $id   = shift;
+    $self->{IndexIt}{$id} = shift if @_;
+    return $self->{IndexIt}{$id};
+}
+
+sub toplevel {
+    my $self = shift;
+    my $id   = shift;
+    $self->{TopLevel}{$id} = shift if @_;
+    return $self->{TopLevel}{$id};
+}
+
+sub each_toplevel {
+    my $self = shift;
+    my ($id) = each %{$self->{TopLevel}};
+    $id;
+}
+
+sub local2global {
+    my $self = shift;
+    my $id   = shift;
+    $self->{Local2Global}{$id} = shift if @_;
+    return $self->{Local2Global}{$id};
+}
+
+sub add_children {
+    my $self      = shift;
+    my $parent_id = shift;
+    # (@children) = @_;
+    $self->{Parent2Child}{$parent_id} = shift while @_;
+}
+
+sub children {
+    my $self = shift;
+    my $parent_id = shift;
+
+    my @children;
+
+    my $db        = tied(%{$self->{Parent2Child}});
+    my $key       = $parent_id;
+    my $value     = '';
+    for (my $status = $db->seq($key,$value,R_CURSOR);
+	 $status    == 0 && $key eq $parent_id;
+	 $status    = $db->seq($key,$value,R_NEXT)
+	) {
+	push @children,$value;
+    }
+    return wantarray ? @children: \@children;
+}
+
+# this acts like each() and returns each parent id and an array ref of children
+sub each_family {
+    my $self = shift;
+
+    my $db        = tied(%{$self->{Parent2Child}});
+
+    if ($self->{_cursordone}) {
+	undef $self->{_cursordone};
+	undef $self->{_parent};
+	undef $self->{_child};
+	return;
+    }
+
+    # do a slightly tricky cursor search
+    unless (defined $self->{_parent}) {
+	return unless $db->seq($self->{_parent},$self->{_child},R_FIRST) == 0;
+    }
+
+    my $parent   = $self->{_parent};
+    my @children = $self->{_child};
+
+    my $status;
+    while (($status = $db->seq($self->{_parent},$self->{_child},R_NEXT)) == 0
+	   && $self->{_parent} eq $parent
+	) {
+	push @children,$self->{_child};
+    }
+
+    $self->{_cursordone}++ if $status != 0;
+    
+    return ($parent,\@children);
+}
+
+sub local_ids {
+    my $self = shift;
+    my @ids  = keys %{$self->{Local2Global}}
+                   if $self->{Local2Global};
+    return \@ids;
+}
+
+sub loaded_ids {
+    my $self = shift;
+    my @ids  = values %{$self->{Local2Global}}
+                     if $self->{Local2Global};
+    return \@ids;
+}
+
+1;
diff --git a/lib/Bio/Graphics/Browser2.pm b/lib/Bio/Graphics/Browser2.pm
index 91beef7..c9a2665 100644
--- a/lib/Bio/Graphics/Browser2.pm
+++ b/lib/Bio/Graphics/Browser2.pm
@@ -2,7 +2,7 @@ package Bio::Graphics::Browser2;
 # $Id$
 # Globals and utilities for GBrowse and friends
 
-our $VERSION = '2.54';
+our $VERSION = '2.56';
 
 use strict;
 use warnings;
@@ -82,7 +82,7 @@ sub config_path {
 sub htdocs_path {
   my $self    = shift;
   my $option  = shift;
-  $self->resolve_path($self->setting(general => $option),'htdocs') 
+  $self->resolve_path($self->setting(general => $option),'htdocs')
       || "$ENV{DOCUMENT_ROOT}/gbrowse2";
 }
 
@@ -92,7 +92,7 @@ sub url_path {
   $self->resolve_path( scalar($self->setting(general => $option)),'url');
 }
 
-sub config_base {$ENV{GBROWSE_CONF} 
+sub config_base {$ENV{GBROWSE_CONF}
 		    || eval {shift->setting(general=>'config_base')}
 			|| GBrowse::ConfigData->config('conf')
 		              || '/etc/GBrowse2' }
@@ -100,7 +100,7 @@ sub htdocs_base {$ENV{GBROWSE_HTDOCS}
 		 || eval{shift->setting(general=>'htdocs_base')}
                     || GBrowse::ConfigData->config('htdocs')
 		        || '/var/www/gbrowse2'     }
-sub url_base    {eval{shift->setting(general=>'url_base')}   
+sub url_base    {eval{shift->setting(general=>'url_base')}
                      || basename(GBrowse::ConfigData->config('htdocs'))
 		        || '/gbrowse2'             }
 
@@ -112,12 +112,12 @@ sub persistent_base    {
     my $base = $self->setting(general=>'persistent_base');
     return $base || $self->tmp_base;  # for compatibility with pre 2.27 installs
 }
-sub db_base        { 
+sub db_base        {
     my $self = shift;
     my $base = $self->setting(general=>'db_base');
     return $base || File::Spec->catfile(shift->persistent_base,'databases');
 }
-sub userdata_base  { 
+sub userdata_base  {
     my $self = shift;
     my $base = $self->setting(general=>'userdata_base');
     return $base ||  File::Spec->catfile($self->persistent_base,'userdata');
@@ -158,7 +158,7 @@ sub make_path {
     my $path = shift;
     return unless $path =~ /^(.+)$/;
     $path = $1;
-    mkpath($path,0,0777) unless -d $path;    
+    mkpath($path,0,0777) unless -d $path;
 }
 
 sub tmpdir {
@@ -243,6 +243,7 @@ sub language_path  { shift->config_path('language_path')   }
 sub templates_path { shift->config_path('templates_path')  }
 sub moby_path      { shift->config_path('moby_path')       }
 
+sub preload_datasources    { shift->setting(general=>'preload data sources') || 0 } # default not-preload
 sub global_timeout         { shift->setting(general=>'global_timeout')      ||  60   }
 sub remember_settings_time { shift->setting(general=>'expire session')      || '1M'  }
 sub cache_time             { shift->setting(general=>'expire cache')        || '2h'  }
@@ -259,16 +260,16 @@ sub smtp_enabled           { return defined shift->smtp;
 sub user_account_db        { shift->setting(general=>'user_account_db')                                       } # Used by uploads & user databases, they set their own defaults.
 sub user_accounts	   { my $self = shift;
 			     return $self->setting(general=>'user_accounts') ||
-				    $self->setting(general=>'user_accounts')  || 
+				    $self->setting(general=>'user_accounts')  ||
 				    0; }
 sub user_accounts_allow_registration
-                           { 
+                           {
 			       my $val = shift->setting(general=>'user_accounts_registration');
 			       return 1 unless defined $val;
 			       return $val;
 			   }
 sub user_accounts_allow_openid
-                           { 
+                           {
 			       my $val = shift->setting(general=>'user_accounts_openid');
 			       return 1 unless defined $val;
 			       return $val;
@@ -285,7 +286,7 @@ sub upload_db_adaptor {
     my $self = shift;
     my $adaptor = $self->setting(general=>'upload_db_adaptor') || $self->setting(general=>'userdb_adaptor');
     $adaptor or return;
-    warn "The upload_db_adaptor in your Gbrowse.conf file isn't in the DBI::<module> format: remember, it's not a connection string." 
+    warn "The upload_db_adaptor in your Gbrowse.conf file isn't in the DBI::<module> format: remember, it's not a connection string."
 	if $adaptor =~ /^DBI/ && $adaptor !~ /(^DBI::+)/i;
     return $adaptor;
 }
@@ -363,7 +364,7 @@ sub data_source_path {
       my $path = $self->resolve_path($self->setting("=~".$regex_key=>'path'),'config');
       my @matches = ($dsn =~ /$regex_key/);
       for (my $i = 1; $i <= scalar(@matches); $i++) {
-	  $path =~ s/\$$i/$matches[$i-1]/;
+	  $path =~ s/\$$i/$matches[$i-1]/g;
       }
       return $self->resolve_path($path, 'config');
   }
@@ -436,7 +437,7 @@ sub get_source_from_cgi {
     $source    =~ s!\#$!!;  # get rid of trailing # left by IE
     $source    =~ s!^/+!!;  # get rid of leading & trailing / from path_info()
     $source    =~ s!/+$!!;
-    
+
     $source;
 }
 
@@ -453,7 +454,7 @@ sub update_data_source {
     $session->source($new_source);
     $source = $new_source;
   } else {
-    my $fallback_source = $self->valid_source($old_source) 
+    my $fallback_source = $self->valid_source($old_source)
 	? $old_source
 	: $self->default_source;
     $session->source($fallback_source);
diff --git a/lib/Bio/Graphics/Browser2/Action.pm b/lib/Bio/Graphics/Browser2/Action.pm
index ba37047..115ce17 100644
--- a/lib/Bio/Graphics/Browser2/Action.pm
+++ b/lib/Bio/Graphics/Browser2/Action.pm
@@ -10,7 +10,7 @@ use Bio::Graphics::Browser2::TrackDumper;
 use Bio::Graphics::Browser2::Render::HTML;
 use Bio::Graphics::Browser2::SendMail;
 use File::Basename 'basename';
-use File::Path     'make_path';
+use File::Path     'mkpath';
 use JSON;
 use constant DEBUG => 0;
 use Data::Dumper;
@@ -174,7 +174,7 @@ sub ACTION_update_sections {
     my $q       = shift;
 
     my $render = $self->render;
-    my @section_names = $q->param('section_names');
+    my @section_names = $q->multi_param('section_names');
     my $keyword = $q->param('keyword');
     my $offset = $q->param('offset');
 
@@ -282,7 +282,7 @@ sub ACTION_retrieve_multiple {
     $render->init_plugins();
 
     my %track_html;
-    my @track_ids = $q->param('track_ids');
+    my @track_ids = $q->multi_param('track_ids');
 
     foreach my $track_id (@track_ids) {
 	my $track_key = $q->param( 'tk_' . $track_id ) or next;
@@ -303,8 +303,8 @@ sub ACTION_add_tracks {
     my $q    = shift;
 
     my $render = $self->render;
-    my @track_names = $q->param('track_names');
-	
+    my @track_names = $q->multi_param('track_names');
+
     $render->init_database();
     $render->init_plugins();
 
@@ -509,7 +509,7 @@ sub ACTION_mail_snapshot {
 
      my $filename = $snapshots->{$name}{snapshot_id};
          
-     make_path(File::Spec->catfile($dir,$source,$id));
+     mkpath(File::Spec->catfile($dir,$source,$id));
 	
      #Storing the snapshot as a string and saving it to a textfile. Typical directory /var/lib/gbrowse2/userdata/{source}/{uploadid}: 
      my $snapshot = Dumper($snapshots->{$name}{data});
@@ -622,8 +622,8 @@ sub ACTION_show_hide_section {
     my $self = shift;
     my $q    = shift;
 
-    my @show = $q->param('show');
-    my @hide = $q->param('hide');
+    my @show = $q->multi_param('show');
+    my @hide = $q->multi_param('hide');
 
     my $settings = $self->state;
     $settings->{section_visible}{$_} = 0 foreach @hide;
@@ -636,8 +636,8 @@ sub ACTION_open_collapse_track {
     my $self = shift;
     my $q    = shift;
 
-    my @open     = $q->param('open');
-    my @collapse = $q->param('collapse');
+    my @open     = $q->multi_param('open');
+    my @collapse = $q->multi_param('collapse');
 
     my $settings = $self->state;
     $settings->{track_collapsed}{$_} = 1 foreach @collapse;
@@ -653,7 +653,7 @@ sub ACTION_change_track_order {
     warn "change_track_order()" if DEBUG;
 
     my $settings = $self->state;
-    my @labels   = $q->param('label[]') or return;
+    my @labels   = $q->multi_param('label[]') or return;
     foreach (@labels) {
 	s/%5F/_/g;
 	s/:(overview|region|detail)$// if m/^(plugin|file|http|ftp)/;
@@ -730,10 +730,10 @@ sub ACTION_autocomplete_user_search {
 sub ACTION_get_feature_info {
     my $self = shift;
     my $q    = shift;
-    defined(my $etype = CGI::unescape($q->param('event_type'))) or croak;
-    defined(my $track = CGI::unescape($q->param('track')))      or croak;
-    defined(my $dbid  = CGI::unescape($q->param('dbid')))       or croak;
-    defined(my $fid   = CGI::unescape($q->param('feature_id'))) or croak;
+    defined(my $etype = CGI::unescape(scalar $q->param('event_type'))) or croak;
+    defined(my $track = CGI::unescape(scalar $q->param('track')))      or croak;
+    defined(my $dbid  = CGI::unescape(scalar $q->param('dbid')))       or croak;
+    defined(my $fid   = CGI::unescape(scalar $q->param('feature_id'))) or croak;
     $fid                 or  return (204,'text/plain','nothing at all');
     ($dbid =~ /^remote/ && $etype eq 'mouseover')
                         &&  return (204,'text/plain','nothing at all');
diff --git a/lib/Bio/Graphics/Browser2/DataLoader/archive.pm b/lib/Bio/Graphics/Browser2/DataLoader/archive.pm
new file mode 100644
index 0000000..cd449ee
--- /dev/null
+++ b/lib/Bio/Graphics/Browser2/DataLoader/archive.pm
@@ -0,0 +1,318 @@
+package Bio::Graphics::Browser2::DataLoader::archive;
+
+# $Id$
+use strict;
+use base 'Bio::Graphics::Browser2::DataLoader';
+use Bio::DB::BigWigSet;
+use File::Spec;
+
+sub new {
+    my $class = shift;
+    my $self  = $class->SUPER::new(@_);
+    $self->{default_track_name} = 'track000';
+    $self;
+}
+
+sub default_track_name {
+    my $self = shift;
+    return $self->{default_track_name}++;
+}
+
+sub load {
+    my $self = shift;
+    my ($initial_lines,$fh) = @_;
+    
+    $self->flag_busy(1);
+    eval {
+	$self->open_conf;
+	$self->set_status('starting load');
+	
+	mkdir $self->sources_path or die $!;
+	$self->{archive} = File::Spec->catfile($self->sources_path,$self->track_name);
+	my $source_file = IO::File->new($self->{archive},'>');
+
+	warn "sourcefile=$self->{archive}";
+
+	$self->start_load;
+
+	$self->set_status('load data');
+	my $bytes_loaded = 0;
+	foreach (@$initial_lines) {
+	    $source_file->print($_);
+	    $bytes_loaded += length $_;
+	}
+
+	my $buffer;
+	while ((my $bytes = read($fh,$buffer,8192) > 0)) {
+	    $source_file->print($buffer);
+	    $bytes_loaded += length $ buffer;
+	    $self->set_status("loaded $bytes_loaded bytes") if $bytes++ % 10000;
+	}
+	$source_file->close();
+    
+	$self->finish_load;
+	$self->check_metadata;
+	$self->write_conf;
+	$self->close_conf;
+	$self->set_processing_complete;
+    };
+
+    $self->flag_busy(0);
+    die $@ if $@;
+    return $self->tracks;
+}
+
+sub finish_load {
+	my $self = shift;
+	$self->set_status("extracting files from archive");
+	
+	my $archive = $self->{archive};
+	my $bin = $archive =~ /\.tar$/i ? $self->search_for_binary('tar') :
+		$archive =~ /\.zip$/i ? $self->search_for_binary('unzip') :
+		'';
+	die "unrecognized file type!" unless $bin;
+	
+	my $command = $archive =~ /\.tar$/i ? "$bin -tf" : "$bin -l";
+	my $fh;
+	open $fh, "($command $archive && echo 'success') 2>&1 |";
+	my @contents = <$fh>;
+	close $fh;
+	unless ($contents[-1] =~ /success/) {
+		die "ARCHIVE LIST ERROR: @contents";
+	}
+	pop @contents;
+	
+	my @to_extract;
+	while (@contents) {
+		my $item = shift @contents;
+		next if ($item =~ /^(Archive\:| Length|\-{5,})/); # unzip headers
+		$item = (split / {2,}/, $item)[-1]; # split on two or more spaces
+		chomp $item;
+		my (undef, undef, $file) = File::Spec->splitpath($item);
+		if ($file =~ /^meta/i) {
+			push @to_extract, [$item, $file];
+		}
+		elsif ($file =~ /^\./) {
+			next; # skip .files if present
+		}
+		elsif ($file =~ /\.bw$/i) {
+			push @to_extract, [$item, $file];
+		}
+	}
+	die "no recognizable files in archive to extract" unless @to_extract;
+	
+	# we are trying to be safe here and extract just the file contents
+	# without directory paths to avoid ill- or mis-intentioned stray files
+	my $path = $self->sources_path;
+	my $count;
+	$command = $archive =~ /\.tar$/i ? "$bin -x -O -f" : "$bin -p";
+	foreach (@to_extract) {
+		my $target = File::Spec->catfile($path, $_->[1]);
+		# extract file from tar archive and redirect to file in our path
+		open my $in, "$command $archive \"$_->[0]\" |" or 
+			die "unable to open achive for extraction";
+		open my $out, '>', $target or die "unable to open target file";
+		while (<$in>) {print $out $_}
+		close $in;
+		close $out;
+		$count++ if -e $target && -s _ ;
+	}
+	die "not all files could be extracted\n" unless $count == scalar(@to_extract);
+	unlink $archive; # no longer need
+}
+
+sub search_for_binary {
+    my $self   = shift;
+    my $target = shift;
+    for my $p (split ':', $ENV{PATH}) {
+		my $tgt = File::Spec->catfile($p,$target);
+		return  $tgt if -e $tgt && -x _;
+    }
+    return;
+}
+
+sub check_metadata {
+	my $self = shift;
+	$self->set_status('checking metadata');
+	my $path = $self->sources_path;
+	
+	my $count = Bio::DB::BigWigSet->index_dir($path);
+	die "two or more BigWig files are required" unless $count > 1;
+	
+	my $bws = Bio::DB::BigWigSet->new(-dir => $path);
+	my $md = $bws->metadata;
+	my $default = $self->track_name; # default type
+	my %types; # metadata types, not required to be unique
+	my %names; # metadata display_names, required to be unique
+	my $flag; # for updated metadata
+	foreach my $i (keys %$md) {
+		if (exists $md->{$i}{'type'}) {
+			$types{ $md->{$i}{'type'} }++;
+		}
+		elsif (exists $md->{$i}{'method'} && exists $md->{$i}{'source'}) {
+			$types{ $md->{$i}{'method'} . ':' . $md->{$i}{'source'} }++;
+		}
+		elsif (exists $md->{$i}{'method'}) {
+			$types{ $md->{$i}{'method'} }++;
+		}
+		elsif (exists $md->{$i}{'primary_tag'}) {
+			$types{ $md->{$i}{'primary_tag'} }++;
+		}
+		else {
+			$types{$default}++;
+			my (undef, undef, $filename) = File::Spec->splitpath( $md->{$i}{'dbid'} );
+			$bws->set_bigwig_attributes($md->{$i}{'dbid'}, {'type'=>$default});
+			$flag++;
+		}
+		
+		if (exists $md->{$i}{'display_name'}) {
+			 if (exists $names{ $md->{$i}{'display_name'} }) {
+			 	# must make the name unique
+				my (undef, undef, $name) = File::Spec->splitpath( $md->{$i}{'dbid'} );
+			 	$name =~ s/\.bw$//i;
+			 	$bws->set_bigwig_attributes(
+			 		$md->{$i}{'dbid'}, {'display_name'=>$name} );
+			 	$names{$name}++;
+			 	$flag++;
+			 }
+			 else {
+			 	$names{ $md->{$i}{'display_name'} }++;
+			 }
+		}
+		else {
+			my (undef, undef, $name) = File::Spec->splitpath( $md->{$i}{'dbid'} );
+			$name =~ s/\.bw$//i;
+			$bws->set_bigwig_attributes(
+				$md->{$i}{'dbid'}, {'display_name'=>$name} );
+			$names{$name}++;
+			$flag++;
+		}
+	}
+	
+	$self->write_new_metadata($bws) if $flag;
+	$self->typelist(keys %types);
+	$self->namelist(keys %names);
+}
+
+sub typelist {
+	my $self = shift;
+	$self->{typelist} ||= [];
+	@{ $self->{typelist} } = @_ if @_;
+	return @{ $self->{typelist} };
+}
+
+sub namelist {
+	my $self = shift;
+	$self->{namelist} ||= [];
+	@{ $self->{namelist} } = @_ if @_;
+	return @{ $self->{namelist} };
+}
+
+sub write_new_metadata {
+	# Bio::DB::BigWigSet does not have a method to write out updated metadata
+	# so we will do it here
+	my $self = shift;
+	my $bws = shift;
+	my $path = $self->sources_path;
+	foreach my $f (glob($path)) {
+		unlink $f if $f =~ /^meta/i;
+	}
+	my $index = File::Spec->catfile($path, 'metadata.index');
+	open my $fh, ">", $index or return;
+	my $md = $bws->metadata;
+	foreach my $i (sort {$a <=> $b} keys %$md) {
+		my (undef, undef, $file) = File::Spec->splitpath( $md->{$i}{'dbid'} );
+		my $string = "[$file]\n";
+		foreach my $k (keys %{$md->{$i}}) {
+			next if $k eq 'dbid';
+			$string .= "$k = " . $md->{$i}{$k} . "\n";
+		}
+		print $fh "$string\n";
+	}
+	close $fh;
+}
+
+sub write_conf {
+	my $self = shift;
+    my $name   = $self->track_name;
+    my $path   = $self->sources_path;
+    my $loadid = $self->loadid;
+    my $conf   = $self->conf_fh;
+    my $dbid   = $self->new_track_label;
+    my @types  = $self->typelist;
+    my $table;
+    foreach my $n ($self->namelist) {
+    	$table .= "\n   :\"$n\" \"$n\" ;";
+    }
+    
+    print $conf <<END;
+[$dbid:database]
+db_adaptor    = Bio::DB::BigWigSet
+db_args       = -dir '$path'
+                -feature_type summary
+
+#>>>>>>>>>> cut here <<<<<<<<
+    
+[$dbid]
+database = $dbid
+feature  = @types
+subtrack select = Name tag_value display_name
+subtrack table = $table
+glyph    = wiggle_whiskers
+# change glyph to wiggle_xyplot to use semi-transparent overlap
+fgcolor  = black
+height   = 50
+autoscale = chromosome
+key      = $name
+description = 
+
+END
+;
+
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bio::Graphics::Browser2::DataLoader::archive
+
+=head1 DESCRIPTION
+
+A data loader to work with archives of BigWig files to generate a BigWigSet 
+database. Two or more BigWig files may be combined into an archive file and 
+uploaded. The files are extracted, a metadata index generated if required, and 
+a configuration written using subtracks for each BigWig file. This allows a 
+fast, convenient option to bundle multiple data files together and provide a 
+concise, organized interface to multiple data tracks.
+
+Supported archives include TAR (.tar, .tar.gz, .tgz, .tbz2, .tar.bz2) and 
+ZIP (.zip) files. Archives should include only BigWig files (which must have 
+a .bw extension) and optionally a metadata text file. Extraneous files and 
+directory paths are ignored. 
+
+Subtrack tables are set up using the display_name tag value.
+
+See the documentation for Bio::DB::BigWigSet for more information.
+
+=head1 SETUP
+
+The Bio-BigFile (Bio::DB::BigWigSet) Perl module must be installed.
+
+Archives are processed through external tar and unzip utilities. These 
+are located by searching the default environment PATH.
+
+=head1 AUTHOR
+
+ Timothy J. Parnell, PhD
+ Dept of Oncological Sciences
+ Huntsman Cancer Institute
+ University of Utah
+ Salt Lake City, UT, 84112
+
+This package is free software; you can redistribute it and/or modify
+it under the terms of the GPL (either version 1, or at your option,
+any later version) or the Artistic License 2.0.  
+
diff --git a/lib/Bio/Graphics/Browser2/DataLoader/bam.pm b/lib/Bio/Graphics/Browser2/DataLoader/bam.pm
index dbd7285..dc4fb98 100644
--- a/lib/Bio/Graphics/Browser2/DataLoader/bam.pm
+++ b/lib/Bio/Graphics/Browser2/DataLoader/bam.pm
@@ -156,16 +156,21 @@ sub finish_load {
     my $dest    = File::Spec->catfile($self->data_path,$self->track_name);
     $dest      =~ s/\.[bs]am$//i; # sorting will add the .bam extension
 
+    # check whether we need to sort or not
+    if ($self->is_sorted($source)) {
+        # bam is already sorted by coordinate, destination will become source
+        $dest = $source;
+    }
+    else {
     $self->set_status('sorting BAM file');
     Bio::DB::Bam->sort_core(0,$source,$dest,250*1e6);
+        $dest .= '.bam';
+    }
 
     $self->set_status('indexing BAM file');
-
-    $dest     .= '.bam';
     Bio::DB::Bam->index_build($dest);
 
     my $bigwig_exists = 0;
-
     if ($self->has_bigwig) {
 	$self->set_status('creating BigWig coverage file');
 	$bigwig_exists = $self->create_big_wig();
@@ -193,4 +198,11 @@ sub create_big_wig {
     1;
 }
 
+sub is_sorted {
+	my $self = shift;
+	my $sam = Bio::DB::Sam->new(-bam => shift, -autoindex => 0);
+	my $header = $sam->bam->header->text;
+	return (split /\n/, $header)[0] =~ /SO:coordinate/i;
+}
+
 1;
diff --git a/lib/Bio/Graphics/Browser2/DataLoader/bigbed.pm b/lib/Bio/Graphics/Browser2/DataLoader/bigbed.pm
new file mode 100644
index 0000000..27e3086
--- /dev/null
+++ b/lib/Bio/Graphics/Browser2/DataLoader/bigbed.pm
@@ -0,0 +1,119 @@
+package Bio::Graphics::Browser2::DataLoader::bigbed;
+
+# $Id$
+use strict;
+use base 'Bio::Graphics::Browser2::DataLoader';
+use Bio::DB::BigBed;
+use File::Basename 'basename','dirname';
+my @COLORS = qw(blue red orange brown mauve peach 
+                green cyan yellow coral);
+
+sub new {
+    my $class = shift;
+    my $self  = $class->SUPER::new(@_);
+    $self->{default_track_name} = 'track000';
+    $self;
+}
+
+sub default_track_name {
+    my $self = shift;
+    return $self->{default_track_name}++;
+}
+
+sub load {
+    my $self                = shift;
+    my ($initial_lines,$fh) = @_;
+
+    $self->flag_busy(1);
+    eval {
+	$self->open_conf;
+	$self->set_status('starting load');
+	
+	mkdir $self->sources_path or die $!;
+	$self->{bigbed} = File::Spec->catfile($self->sources_path,$self->track_name);
+	my $source_file = IO::File->new($self->{bigbed},'>');
+
+	warn "sourcefile=$self->{bigbed}";
+
+	$self->start_load;
+
+	$self->set_status('load data');
+	my $bytes_loaded = 0;
+	foreach (@$initial_lines) {
+	    $source_file->print($_);
+	    $bytes_loaded += length $_;
+	}
+
+	my $buffer;
+	while ((my $bytes = read($fh,$buffer,8192) > 0)) {
+	    $source_file->print($buffer);
+	    $bytes_loaded += length $ buffer;
+	    $self->set_status("loaded $bytes_loaded bytes") if $bytes++ % 10000;
+	}
+	$source_file->close();
+
+	$self->finish_load;
+	$self->close_conf;
+	$self->set_processing_complete;
+    };
+
+    $self->flag_busy(0);
+    die $@ if $@;
+    return $self->tracks;
+}
+
+sub finish_load {
+    my $self = shift;
+
+    my $loadid     = $self->loadid;
+
+    $self->set_status('creating configuration');
+    my $conf       = $self->conf_fh;
+    my $dbid       = $self->new_track_label;
+    my $bigbed     = $self->{bigbed} or die "no bigbed file defined";
+    print $conf <<END;
+[$dbid:database]
+db_adaptor    = Bio::DB::BigBed
+db_args       = -bigbed '$bigbed'
+
+END
+    ;
+    print $conf "#>>>>>>>>>> cut here <<<<<<<<\n";
+    my $color = $COLORS[rand @COLORS];
+    my $name = $self->track_name;
+    
+    print $conf <<END
+[$dbid]
+database = $dbid
+feature  = region
+glyph    = segments
+label density = 50
+feature_limit = 500
+bump     = fast
+stranded = 1
+height   = 4
+bgcolor  = $color
+fgcolor  = $color
+key      = $name segments
+description = 
+
+[$dbid\_coverage]
+database = $dbid
+feature  = summary
+glyph    = wiggle_whiskers
+fgcolor  = black
+height   = 50
+autoscale = chromosome
+key      = $name coverage
+description = 
+
+END
+;
+# We are defining two separate tracks rather than using semantic zoom 
+# because of the flexible nature of the bigBed format. It can be used 
+# as a Bam substitute where coverage is the best glyph, or it can be used 
+# for sparse intervals of interest where segments is the best glyph. 
+# Onus is on the user to select the most appropriate one.
+}
+
+1;
diff --git a/lib/Bio/Graphics/Browser2/DataLoader/useq.pm b/lib/Bio/Graphics/Browser2/DataLoader/useq.pm
new file mode 100644
index 0000000..5f2f0b5
--- /dev/null
+++ b/lib/Bio/Graphics/Browser2/DataLoader/useq.pm
@@ -0,0 +1,361 @@
+package Bio::Graphics::Browser2::DataLoader::useq;
+
+# $Id$
+use strict;
+use base 'Bio::Graphics::Browser2::DataLoader';
+use Bio::DB::BigBed;
+use Bio::DB::BigWig;
+use File::Spec;
+
+sub new {
+    my $class = shift;
+    my $self  = $class->SUPER::new(@_);
+    $self->{default_track_name} = 'track000';
+    $self->find_paths;
+    $self;
+}
+
+sub default_track_name {
+    my $self = shift;
+    return $self->{default_track_name}++;
+}
+
+sub find_paths {
+	my $self = shift;
+	
+	# looking for a non-executable jar file that may not be in env path
+	# so hard code likely paths, taking into account known Virtual Machines
+	my @paths = qw(/usr /usr/local /opt /opt/gbrowse /data /data/opt /Applications);
+    push @paths, split ':', $ENV{PATH};
+    
+	my ($USeq2UCSCBig, $bigPath, $java);
+	foreach my $p (@paths) {
+		unless ($USeq2UCSCBig) {
+			my $path = File::Spec->catdir($p, 'USeq*');
+			foreach my $candidate (reverse glob($path)) {
+				# we reverse the glob results to ensure we find the latest 
+				# version if multiple installed, eg USeq_8.4.4 before USeq_8.0.9
+				my $app = File::Spec->catdir($candidate, 'Apps', 'USeq2UCSCBig');
+				if (-e $app) {
+					$USeq2UCSCBig = $app;
+				}
+			}
+		}
+		
+		unless ($bigPath) {
+			# we need the bin path to both converter utilities
+			my $w2bw = File::Spec->catdir($p, 'wigToBigWig');
+			my $b2bb = File::Spec->catdir($p, 'bedToBigBed');
+			if ( (-e $w2bw && -x _ ) and
+				 (-e $b2bb && -x _ ) ) {
+				$bigPath = $p;
+			}
+		}
+		
+		unless ($java) {
+			my $path = File::Spec->catdir($p, 'java');
+			$java = $path if (-e $path && -x _ );
+		}
+		
+		last if $USeq2UCSCBig && $bigPath && $java;
+	}
+	
+	die "Please install the USeq package from http://useq.sourceforge.net" 
+		unless $USeq2UCSCBig;
+	die "Please install wigToBigWig and bedToBigBed in your path" 
+		unless $bigPath;
+	die "Please install Java 1.6+" unless $java;
+	
+	$self->{USeq2UCSCBig} = $USeq2UCSCBig;
+	$self->{bigPath}      = $bigPath;
+	$self->{java}         = $java;
+	return 1;
+}
+
+
+sub load {
+    my $self                = shift;
+    my ($initial_lines,$fh) = @_;
+    
+    $self->flag_busy(1);
+    eval {
+	$self->open_conf;
+	$self->set_status('starting load');
+	
+	mkdir $self->sources_path or die $!;
+	$self->{useq} = File::Spec->catfile($self->sources_path,$self->track_name);
+	my $source_file = IO::File->new($self->{useq},'>');
+
+	warn "sourcefile=$self->{useq}";
+
+	$self->start_load;
+
+	$self->set_status('load data');
+	my $bytes_loaded = 0;
+	foreach (@$initial_lines) {
+	    $source_file->print($_);
+	    $bytes_loaded += length $_;
+	}
+
+	my $buffer;
+	while ((my $bytes = read($fh,$buffer,8192) > 0)) {
+	    $source_file->print($buffer);
+	    $bytes_loaded += length $ buffer;
+	    $self->set_status("loaded $bytes_loaded bytes") if $bytes++ % 10000;
+	}
+	$source_file->close();
+    
+	$self->finish_load;
+	$self->close_conf;
+	$self->set_processing_complete;
+    };
+
+    $self->flag_busy(0);
+    die $@ if $@;
+    return $self->tracks;
+}
+
+sub finish_load {
+    my $self = shift;
+	
+    $self->convert_useq;
+    
+	my @bw;
+	my @bb;
+	my $path = $self->sources_path . "/*";
+	foreach my $f (glob($path)) {
+		push @bw, $f if $f =~ /\.bw$/;
+		push @bb, $f if $f =~ /\.bb$/;
+	}
+	
+	if (scalar @bb == 1) {
+		$self->finish_bigbed_load(@bb);
+	}
+	elsif (scalar @bw == 1) {
+		$self->finish_bigwig_load(@bw);
+	}
+	elsif (scalar @bw == 2) {
+		$self->finish_stranded_bigwig_load(@bw);
+	}
+}
+
+sub convert_useq {
+	my $self = shift;
+	
+    $self->set_status('Converting with USeq2UCSCBig');
+	my $java    = $self->{java};
+	my $app     = $self->{USeq2UCSCBig};
+	my $bigPath = $self->{bigPath};
+	my $useq    = $self->{useq};
+	local $SIG{CHLD} = 'DEFAULT';
+	my $fh;
+	open $fh, "($java -jar '$app' -d '$bigPath' -u '$useq' && echo 'success') 2>&1 |";
+	my @lines = <$fh>;
+	close $fh;
+	unless ($lines[-1] =~ /success/) {
+	    die "USEQ CONVERSION ERROR: @lines";
+	}
+	unlink $useq; # no longer need the original file
+	unlink "$useq.chromLengths" if -e "$useq.chromLengths";
+	unlink "$useq.wig" if -e "$useq.wig";
+}
+
+sub finish_bigbed_load {
+    my $self = shift;
+    my $bigbed     = shift;
+	my @COLORS = qw(blue red orange brown mauve peach 
+					green cyan yellow coral);
+    
+    my $loadid     = $self->loadid;
+    $self->set_status('writing configuration for bigBed');
+    my $conf       = $self->conf_fh;
+    my $dbid       = $self->new_track_label;
+    print $conf <<END;
+[$dbid:database]
+db_adaptor    = Bio::DB::BigBed
+db_args       = -bigbed '$bigbed'
+
+END
+    ;
+    print $conf "#>>>>>>>>>> cut here <<<<<<<<\n";
+    my $color = $COLORS[rand @COLORS];
+    my $name = $self->track_name;
+    
+    print $conf <<END
+[$dbid]
+database = $dbid
+feature  = region
+glyph    = segments
+label density = 50
+feature_limit = 500
+bump     = fast
+stranded = 1
+height   = 4
+bgcolor  = $color
+fgcolor  = $color
+key      = $name segments
+description = 
+
+[$dbid\_coverage]
+database = $dbid
+feature  = summary
+glyph    = wiggle_whiskers
+fgcolor  = black
+height   = 50
+autoscale = chromosome
+key      = $name coverage
+description = 
+
+END
+;
+# We are defining two separate tracks rather than using semantic zoom 
+# because of the flexible nature of the bigBed format. It can be used 
+# as a Bam substitute where coverage is the best glyph, or it can be used 
+# for sparse intervals of interest where segments is the best glyph. 
+# Onus is on the user to select the most appropriate one.
+}
+
+sub finish_bigwig_load {
+    my $self = shift;
+    my $bigwig     = shift;
+    
+    my $loadid     = $self->loadid;
+    $self->set_status('writing configuration for bigWig');
+    my $conf       = $self->conf_fh;
+    my $dbid       = $self->new_track_label;
+    print $conf <<END;
+[$dbid:database]
+db_adaptor    = Bio::DB::BigWig
+db_args       = -bigwig '$bigwig'
+
+END
+    ;
+    print $conf "#>>>>>>>>>> cut here <<<<<<<<\n";
+    my $name = $self->track_name;
+    
+    print $conf <<END
+[$dbid]
+database = $dbid
+feature  = summary
+glyph    = wiggle_whiskers
+fgcolor  = black
+height   = 50
+autoscale = chromosome
+key      = $name
+description = 
+
+END
+;
+
+}
+
+sub finish_stranded_bigwig_load {
+    my $self = shift;
+    
+    $self->set_status('preparing db for BigWigSet');
+    
+    my ($minus, $plus);
+    foreach (@_) {
+    	my (undef, undef, $file) = File::Spec->splitpath($_);
+    	$file =~ s/\.bw$//;
+    	$plus  = $file if $file =~ /Plus$/;
+    	$minus = $file if $file =~ /Minus$/;
+    }
+	
+    my $name = $self->track_name;
+	my $path  = $self->sources_path;
+	my $index = File::Spec->catdir($path, 'metadata.txt');
+    open my $fh, ">", $index or return;
+    
+    print $fh <<END
+[$plus\.bw]
+primary_tag  = $name
+display_name = $plus
+strand       = plus 
+
+[$minus\.bw]
+primary_tag  = $name
+display_name = $minus
+strand       = minus
+END
+;
+    close $fh;
+    
+    my $loadid     = $self->loadid;
+    my $conf       = $self->conf_fh;
+    my $dbid       = $self->new_track_label;
+    print $conf <<END;
+[$dbid:database]
+db_adaptor    = Bio::DB::BigWigSet
+db_args       = -dir '$path'
+                -feature_type summary
+
+#>>>>>>>>>> cut here <<<<<<<<
+    
+[$dbid]
+database = $dbid
+feature  = $name
+subtrack select = Strand tag_value strand
+subtrack table  = :Plus plus;
+				  :Minus minus;
+glyph    = wiggle_xyplot
+height   = 50
+bgcolor         = blue
+fgcolor         = black
+autoscale = chromosome
+key      = $name
+description = 
+
+END
+;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Bio::Graphics::Browser2::DataLoader::useq
+
+=head1 DESCRIPTION
+
+A data loader for the USeq archive, recognized by the file extension ".useq". 
+See L<http://useq.sourceforge.net/useqArchiveFormat.html>
+for information regarding the file format. Briefly, this format can store either 
+genomic intervals with or without text and/or scores, or quantitative scores 
+along a chromosome (point data). 
+
+There is currently no native BioPerl adaptor for the USeq archive. Upon upload, the 
+file is converted to either a UCSC BigBed or BigWig format, depending upon the 
+file contents. Stranded point data may be converted into two BigWig files, each for 
+the Plus and Minus strand. Configuration files are generated as appropriate for 
+the converted files. 
+
+=head1 SETUP
+
+To process the USeq archive, the USeq package (L<http://useq.sourceforge.net> must 
+be installed in a globally accessible path. This location is searched upon 
+initiation. Common paths to search include "/usr", "/usr/local", "/opt", 
+"/opt/gbrowse", "/data", "/data/opt", and "/Applications", in that order. 
+
+The USeq App "USeq2UCSCBig" (a jar file) is used to convert the USeq archive. This 
+app requires three binary executables: "java" and the two UCSC utilities "bedToBigBed" 
+and "wigToBigWig". These are searched for in the environment $PATH variable. 
+The UCSC utilities are available at L<http://hgdownload.cse.ucsc.edu/admin/exe/>. 
+The USeq Apps requires Java 1.6+. 
+
+Failure to find the paths for all three will result in failure to process the .useq 
+file. 
+
+=head1 AUTHOR
+
+ Timothy J. Parnell, PhD
+ Dept of Oncological Sciences
+ Huntsman Cancer Institute
+ University of Utah
+ Salt Lake City, UT, 84112
+
+This package is free software; you can redistribute it and/or modify
+it under the terms of the GPL (either version 1, or at your option,
+any later version) or the Artistic License 2.0.  
+
diff --git a/lib/Bio/Graphics/Browser2/DataSource.pm b/lib/Bio/Graphics/Browser2/DataSource.pm
index 01f2eb4..148e036 100644
--- a/lib/Bio/Graphics/Browser2/DataSource.pm
+++ b/lib/Bio/Graphics/Browser2/DataSource.pm
@@ -619,7 +619,7 @@ sub semantic_labels {
   if (my @lowres = map {[split ':']}
       grep {/^$label:(\d+)/ && $1 <= $length}
       $self->configured_types) {
-	return $label,map {join ':',@$_} sort {$a->[1] <=> $b->[1]} @lowres;
+	return $label,map {join ':',@$_} sort {$a->[-1] <=> $b->[-1]} @lowres;
   }
   return $label
 }
@@ -636,7 +636,7 @@ sub semantic_label {
       grep {/^$label:(\d+)/ && $1 <= $length}
       $self->configured_types)
     {
-      ($label) = map {join ':',@$_} sort {$b->[1] <=> $a->[1]} @lowres;
+      ($label) = map {join ':',@$_} sort {$b->[-1] <=> $a->[-1]} @lowres;
     }
   $label
 }
@@ -753,7 +753,7 @@ sub invert_types {
     my $config = shift;
     return unless $config;
 
-    my $keys         = md5_hex(keys %$config);
+    my $keys         = md5_hex(sort keys %$config);
 
     # check in-memory cache
     if (exists $self->{_inverted}{$keys}) {
@@ -952,7 +952,7 @@ sub db_settings {
 
   # Do environment substitutions in the args. Assume that the environment is safe.
   foreach (@argv) {
-      s/\$ENV{(\w+)}/$ENV{$1}||''/ge;
+      s/\$ENV\{(\w+)\}/$ENV{$1}||''/ge;
       s/\$HTDOCS/Bio::Graphics::Browser2->htdocs_base/ge;
       s/\$DB/Bio::Graphics::Browser2->db_base/ge;
       s/\$CONF/Bio::Graphics::Browser2->config_base/ge;
@@ -1033,7 +1033,6 @@ sub default_dbid {
     return $self->db2id($self->open_database);
 }
 
-
 sub search_options {
     my $self = shift;
     my $dbid = shift;
diff --git a/lib/Bio/Graphics/Browser2/Region.pm b/lib/Bio/Graphics/Browser2/Region.pm
index 4e18cf1..0dd6a25 100644
--- a/lib/Bio/Graphics/Browser2/Region.pm
+++ b/lib/Bio/Graphics/Browser2/Region.pm
@@ -201,6 +201,7 @@ sub search_db {
   my ($features);
   if (my $name = $args->{-search_term}) {
       $name =~ tr/a-zA-Z0-9|.'"_*?: ;+-\/\#\[\]//cd;  # remove rude/naughty characters
+      $name =~ s/^\s+|\s+$//g; # trim leading & trailing whitespace
       my ($ref,$start,$stop,$class,$id) = $self->parse_feature_name($name);
       $features =  $self->lookup_features($ref,$start,$stop,$class,$name,$id);
   }
diff --git a/lib/Bio/Graphics/Browser2/RegionSearch.pm b/lib/Bio/Graphics/Browser2/RegionSearch.pm
index 3347294..d51b868 100644
--- a/lib/Bio/Graphics/Browser2/RegionSearch.pm
+++ b/lib/Bio/Graphics/Browser2/RegionSearch.pm
@@ -315,6 +315,7 @@ sub search_features {
 		  lc $_->seq_id eq $state->{name}) # this hack gives special privileges to matches to seq_ids
 		 ? 'region' 
 		 : $_->primary_tag),
+		 $_->display_name,
 		 $_->seq_id,
 		 $_->start,
 		 $_->end,
diff --git a/lib/Bio/Graphics/Browser2/Render.pm b/lib/Bio/Graphics/Browser2/Render.pm
index fa12d27..e6b86c6 100644
--- a/lib/Bio/Graphics/Browser2/Render.pm
+++ b/lib/Bio/Graphics/Browser2/Render.pm
@@ -66,7 +66,7 @@ sub new {
     my $globals = shift;
     $requested_id = param('id')        || CGI::cookie('gbrowse_sess');
     $authority    = param('authority') || CGI::cookie('authority');
-    my $shared_ok = Bio::Graphics::Browser2::Action->shared_lock_ok(param('action'));
+    my $shared_ok = Bio::Graphics::Browser2::Action->shared_lock_ok(scalar param('action'));
     $session      = $globals->authorized_session($requested_id, 
 						 $authority,
 						 $shared_ok);
@@ -198,7 +198,8 @@ sub plugins {
 sub plugin_name {
     my $self = shift;
     my $label = shift;
-    my ($id) = $label =~ /^plugin:(\w+)/;
+    #my ($id) = $label =~ /^plugin:(\w+)/;
+    my (undef,$id) = split(/:/,$label);
     return $self->plugins->plugin($id)->name;
 }
 
@@ -254,6 +255,8 @@ sub run {
       $source->set_username(undef);
   }
 
+  ##warn "username = ",$session->username;
+
   if ($source->must_authenticate) {
       if ($session->private && 
 	  $self->user_authorized_for_source($session->username))
@@ -770,6 +773,7 @@ sub state_cookie {
       -name    => $CGI::Session::NAME,
       -value   => $id,
       -path    => $path,
+      -httponly=> 1,
       -expires => '+'.$globals->time2sec($globals->remember_settings_time).'s',
       );
   return $cookie;
@@ -784,6 +788,7 @@ sub auth_cookie {
     my $remember = $self->session->remember_auth;
     my @args = (-name => 'authority',
 		-value=> $auth,
+		-httponly=>1,
 		-path => $path);
     if ($remember) {
 	push @args,(-expires => '+'.$globals->time2sec($globals->remember_settings_time).'s');
@@ -922,6 +927,7 @@ sub segment_info_object {
         details_mult         => $self->details_mult(),
         hilite_fill          => $self->data_source->global_setting('hilite fill')    || 'red',  # Not sure if there's a
         hilite_outline       => $self->data_source->global_setting('hilite outline') || 'gray', # better place for this
+	hilite_height        => $self->data_source->global_setting('hilite height')  || '400px',
         flip                 => $state->{flip},
         initial_view_start   => $state->{view_start},
         initial_view_stop    => $state->{view_stop},
@@ -1135,6 +1141,13 @@ sub region {
     else { # a feature search
 	my $search   = $self->get_search_object();
 	my $features = $search->search_features();
+	my $max_feats = $self->data_source->global_setting('max keyword results');
+	if ($features && @$features > $max_feats) {
+	    $max_feats--;
+	    @$features = $max_feats ?                # How many search results?
+		         @{$features}[0..$max_feats] # max is > 1 
+			 : ($features->[0]);         # max is 1
+	}
 	if ($@) {
 	    (my $msg = $@) =~ s/\sat.+line \d+//;
 	    $self->error_message($msg);
@@ -1223,7 +1236,7 @@ sub get_search_object {
 	  state  => $self->state,
 	});
     $search->init_databases(
-	param('dbid') ? [param('dbid')]
+	param('dbid') ? [multi_param('dbid')]
 	:()
 	);
     return $self->{searchobj} = $search;
@@ -1538,6 +1551,11 @@ sub handle_plugins {
             && return 1;
     }
 
+    if ($plugin_type eq 'annotator' && $plugin_action eq $self->translate('Go')) {
+	warn "HERE I AM";
+	return 1;
+    }
+
     return;
 }
 
@@ -1660,7 +1678,7 @@ sub write_auto {
         my $position
             = $f->sub_SeqFeature
             ? join( ',',
-            map { $_->start . '..' . $_->end } $f->sub_SeqFeature )
+            map { $_->strand >= 0 ? $_->start . '..' . $_->end : $_->end . '..' . $_->start } $f->sub_SeqFeature )
             : $f->start . '..' . $f->end;
         $name .= "($seenit{$name})" if $seenit{$name}++;
         $feature_file .= "\nreference=$reference\n";
@@ -2031,7 +2049,8 @@ sub auto_open {
 	    warn "auto_open(): add_track_to_state($desired_label)" if DEBUG;
 	    $self->add_track_to_state($desired_label);
 	    $state->{h_feat} = {};
-	    $state->{h_feat}{ lc $feature->display_name } = 'yellow'
+	    my $h_color = $self->data_source->global_setting('hilite color') || 'yellow';
+	    $state->{h_feat}{ lc $feature->display_name } = $h_color
 		unless param('h_feat') && param('h_feat') eq '_clear_';
 	}
     }
@@ -2339,7 +2358,7 @@ sub update_options {
                grid flip width region_size show_tooltips cache
                );
 
-  if (my @features = shellwords(param('h_feat'))) {
+  if (my @features = shellwords(multi_param('h_feat'))) {
       $state->{h_feat} = {};
       for my $hilight (@features) {
 	  last if $hilight eq '_clear_';
@@ -2348,7 +2367,7 @@ sub update_options {
       }
   }
 
-  if (my @regions = shellwords(param('h_region'))) {
+  if (my @regions = shellwords(multi_param('h_region'))) {
       $state->{h_region} = [];
       foreach (@regions) {
 	  last if $_ eq '_clear_';
@@ -2358,7 +2377,7 @@ sub update_options {
   }
 
   # Process the magic "q" parameter, which overrides everything else.
-  if (my @q = param('q')) {
+  if (my @q = multi_param('q')) {
     delete $state->{$_} foreach qw(name ref h_feat h_region);
     $state->{q} = [map {split /[+-]/} @q];
   }
@@ -2375,24 +2394,24 @@ sub update_tracks {
   my $self  = shift;
   my $state = shift;
 
-  if (my @add = param('add')) {
-      my @style = param('style');
+  if (my @add = multi_param('add')) {
+      my @style = multi_param('style');
       $self->handle_quickie(\@add,\@style);
   }
 
   # selected tracks can be set by the 'l', 'label' or 't' parameter
   # the preferred parameter is 'l', because it implements correct
   # semantics for the label separator
-  if (my @l = param('l')) {
+  if (my @l = multi_param('l')) {
       $self->set_tracks($self->split_labels_correctly(@l));
   }
-  elsif (@l = param('label')) {
+  elsif (@l = multi_param('label')) {
       $self->set_tracks($self->split_labels(@l));
   } #... the 't' parameter
-  elsif (my @t = param('t')) {
+  elsif (my @t = multi_param('t')) {
       $self->set_tracks($self->split_labels(@t));
   } #... the 'ds' (data source) or the 'ts' (track source) parameter
-  elsif ((my @ds = shellwords param('ds')) || (my @ts = shellwords param('ts'))) {
+  elsif ((my @ds = shellwords multi_param('ds')) || (my @ts = shellwords multi_param('ts'))) {
       my @main_l = @ds ? $self->data_source->data_source_to_label(@ds) : $self->data_source->track_source_to_label(@ts);
       if (!@ds && @ts) {
        my %ds = ();
@@ -2422,11 +2441,11 @@ sub update_tracks {
       $self->set_tracks(@main_l);
   }
   
-  if (my @selected = $self->split_labels_correctly(param('enable'))) {
+  if (my @selected = $self->split_labels_correctly(multi_param('enable'))) {
       $self->add_track_to_state($_) foreach @selected;
   }
   
-  if (my @selected = $self->split_labels_correctly(param('disable'))) {
+  if (my @selected = $self->split_labels_correctly(multi_param('disable'))) {
       $self->remove_track_from_state($_) foreach @selected;
   }
   
@@ -3734,7 +3753,7 @@ sub make_hilite_callback {
    
     # if we get here, we select the search term for highlighting
     my %names = map 
-                 {lc $_=> 1}
+                 {lc $_=> 1} grep { defined $_ }
                   $feature->display_name,
                   eval{$feature->get_tag_values('Alias')};
     return unless %names;
diff --git a/lib/Bio/Graphics/Browser2/Render/HTML.pm b/lib/Bio/Graphics/Browser2/Render/HTML.pm
index 02ff64b..5fefc05 100644
--- a/lib/Bio/Graphics/Browser2/Render/HTML.pm
+++ b/lib/Bio/Graphics/Browser2/Render/HTML.pm
@@ -1821,6 +1821,7 @@ sub plugin_menu {
     ' ',
     button(
       -name     => 'plugin_action',
+      -id       => 'configure_plugin_button',
       -value    => $self->translate('Configure'),
       -onClick => 'Controller.configure_plugin("plugin_configure_div");'
     ),
@@ -2222,11 +2223,11 @@ sub track_citation {
     	    'Click here to display in new window...');    
        $cit_link = p($cit_link);
     }
-    $cit_html = p($cit_link||br,$cit_txt);
-    my $title    = div({-style => 'background:gainsboro;padding:5px;font-weight:bold'},$key);
-    my $download = a({-href=>"?l=$label;f=save+datafile"},$self->tr('DOWNLOAD_TRACK_DATA_ALL'));
+    $cit_html = div($cit_link||br,$cit_txt);
     my $id       = $self->tr('TRACK_ID',$label);
-    return  p(div({-style=>'text-align:center;font-size:small'},$title,$id,"[$download]"),$cit_html);
+    my $download = a({-href=>"?l=$label;f=save+datafile"},$self->tr('DOWNLOAD_TRACK_DATA_ALL'));
+    my $title    = div({-style => 'background:gainsboro;padding:5px;font-weight:bold'},$key,br(),$id,br(),$download);
+    return  join '',div({-style=>'text-align:left;font-size:small'},$title,$cit_html);
 }
 
 sub download_track_menu {
@@ -2550,6 +2551,7 @@ sub can_generate_pdf {
     return $CAN_PDF = $source->global_setting('generate pdf') 
 	if defined $source->global_setting('generate pdf');
 
+    return $CAN_PDF=1 if `which svg2pdf`;
     return $CAN_PDF=0 unless `which inkscape`;
     # see whether we have the needed .inkscape and .gnome2 directories
     my $home = (getpwuid($<))[7];
@@ -2603,7 +2605,7 @@ sub display_citation {
         $key .= " (at >$lim bp)";
    }
 
-   my $citation = div({-class => 'searchbody', -style => 'padding:10px;width:70%'}, h4($key), $cit_txt);
+   my $citation = div({-class => 'searchbody', -style => 'padding:10px;width:70%'}, h4(escape($key)), $cit_txt);
      
  
    $return_html
diff --git a/lib/Bio/Graphics/Browser2/Render/Login.pm b/lib/Bio/Graphics/Browser2/Render/Login.pm
index cf82c32..a686d3f 100644
--- a/lib/Bio/Graphics/Browser2/Render/Login.pm
+++ b/lib/Bio/Graphics/Browser2/Render/Login.pm
@@ -276,7 +276,7 @@ sub run_asynchronous_request {
     my $userdb = $self->renderer->userdb;
     $userdb or return (500,'text/plain',"Couldn't get userdb object");
 
-    my %actions  = map {$_=>1} $q->param('login_action');
+    my %actions  = map {$_=>1} $q->multi_param('login_action');
     my %callback;
 
     my $user       = $q->param('user');
@@ -320,6 +320,7 @@ sub run_asynchronous_request {
 	 :$actions{get_gecos}         ? $userdb->do_get_gecos($user)
 	 :$actions{get_email}         ? $userdb->do_get_email($user)
 	 :(500,'text/plain','programmer error');
+    warn "content = $content";
     return ($status,$content_type,$content);
 }
 
diff --git a/lib/Bio/Graphics/Browser2/Render/Slave.pm b/lib/Bio/Graphics/Browser2/Render/Slave.pm
index 5ea0639..f3261f1 100644
--- a/lib/Bio/Graphics/Browser2/Render/Slave.pm
+++ b/lib/Bio/Graphics/Browser2/Render/Slave.pm
@@ -3,7 +3,7 @@ package Bio::Graphics::Browser2::Render::Slave;
 use strict;
 use HTTP::Daemon;
 use Storable qw(nfreeze thaw lock_store lock_retrieve);
-use CGI qw(header param escape unescape);
+use CGI qw(header param escape unescape multi_param);
 use IO::File;
 use IO::String;
 use File::Spec;
@@ -77,7 +77,7 @@ sub kill        {
     my $self = shift;
     my $pid  = $self->pid;
     if (!$pid && (my $pidfile = $self->pidfile)) {
-	my $fh = IO::File->open($pidfile);
+	my $fh = IO::File->new($pidfile);
 	$pid   = $fh->getline;
 	chomp($pid);
 	$fh->close;
@@ -268,15 +268,15 @@ sub process_request {
 
     $self->Debug("process_request(): read ",$r->content_length," bytes");
     $self->Bench('setting environment');
-    $self->setup_environment(param('env'));
+    $self->setup_environment(multi_param('env'));
     my $operation = param('operation') || 'invalid';
 
     $self->Debug("process_request(): operation = $operation");
 
     # make sure databases are already open in parent process
     $self->Bench('thawing parameters');
-    my $tracks   = thaw param('tracks')     if param('tracks');
-    my $settings = thaw param('settings')   if param('settings');
+    my $tracks   = thaw multi_param('tracks')     if multi_param('tracks');
+    my $settings = thaw multi_param('settings')   if multi_param('settings');
     my $dsn      = param('datasource');
     my $d_name   = param('data_name');
     my $d_mtime  = param('data_mtime');
@@ -401,8 +401,8 @@ sub render_tracks {
     my $self = shift;
     my ($tracks,$datasource,$settings) = @_;
 
-    my $language	= thaw param('language');
-    my $panel_args      = thaw param('panel_args');
+    my $language	= thaw multi_param('language');
+    my $panel_args      = thaw multi_param('panel_args');
 
     $self->do_init($datasource);
     $self->adjust_conf($datasource);
@@ -498,7 +498,7 @@ sub search_features {
     my $self = shift;
     my ($tracks,$datasource,$settings) = @_;
 
-    my $searchargs      = thaw param('searchargs');
+    my $searchargs      = thaw multi_param('searchargs');
 
     # initialize a region search object
     my $search = Bio::Graphics::Browser2::RegionSearch->new(
diff --git a/lib/Bio/Graphics/Browser2/Render/Slave/AWS_Balancer.pm b/lib/Bio/Graphics/Browser2/Render/Slave/AWS_Balancer.pm
new file mode 100644
index 0000000..0e33f37
--- /dev/null
+++ b/lib/Bio/Graphics/Browser2/Render/Slave/AWS_Balancer.pm
@@ -0,0 +1,1022 @@
+package Bio::Graphics::Browser2::Render::Slave::AWS_Balancer;
+
+# This module is used to manage GBrowse slaves in an on-demand Amazon EC2
+# environment.
+
+use strict;
+use Parse::Apache::ServerStatus;
+use VM::EC2 1.22;
+use VM::EC2::Instance::Metadata;
+use VM::EC2::Staging::Manager;
+use LWP::Simple 'get','head';
+use LWP::UserAgent;
+use Parse::Apache::ServerStatus;
+use IO::File;
+use POSIX 'strftime','setsid','setuid';
+use Carp 'croak';
+use FindBin '$Bin';
+
+use constant CONFIGURE_SLAVES => "$Bin/gbrowse_configure_slaves.pl";
+
+# arguments:
+# ( -conf       => $config_path,
+#   -access_key => $aws_access_key,
+#   -secret_key => $aws_secret_key,
+#   -logfile    => $path_to_logfile,
+#   -pidfile    => $path_to_pidfile,
+#   -user       => $user_name_to_run_under,# (root only)
+#   -daemon     => $daemon_mode,
+#   -ssh_key    => $ssh_login_key (optional)
+# )
+
+sub new {
+    my $class = shift;
+    my %args  = @_;
+    $args{-conf}     or croak "-conf argument required";
+    -e $args{-conf}  or croak "$args{-conf} not found";
+
+    #setup EC2 environment
+    $args{-access_key}  ||= $ENV{EC2_ACCESS_KEY};
+    $args{-secret_key}  ||= $ENV{EC2_SECRET_KEY};
+
+    my $self = bless {
+	access_key => $args{-access_key},
+	secret_key => $args{-secret_key},
+	logfile    => $args{-logfile},
+	pidfile    => $args{-pidfile},
+	user       => $args{-user},
+	conf_file  => $args{-conf},
+	daemon     => $args{-daemon},
+	ssh_key    => $args{-ssh_key},
+	verbosity  => 2,
+    },ref $class || $class;
+    $self->initialize();
+    return $self;
+}
+
+sub logfile    {shift->{logfile}}
+sub pidfile    {shift->{pidfile}}
+sub pid        {shift->{pid}}
+sub user       {shift->{user}}
+sub daemon     {shift->{daemon}}
+sub ssh_key    {shift->{ssh_key}}
+sub ec2_credentials {
+    my $self = shift;
+    if ($self->running_as_instance) {
+	my $credentials = $self->{instance_metadata}->iam_credentials;
+	return (-security_token => $credentials) if $credentials;
+	$self->log_debug('No instance security credentials. Does this instance have an IAM role?');
+    }
+    $self->{access_key} ||= $self->_prompt('Enter your EC2 access key:');
+    $self->{secret_key} ||= $self->_prompt('Enter your EC2 secret key:');
+    return (-access_key => $self->{access_key},
+	    -secret_key => $self->{secret_key})
+}
+sub logfh {
+    my $self = shift;
+    my $d    = $self->{logfh};
+    $self->{logfh} = shift if @_;
+    $d;
+}
+
+sub verbosity {
+    my $self = shift;
+    my $d    = $self->{verbosity};
+    $self->{verbosity} = shift if @_;
+    $d;
+}
+
+sub initialize {
+    my $self = shift;
+    $self->_parse_conf_file;
+    $self->_parse_instance_metadata;
+}
+
+sub DESTROY {
+    my $self = shift;
+    $self->cleanup;
+}
+
+sub run {
+    my $self = shift;
+    $self->become_daemon && return 
+	if $self->daemon;
+
+    my $killed;
+    local $SIG{INT} = local $SIG{TERM} = sub {$self->log_info('Termination signal received');
+					      $killed++; };
+    $self->{pid} = $$;
+
+    my $poll = $self->master_poll;
+    eval {$self->ec2} or croak $@;
+    $self->log_info("Monitoring load at intervals of $poll sec\n");
+    while (sleep $poll) {
+	last if $killed;
+	my $load = $self->get_load();
+	$self->log_debug("Current load: $load req/s\n");
+	$self->adjust_instances($load);
+	$self->update_requests();
+    }
+
+    $self->log_info('Normal termination');
+}
+
+sub stop_daemon {
+    my $self = shift;
+    my $pid  = $self->pid;
+    if (!$pid && (my $pidfile = $self->pidfile)) {
+	my $fh = IO::File->new($pidfile) or croak "No PID file; is daemon runnning?";
+	$pid   = $fh->getline;
+	chomp($pid);
+	$fh->close;
+    }
+    unlink $self->pidfile if -e $self->pidfile;
+    kill TERM=>$pid if defined $pid;
+}
+
+#######################
+# configuration
+######################
+
+sub conf_file {shift->{conf_file}}
+
+sub load_table {
+    return shift->{options}{'LOAD TABLE'};
+}
+
+sub option {
+    my $self = shift;
+    my ($stanza,$option) = @_;
+    return $self->{options}{uc $stanza}{$option};
+}
+
+# given load, returns two element list of min_instances, max_instances
+sub slaves_wanted {
+    my $self = shift;
+    my $load = shift;
+
+    my $lt   = $self->load_table or croak 'no load table!';
+    my ($min,$max) = (0,0);
+    for my $l (sort {$a<=>$b} keys %$lt) {
+	($min,$max) = @{$lt->{$l}} if $load >= $l;
+    }
+    return ($min,$max);
+}
+
+sub slave_instance_type { shift->option('SLAVE','instance_type') || 'm1.large' }
+sub slave_spot_bid      { shift->option('SLAVE','spot_bid')      || 0.08       }
+sub slave_ports         { my $p = shift->option('SLAVE','ports');
+			  my @p = split /\s+/,$p;
+			  return @p ? @p : (8101); }
+sub slave_endpoint {
+    my $self = shift;
+    if ($self->running_as_instance) {
+	my $zone =  $self->{instance_metadata}->endpoint;
+	return $zone;
+    } else {
+	my $region = $self->option('SLAVE','region') || 'us-east-1';
+	return "http://ec2.$region.amazonaws.com";
+    }
+}
+
+sub slave_zone {
+    my $self = shift;
+    if ($self->running_as_instance) {
+	return $self->{instance_metadata}->availabilityZone;
+    } else {
+	$self->option('SLAVE','availability_zone');
+    }
+}
+
+sub slave_image_id {
+    my $self = shift;
+    if ($self->running_as_instance) {
+	return $self->{instance_metadata}->imageId;
+    } else {
+	$self->option('SLAVE','image_id');
+    }
+}
+
+sub slave_data_snapshots {
+    my $self  = shift;
+    return split /\s+/,$self->option('SLAVE','data_snapshots');
+}
+
+sub slave_block_device_mapping {
+    my $self = shift;
+    my $image = $self->ec2->describe_images($self->slave_image_id)
+	or die "Could not find image ",$self->slave_image_id;
+    my $root    = $image->rootDeviceName;
+    my @root    = grep {$_->deviceName eq $root} $image->blockDeviceMapping;
+    
+    my @snaps = $self->slave_data_snapshots;
+    my @bdm;
+  DEVICE:
+    for my $major ('g'..'z') {
+	for my $minor (1..15) {
+	    my $snap = shift @snaps or last DEVICE;
+	    push @bdm,"/dev/sd${major}${minor}=${snap}::true";
+	}
+    }
+    return [@root, at bdm];
+}
+
+sub slave_subnet {
+    my $self = shift;
+    if ($self->running_as_instance) {
+	return eval {(values %{$self->{instance_metadata}->interfaces})[0]{subnetId}};
+    } else {
+	$self->option('SLAVE','subnet');
+    }
+}
+
+sub slave_ssh_key {
+    my $self = shift;
+    my $key  = $self->ssh_key;
+    $key   ||= $self->option('SLAVE','ssh_key');
+    return $key;
+}
+
+sub slave_security_group {
+    my $self = shift;
+    my $sg   = $self->{slave_security_group};
+    return $sg if $sg;
+    my $ec2 = $self->ec2;
+    $sg =   eval {$ec2->describe_security_groups(-name     =>  "GBROWSE_SLAVE_$$")};
+    $sg ||= $ec2->create_security_group(-name        =>  "GBROWSE_SLAVE_$$",
+					-description => 'Temporary security group for slave communications');
+    my $ip = $self->running_as_instance ? $self->internal_ip : $self->master_ip;
+    
+    $self->log_debug(
+	$sg->authorize_incoming(-protocol  => 'tcp',
+				-port      => $_,
+				-source_ip => "$ip/32")
+	) foreach $self->slave_ports;
+    $self->log_debug(
+	$sg->authorize_incoming(-protocol => 'tcp',
+				-port     => 22,
+				-source_ip=> "$ip/32"))
+	if $self->slave_ssh_key;
+    
+    $sg->update or croak $ec2->error_str;
+    return $self->{slave_security_group} = $sg;
+}
+
+sub ec2 {
+    my $self = shift;
+    # create a new ec2 each time because security credentials may expire
+    my @credentials = $self->ec2_credentials;
+    return $self->{ec2} = VM::EC2->new(-endpoint    => $self->slave_endpoint,
+				       -raise_error => 1,
+				       @credentials);
+}
+
+sub internal_ip {
+    my $self = shift;
+    return unless $self->running_as_instance;
+    return $self->{instance_metadata}->privateIpAddress;
+}
+
+sub master_security_group {
+    my $self = shift;
+    return unless $self->running_as_instance;
+    my $sg = ($self->{instance_metadata}->securityGroups)[0];
+    $sg    =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
+    return $sg;
+}
+
+sub master_ip {
+    my $self = shift;
+    my $ip   = $self->option('MASTER','external_ip');
+    $ip ||= $self->_get_external_ip;
+    return $ip;
+}
+
+# poll interval in seconds
+sub master_poll {
+    my $self = shift;
+    my $pi   = $self->option('MASTER','poll_interval');
+    return $pi * 60;
+}
+
+sub master_server_status_url {
+    my $self = shift;
+    return $self->option('MASTER','server_status_url') 
+	|| 'http://localhost/server-status';
+}
+
+sub running_as_instance {
+    my $self = shift;
+    return -e '/var/lib/cloud/data/previous-instance-id' 
+	&& head('http://169.254.169.254');
+}
+
+
+# update conf file with new snapshot images
+sub update_data_snapshots {
+    my $self = shift;
+    my @snapshot_ids = @_;
+    my $timestamp     = 'synchronized with local filesystem on '.localtime;
+    my $conf_file     = $self->conf_file;
+    my ($user,$group) = (stat($conf_file))[4,5];
+    open my $in,'<',$conf_file        or die "Couldn't open $conf_file: $!";
+    open my $out,'>',"$conf_file.new" or die "Couldn't open $conf_file: $!";
+    while (<$in>) {
+	chomp;
+	s/^(data_snapshots\s*=).*/$1 @snapshot_ids # $timestamp/;
+	print $out "$_\n";
+    }
+    close $in;
+    close $out;
+    rename "$conf_file","$conf_file.bak" or die "Can't rename $conf_file: $!";
+    rename "$conf_file.new","$conf_file" or die "Can't rename $conf_file.new: $!";
+    chown $user,$group,$conf_file;
+}
+
+#######################
+# status
+######################
+
+# return true if slave is listening on at least one of the designated ports
+sub ping_slave {
+    my $self      = shift;
+    my $instance  = shift;
+    my $ip        = $self->running_as_instance?$instance->privateIpAddress:$instance->ipAddress;
+    my ($port) = $self->slave_ports;
+    my $ua     = LWP::UserAgent->new;
+    my $req    = HTTP::Request->new(HEAD => "http://$ip:$port");
+    my $res    = $ua->request($req);
+    return $res->code == 403;
+}
+
+# returns list of slave instances as VM::EC2::Instance objects
+sub running_slaves {
+    my $self = shift;
+    $self->{running_slaves} ||= {};
+    return values %{$self->{running_slaves}};
+}
+
+sub add_slave {
+    my $self = shift;
+    my $instance = shift;
+    $self->{running_slaves}{$instance}=$instance;
+}
+
+sub remove_slave {
+    my $self = shift;
+    my $instance = shift;
+    delete $self->{running_slaves}{$instance};
+}
+
+# given an instance ID, returns the slave VM::EC2::Instance object
+sub id2slave {
+    my $self = shift;
+    my $id   = shift;
+    return $self->{running_slaves}{$id};
+}
+
+# spot requests - only tracks pending requests
+sub pending_spot_requests {
+    my $self = shift;
+    $self->{pending_requests} ||= {};
+    return values %{$self->{pending_requests}};
+}
+
+sub add_spot_request {
+    my $self = shift;
+    my $sr   = shift;
+    $self->{pending_requests}{$sr} = $sr;
+}
+
+sub remove_spot_request {
+    my $self = shift;
+    my $sr   = shift;
+    delete $self->{pending_requests}{$sr};
+}
+
+sub id2_spot_request {
+    my $self = shift;
+    my $id   = shift;
+    return $self->{pending_requests}{$id};
+}
+
+sub get_load {
+    my $self      = shift;
+    $self->{pr} ||= Parse::Apache::ServerStatus->new(url=>$self->master_server_status_url);
+    if (-e '/tmp/gbrowse_load') {
+	open my $fh,'/tmp/gbrowse_load';
+	chomp (my $load = <$fh>);
+	return $load;
+    }
+    my $stats = $self->{pr}->get or $self->fatal("couldn't fetch load from Apache status: ",$self->{pr}->errstr);
+    return $stats->{rs};
+}
+
+
+###########################################
+# state change
+###########################################
+
+# this is called to update the number of live and pending slaves
+# according to the load
+sub adjust_instances {
+    my $self = shift;
+    my $load = shift;
+    my ($min,$max) = $self->slaves_wanted($load);
+    my $current    = $self->pending_spot_requests + $self->running_slaves;
+    
+    if ($current < $min) {
+	$self->log_debug("Need to add more slave spot instances (have $current, wanted $min)\n");
+	$self->request_spot_instance while $current++ < $min;
+    }
+
+    elsif ($current > $max) {
+	$self->log_debug("Need to delete some slave spot instances (have $current, wanted $max)\n");
+	my $reconfigure;
+	my $ec2        = $self->ec2;
+	my @candidates = ($self->pending_spot_requests,$self->running_slaves);
+	while ($current-- > $max) {
+	    my $c = shift @candidates;
+	    if ($c->isa('VM::EC2::Spot::InstanceRequest')) {
+		$self->log_debug("Cancelling spot instance request $c\n");
+		$ec2->cancel_spot_instance_requests($c);
+		$self->remove_spot_request($c);
+		$reconfigure++;
+	    } elsif ($c->isa('VM::EC2::Instance')) {
+		$self->log_debug("Terminating slave instance $c\n");
+		$ec2->terminate_instances($c);
+		$self->remove_slave($c);
+		$reconfigure++;
+	    }
+	}
+	# we reconfigure master immediately to avoid calling instance that were terminated
+	$self->reconfigure_master() if $reconfigure;
+    }
+}
+
+# this is called to act on state changes in spot requests and instances
+sub update_requests {
+    my $self = shift;
+    my @requests = $self->pending_spot_requests;
+    for my $sr (@requests) {
+	my $state    = $sr->current_status;
+	$self->log_debug("Status of $sr is $state");
+	my $instance = $sr->instance;
+	if ($state eq 'fulfilled' && $instance && $instance->instanceState eq 'running') {
+	    $instance->add_tag(Name => 'GBrowse Slave');
+	    $self->log_debug("New instance $instance; testing readiness");
+	    next unless $self->ping_slave($instance);   # not ready - try again on next poll
+	    $self->log_debug("New slave instance is ready");
+	    $self->add_slave($instance);
+	    $self->remove_spot_request($sr);            # we will never check this request again
+	    $self->reconfigure_master();
+	} elsif ($sr->current_state =~ /cancelled|failed/ ) {
+	    $self->remove_spot_request($sr);
+	}
+    }
+}
+
+# launch a spot instance request
+sub request_spot_instance {
+    my $self = shift;
+    my $ec2  = $self->ec2;
+
+    my $subnet = $self->slave_subnet;
+    my @ports  = $self->slave_ports;
+    my $key    = $self->slave_ssh_key;
+
+    my @options = (
+	-image_id             => $self->slave_image_id,
+	-instance_type        => $self->slave_instance_type,
+	-instance_count       => 1,
+	-security_group_id    => $self->slave_security_group,
+	-spot_price           => $self->slave_spot_bid,
+	-block_device_mapping => $self->slave_block_device_mapping,
+	-user_data         => "#!/bin/sh\nexec /opt/gbrowse/etc/init.d/gbrowse-slave start @ports",
+	$subnet? (-subnet_id  => $subnet)          : (),
+	$key   ? (-key_name   => $key)             : (),
+	);
+
+    my @debug_options;
+    for (my $i = 0;$i<@options;$i+=2) {
+	my $a  = $options[$i];
+	my $v  = $options[$i+1];
+	if (ref $v && ref $v eq 'ARRAY') {
+	    push @debug_options,($a=>$_) foreach @$v;
+	} else {
+	    push @debug_options,($a=>$v);
+	}
+    }
+    
+
+    my $debug_options = "@debug_options";
+    $debug_options    =~ tr/\n/ /;
+
+    $self->log_debug("Launching a spot request with options: $debug_options\n");
+    my @requests = $ec2->request_spot_instances(@options);
+    @requests or croak $ec2->error_str;
+
+    $_->add_tag(Requestor=>'GBrowse AWS Balancer') foreach @requests;
+    $self->add_spot_request($_) foreach @requests;
+}
+
+sub kill_slave {
+    my $self     = shift;
+    my $instance = shift;
+    $self->remove_slave($instance);
+    $self->reconfigure_master();
+    $instance->terminate();
+}
+
+sub reconfigure_master {
+    my $self   = shift;
+    my @slaves = $self->running_slaves;
+    my @ips    = map {$self->running_as_instance?$_->privateIpAddress:$_->ipAddress} @slaves;
+    my @a;
+    for my $i (@ips) {
+	for my $p ($self->slave_ports) {
+	    push @a,"http://$i:$p";
+	}
+    }
+    if (@a) {
+	system 'sudo',CONFIGURE_SLAVES,(map {('--set'=>$_)} @a);
+    } else {
+	system 'sudo',CONFIGURE_SLAVES,'--set','';
+    }
+	
+}
+
+sub log_debug {shift->_log(3, at _)}
+sub log_info  {shift->_log(2, at _)}
+sub log_warn  {shift->_log(1, at _)}
+sub log_crit  {shift->_log(0, at _)}
+sub fatal {
+    my $self = shift;
+    my @msg  = shift;
+    $self->log_crit(@msg);
+    die;
+}
+
+sub _log {
+    my $self = shift;
+    my ($level, at msg) = @_;
+    return unless $level <= $self->verbosity;
+    my $ts = strftime('%d/%b/%Y:%H:%M:%S %z',localtime);
+    my $msg = ucfirst "@msg";
+    chomp($msg);
+    print STDERR "[$ts] $msg\n";
+}
+
+sub cleanup {
+    my $self = shift;
+    return if !$self->{pid} || $self->{pid} != $$;
+
+    my $ec2 = eval{$self->ec2} or return;
+    $self->log_debug('Running cleanup routine');
+
+    my @requests  = $self->pending_spot_requests;
+    my @instances = ($self->running_slaves,grep {$_} map {$_->instance} @requests);
+
+    if (@instances) {
+	$self->log_debug("terminating spot instances @instances\n");
+	delete $self->{running_slaves};
+	$self->reconfigure_master();
+	$ec2->terminate_instances(@instances);
+    }
+
+    if (my @requests  = grep {$_->current_state eq 'open'} $self->pending_spot_requests) {
+	$self->log_debug("cancelling spot instance requests @requests\n");
+	$ec2->cancel_spot_instance_requests(@requests);
+	delete $self->{pending_requests};
+    }
+
+    if (my $sg = $self->{slave_security_group}) {
+	if (@instances) {
+	    $self->log_debug('waiting for running instances to terminate');
+	    $ec2->wait_for_instances(@instances);
+	}
+	$self->ec2->delete_security_group($sg);
+	delete $self->{slave_security_group};
+	$self->log_debug("deleting security group $sg\n");
+    }
+
+    unlink $self->pidfile if $self->pidfile;
+}
+
+
+
+#######################
+# Synchronization
+#######################
+
+sub launch_staging_server {
+    my $self = shift;
+    my $ec2     = $self->ec2;
+    my $staging = $self->{staging} ||= $ec2->staging_manager(-on_exit=>'run',
+							     -verbose=>3);
+    $self->log_debug("Requesting block device mapping: ",join ',',@{$self->slave_block_device_mapping});
+    my $server  = $staging->get_server(-name          => 'slave_staging_server',
+				       -username      => 'admin',
+				       -instance_type => $self->slave_instance_type,
+				       -image_name    => $self->slave_image_id,
+				       -block_devices => $self->slave_block_device_mapping,
+				       -server_class  => 'Bio::Graphics::Browser2::Render::Slave::StagingServer', # this is defined at the bottom of this .pm file
+				       -architecture  => undef
+	);
+    $server->{manager} = $staging; # avoid global destruction issues
+    return $server;
+}
+
+#######################
+# Daemon stuff
+#######################
+
+# BUG - redundant code cut-and-paste from Slave.pm
+sub become_daemon {
+    my $self = shift;
+
+    my $child = fork();
+    croak "Couldn't fork: $!" unless defined $child;
+    return $child if $child;  # return child PID in parent process
+
+    umask(0);
+    $ENV{PATH} = '/bin:/sbin:/usr/bin:/usr/sbin';
+
+    setsid();   # become process leader
+
+    # write out PID file if requested
+    if (my $l = $self->pidfile) {
+	my $fh = IO::File->new($l,">") 
+	    or $self->log_crit("Could not open pidfile $l: $!");
+	$fh->print($$)
+	    or $self->log_crit("Could not write to pidfile $l: $!");
+	$fh->close();
+    }
+    $self->open_log;
+    open STDERR,">&",$self->logfh if $self->logfh;
+
+    chdir '/';  # don't hold open working directories
+    open STDIN, "</dev/null";
+    open STDOUT,">/dev/null";
+
+    $self->set_user;
+    return;
+}
+
+# change user if requested
+sub set_user {
+    my $self = shift;
+    my $u = $self->user or return;
+    my $uid = getpwnam($u);
+    defined $uid or $self->log_crit("Cannot change uid to $u: unknown user");
+    setuid($uid) or $self->log_crit("Cannot change uid to $u: $!");
+}
+
+# open log file if requested
+sub open_log {
+    my $self = shift;
+    my $l = $self->logfile or return;
+    my $fh = IO::File->new($l,">>")  # append
+	or $self->Fatal("Could not open logfile $l: $!");
+    $fh->autoflush(1);
+    $self->logfh($fh);
+}
+
+
+#######################
+# internal routines
+######################
+
+sub _get_external_ip {
+    my $self = shift;
+    my $ip= get('http://icanhazip.com');
+    chomp($ip);
+    $self->log_info("Found external IP address $ip");
+    return $ip;
+}
+
+sub _parse_conf_file {
+    my $self = shift;
+    return if exists $self->{options}{'LOAD TABLE'};
+    open my $f,$self->conf_file or croak "Could not open ",$self->conf_file,": $!";
+    $self->{pushback} = [];
+    while (defined(my $line = $self->_getline($f))) {
+	$self->_parse_stanza($1,$f) if $line =~ /^\[([^]]+)\]/;
+    }
+    close $f;
+    croak "invalid config file; must contain [LOAD TABLE] and [SLAVE] stanzas"
+	unless exists $self->{options}{'LOAD TABLE'} and exists $self->{options}{'SLAVE'};
+}
+
+sub _parse_stanza {
+    my $self = shift;
+    my ($stanza,$fh) = @_;
+    if (uc $stanza eq 'LOAD TABLE') {
+	$self->_parse_load_table($fh);
+    } else {
+	$self->_parse_regular_stanza($stanza,$fh);
+    }
+}
+
+sub _parse_load_table {
+    my $self = shift;
+    my $fh   = shift;
+    while (my $line = $self->_get_stanza_line($fh)) {
+	my @tokens = split /\s+/,$line;
+	@tokens    == 3 or croak "invalid load table line: $line";
+	my ($load,$min,$max) = @tokens;
+	$self->{options}{'LOAD TABLE'}{$load} = [$min,$max];
+    } 
+}
+
+sub _parse_regular_stanza {
+    my $self = shift;
+    my ($stanza,$fh) = @_;
+    while (my $line = $self->_get_stanza_line($fh)) {
+	my ($option,$value) = $line =~ /^(\S+)\s*=\s*(.+)/ or next;
+	$self->{options}{uc $stanza}{$option} = $value;
+    }
+}
+
+sub _get_stanza_line {
+    my $self = shift;
+    my $fh   = shift;
+    local $^W=0;
+    my $line = $self->_getline($fh);
+    if ($line =~ /^\[/) {
+	push @{$self->{pushback}},$line;
+	return;
+    }
+    return $line;
+}
+
+sub _getline {
+    my $self = shift;
+    my $fh   = shift;
+
+    if (@{$self->{pushback}}) {
+	return pop @{$self->{pushback}};
+    }
+
+    while (1) {
+	defined(my $line = <$fh>) or return;
+	chomp $line;
+	$line =~ /^\s*#/ and next;
+	$line =~ s/\s+#.*$//;
+	$line =~ /\S/    or  next;
+	return $line;
+    }
+}
+
+sub _parse_instance_metadata {
+    my $self = shift;
+    $self->{instance_metadata} ||= VM::EC2::Instance::Metadata->new();
+}
+
+sub _prompt {
+    my $self = shift;
+    my $msg  = shift;
+    -t \*STDIN or return;
+    print STDERR $msg;
+    my $result = <STDIN>;
+    chomp $result;
+    return $result;
+}
+
+##############################################################################################################
+# descendent of VM::EC2::Staging::Server that adds a few tricks
+##############################################################################################################
+
+package Bio::Graphics::Browser2::Render::Slave::StagingServer;
+use base 'VM::EC2::Staging::Server';
+use Sys::Hostname;
+
+use constant GB => 1_073_741_824;
+use constant TB => 1_099_511_627_776;
+
+# return the size of the /opt/gbrowse volume in GB
+# we intentionally truncate to floor
+sub volume_size {
+    my $self = shift;
+    my $df   = $self->scmd('df -B 1 /opt/gbrowse');
+    my ($total,$used,$available) = $df =~ /(\d+)\s+(\d+)\s+(\d+)/;
+    return int(0.5 + $total/GB);
+}
+
+sub grow_volume {
+    my $self = shift;
+    my $gig_wanted = shift;
+
+    # get information about the /dev/volumes/gbrowse lv
+    my ($lv,$vg,undef,$size) = split /,/,$self->scmd('sudo lvs /dev/volumes/gbrowse --noheadings --units g --nosuffix --separator ,');
+    $lv =~ s/^\s+//;
+    my $needed = int($gig_wanted-$size);
+    return if $needed <= 0;
+
+    $self->info("Resizing /opt/gbrowse to $gig_wanted...\n");
+
+    # get information about the physical volumes that belong to this group
+    my %volumes;
+    my $fh = $self->scmd_read('sudo pvs --noheadings --units g --nosuffix --separator ,');
+    while (<$fh>) {
+	chomp;
+	s/^\s+//;
+	my ($pv,$vg,undef,undef,$used,$free) = split /,/;
+	next unless $vg eq 'volumes';
+	$volumes{$pv} = $used+$free;
+    }
+    close $fh;
+
+    # select a volume to resize
+    my $to_resize;
+    for my $pv (sort {$a<=>$b} keys %volumes) {
+	if ($volumes{$pv} + $needed < 1000) {
+	    $to_resize  = $pv;
+	    last;
+	}
+    }
+
+    # If we found a pv that we can resize sufficiently, then go ahead and do that.
+    # Otherwise, we add a new EBS volume to the volume group.
+    $self->info("Unmounting /opt/gbrowse filesystem...\n");
+    $self->ssh('sudo umount /opt/gbrowse') or die "Couldn't umount";
+
+    if ($to_resize) {
+	$self->_resize_pv($to_resize,int($volumes{$to_resize}+$needed));
+    } else {
+	$self->_extend_vg('volumes',int($needed));
+    }
+    
+    # If we get here, the volume group has been extended, so we can
+    # resize the logical volume and the filesystem
+    $self->info("Resizing logical volume...\n");
+    $self->ssh('sudo lvextend -l +100%FREE /dev/volumes/gbrowse') or die "Couldn't lvresize";
+
+    $self->info("Checking filesystem prior to resizing...\n");
+    $self->ssh('sudo e2fsck -f -p /dev/volumes/gbrowse')          or die "e2fsck failed";
+
+    $self->info("Resizing filesystem...\n");
+    $self->ssh('sudo resize2fs -p /dev/volumes/gbrowse')          or die "Couldn't resize2fs";
+
+    $self->info("Remounting filesystem...\n");
+    $self->ssh('sudo mount /opt/gbrowse')                         or die "Couldn't mount";
+
+    1;
+}
+
+sub terminate {
+    my $self = shift;
+    $self->manager->unregister_server($self) if $self->manager;
+    $self->ec2->terminate_instances($self);
+}
+
+sub start_services {
+    my $self = shift;
+    $self->_start_stop_services('start');
+}
+
+sub stop_services {
+    my $self = shift;
+    $self->_start_stop_services('stop');
+}
+
+sub _start_stop_services {
+    my $self = shift;
+    my $action = shift or die "usage: _start_stop_services(start|stop)";
+    $self->info($action eq 'stop' ? "Stopping services...\n":"Starting services...\n");
+    foreach ('apache2','mysql','postgresql') {
+	$self->ssh("sudo service $_ $action");
+    }
+}
+
+sub snapshot_data_volumes {
+    my $self = shift;
+
+    my %volumes;
+    my $fh = $self->scmd_read('sudo pvs --noheadings --units g --nosuffix --separator ,');
+    while (<$fh>) {
+	chomp;
+	s/^\s+//;
+	my ($pv,$vg,undef,undef,$used,$free) = split /,/;
+	next unless $vg eq 'volumes';
+	$pv =~ s!/dev/xvd!/dev/sd!;
+	$volumes{$pv}++;
+    }
+    close $fh;
+
+    my $hostname  = hostname();
+    my $timestamp = localtime();
+
+    # get the EBS volumes for this device
+    my @vols   = map {$_->volume} grep {$volumes{$_->deviceName}} $self->blockDeviceMapping;
+    @vols or die "Could not find the EBS volumes to snapshot";
+
+    $self->info("Unmounting filesystem...\n");
+    $self->ssh('sudo umount /opt/gbrowse') or die "Couldn't umount";
+
+    $self->info("Creating snapshots...\n");
+    my @snapshots = map {$_->create_snapshot("GBrowse data volume synchronized with $hostname on $timestamp")} @vols;
+    $_->add_tag(Name => "GBrowse data from ${hostname}\@${timestamp}") foreach @snapshots;
+
+    $self->info("Remounting filesystem...\n");
+    $self->ssh('sudo mount /opt/gbrowse') or die "Couldn't mount";
+
+    return @snapshots;
+}
+
+sub _extend_vg {
+    my $self = shift;
+    my ($vg,$size) = @_;
+    
+    my ($ebs_device,$local_device) = $self->unused_block_device();
+    $self->info("Creating ${size}G EBS volume...\n");
+    my $vol = $self->ec2->create_volume(-availability_zone => $self->placement,
+					-size              => $size) or die "Couldn't create EBS volume: ",$self->ec2->error_str;
+    $self->ec2->wait_for_volumes($vol);
+    $vol->current_status eq 'available' or die "EBS volume creation failed: ",$self->ec2->error_str;
+    
+    my $a = $vol->attach($self => $ebs_device) or die "EBS volume attachment failed: ",$self->ec2->error_str;
+    $self->ec2->wait_for_attachments($a);
+    $a->current_status eq 'attached'           or die "Volume attachment failed: ",$self->ec2->error_str;
+
+    $a->deleteOnTermination(1);
+
+    $self->info("Creating LVM2 physical device...\n");
+    $self->ssh("sudo pvcreate $local_device")          or die "pvcreate failed";
+
+    $self->info("Extending 'volumes' volume group...\n");
+    $self->ssh("sudo vgextend volumes $local_device")  or die "vgextend failed";
+
+    1;
+}
+
+sub _resize_pv {
+    my $self = shift;
+    my ($device,$new_size) = @_;
+
+    # get the EBS volume for this device
+    my @mapping   = $self->blockDeviceMapping;
+    (my $ebs_device = $device) =~ s!/dev/xvd!/dev/sd!;
+    my ($mapping) = grep /$ebs_device/, at mapping;
+    $mapping or die "Couldn't find an EBS mapping for $device";
+
+    my $volume_id = $mapping->volumeId;
+    my $volume    = $mapping->volume;
+    
+    # disable the volume group
+    $self->ssh('sudo vgchange -an volumes') or die "Couldn't vgchange";
+    
+    # detach the underlying device
+    $self->info("Detaching volume $volume...\n");
+    my $a = $volume->detach                 or die "Couldn't detach";
+    $self->ec2->wait_for_attachments($a);
+
+    # snapshot it
+    $self->info("Snapshotting volume $volume...\n");
+    my $snapshot = $volume->create_snapshot('created by '.__PACKAGE__) or die "Couldn't snapshot: ",$self->ec2->error_str;
+    $self->ec2->wait_for_snapshots($snapshot);
+    $snapshot->current_status eq 'completed' or die "Snapshot errored: ",$self->ec2->error_str;
+    
+    # create a new volume of the appropriate size
+    $self->info("Creating new volume from snapshot...\n");
+    my $zone = $volume->availabilityZone;
+    
+    my $new_volume = $self->ec2->create_volume(-availability_zone => $zone,
+					       -size              => $new_size,
+					       -snapshot_id       => $snapshot) or die "Couldn't create volume: ",$self->ec2->error_str;
+    $self->ec2->wait_for_volumes($new_volume);
+    $new_volume->current_status eq 'available' or die "Volume error: ",$self->ec2->error_str;
+
+    $self->info("Attaching new volume...\n");
+    $a = $self->attach_volume($new_volume => $ebs_device);
+    $self->ec2->wait_for_attachments($a);
+    $new_volume->deleteOnTermination(1);
+
+    # activate 
+    $self->info("Resizing physical volume...\n");
+    $self->ssh("sudo pvresize $device")     or die "Couldn't pvresize";
+    $self->ssh('sudo vgchange -ay volumes') or die "Couldn't vgchange";
+
+    # get rid of the old volume and the new snapshot (which we no longer need)
+    $self->ec2->delete_volume($volume);
+    $self->ec2->delete_snapshot($snapshot);
+
+    1;
+}
+
+1;
+
+=head1 AUTHOR
+
+Lincoln Stein E<lt>lincoln.stein at gmail.comE<gt>.
+
+Copyright (c) 2012 Ontario Institute for Cancer Research
+
+This package and its accompanying libraries is free software; you can
+redistribute it and/or modify it under the terms of the GPL (either
+version 1, or at your option, any later version) or the Artistic
+License 2.0.  Refer to LICENSE for the full license text. In addition,
+please see DISCLAIMER.txt for disclaimers of warranty.
+
+=cut
+
diff --git a/lib/Bio/Graphics/Browser2/Render/Slave/Status.pm b/lib/Bio/Graphics/Browser2/Render/Slave/Status.pm
index e58c2bd..4e9d9f0 100644
--- a/lib/Bio/Graphics/Browser2/Render/Slave/Status.pm
+++ b/lib/Bio/Graphics/Browser2/Render/Slave/Status.pm
@@ -26,7 +26,7 @@ sub new {
     },ref $class || $class;
 }
 
-sub can_lock { shift->{dbfilelock} }
+sub can_lock { shift->{canlock} }
 
 sub db {
     my $self  = shift;
@@ -58,7 +58,6 @@ sub status {
     return 'up'   if $status;
     return 'up'   if (time() - $last_checked) >= $check_time;
 
-    warn "$slave is down" if DEBUG;
     return 'down';
 }
 
@@ -102,13 +101,23 @@ sub select {
     my $db     = $self->db(0);
     my @up     = grep {$self->status($_,$db) eq 'up'} @slaves;
 
-    warn "up slaves = @up" if DEBUG;
+    warn "[$$] up slaves = @up" if DEBUG;
 
     return $up[rand @up];
 }
 
+sub up_slaves {
+    my $self   = shift;
+    my $db     = shift || $self->db(0);
+    my @slaves = keys %$db;
+    return grep {$self->status($_,$db) eq 'up'} @slaves;
+}
 
+1;
+
+=head1 Author
+
+Lincoln D. Stein <lincoln.stein at gmail.com>
 
 
 
-1;
diff --git a/lib/Bio/Graphics/Browser2/RenderPanels.pm b/lib/Bio/Graphics/Browser2/RenderPanels.pm
index 600a30c..659bb23 100644
--- a/lib/Bio/Graphics/Browser2/RenderPanels.pm
+++ b/lib/Bio/Graphics/Browser2/RenderPanels.pm
@@ -147,12 +147,16 @@ sub request_panels {
   my $do_local  = @$local_labels;
   my $do_remote = @$remote_labels;
 
+  if (!($do_local || $do_remote)) {
+      warn "[$$] No uncached requests to process..." if DEBUG;
+      return $data_destinations;
+  }
   # In the case of a deferred request we fork.
   # Parent returns the list of requests.
   # Child processes the requests in the background.
   # If both local and remote requests are needed, then we
   # fork a second time and process them in parallel.
-  if ($args->{deferred}) {
+  elsif ($args->{deferred}) {
 
       # precache local databases into cache
       my $length = $self->segment_length;
@@ -178,7 +182,7 @@ sub request_panels {
 					 $local_labels );
           }
           else {
-              $self->run_remote_requests( $data_destinations, 
+              $self->run_remote_requests( $data_destinations,
 					  $args,
 					  $remote_labels );
           }
@@ -196,7 +200,7 @@ sub request_panels {
   }
 
   else { # not deferred
-      $self->run_local_requests($data_destinations,$args,$local_labels);  
+      $self->run_local_requests($data_destinations,$args,$local_labels);
       $self->run_remote_requests($data_destinations,$args,$remote_labels);
       return $data_destinations;
   }
@@ -271,14 +275,13 @@ sub make_requests {
     foreach my $label ( @{ $labels || [] } ) {
 
         my @track_args = $self->create_track_args( $label, $args );
-
 	my (@filter_args, at featurefile_args, at subtrack_args);
 
 	my $format_option = $settings->{features}{$label}{options};
 
 	my $filter     = $settings->{features}{$label}{filter};
 	@filter_args   = %{$filter->{values}} if $filter->{values};
-	@subtrack_args = @{$settings->{subtracks}{$label}} 
+	@subtrack_args = @{$settings->{subtracks}{$label}}
 	                 if $settings->{subtracks}{$label};
 	my $ff_error;
 
@@ -294,24 +297,24 @@ sub make_requests {
 		    -cache_base => $base,
 		    -panel_args => \@panel_args,
 		    -track_args => \@track_args,
-		    -extra_args => [ @cache_extra, 
-				     @filter_args, 
-				     @featurefile_args, 
+		    -extra_args => [ @cache_extra,
+				     @filter_args,
+				     @featurefile_args,
 				     @subtrack_args,
-				     $format_option, 
+				     $format_option,
 				     $label ],
 		    );
-    
+
 		my $msg = eval {$args->{remotes}->error($track)};
 		$cache_object->flag_error($msg || "Could not fetch data for $track");
 		$d{$track} = $cache_object;
 		next;
 	    }
-	    
+
 	    # broken logic here?
 	    # next unless $label =~ /:$args->{section}$/;
 	    @featurefile_args =  eval {
-		$feature_file->isa('Bio::Das::Segment')||$feature_file->types, 
+		$feature_file->isa('Bio::Das::Segment')||$feature_file->types,
 		$feature_file->mtime;
 	    };
 	}
@@ -325,11 +328,11 @@ sub make_requests {
             -cache_base => $base,
             -panel_args => \@panel_args,
             -track_args => \@track_args,
-            -extra_args => [ @cache_extra, 
-			     @filter_args, 
-			     @featurefile_args,  
-			     @subtrack_args, 
-			     $format_option, 
+            -extra_args => [ @cache_extra,
+			     @filter_args,
+			     @featurefile_args,
+			     @subtrack_args,
+			     $format_option,
 			     $label ],
 	    -cache_time => $cache_time
         );
@@ -345,7 +348,7 @@ sub use_renderfarm {
   return $self->{use_renderfarm} if exists $self->{use_renderfarm};
 
   #comment out to force remote rendering (kludge)
-  $self->source->global_setting('renderfarm') or return;	
+  $self->source->global_setting('renderfarm') or return;
 
   $LPU_AVAILABLE = eval { require LWP::UserAgent; }           unless defined $LPU_AVAILABLE;
   $STO_AVAILABLE = eval { require Storable; 1; }              unless defined $STO_AVAILABLE;
@@ -398,9 +401,9 @@ sub render_tracks {
 	    section  => $args->{section},
         );
     }
-    
+
     return \%result;
- 
+
 }
 
 # Returns the HMTL to show a track with controls, title, arrows, etc.
@@ -413,7 +416,7 @@ sub wrap_rendered_track {
     my $height = $args{'height'};
     my $url    = $args{'url'};
     my $titles = $args{'titles'};
-  
+
     # track_type Used in register_track() javascript method
     my $track_type = $args{'track_type'} || 'standard';
     my $status = $args{'status'};    # for debugging
@@ -424,13 +427,15 @@ sub wrap_rendered_track {
     my $kill     = "$buttons/ex.png";
     my $share    = "$buttons/share.png";
     my $help     = "$buttons/query.png";
+    my $pop_in   = "$buttons/pop_in.png";
+    my $pop_out  = "$buttons/pop_out.png";
     my $download = "$buttons/download.png";
     my $configure= "$buttons/tools.png";
     my $menu 	 = "$buttons/menu.png";
     my $favicon  = "$buttons/fmini.png";
     my $favicon_2= "$buttons/fmini_2.png";
     my $add_or_remove = $self->language->translate('ADDED_TO') || 'Add track to favorites';
-    
+
     my $settings = $self->settings;
     my $source   = $self->source;
 
@@ -453,8 +458,9 @@ sub wrap_rendered_track {
             -height => $height,
             -border => 0,
             -name   => $label,
+	    -class  => 'track_image',
             -style  => $img_style
-	    
+
         }
     );
 
@@ -478,6 +484,10 @@ sub wrap_rendered_track {
     $about_this_track .= $self->language->translate('ABOUT_THIS_TRACK',$label)
         || "<b>About this track</b>";
 
+    my $popout = '';
+    $popout .= $self->language->translate('POP_OUT')
+	|| "<b>Pop out/in</b>";
+
     my $escaped_label = CGI::escape($label);
 
     # The inline config will go into a box 500px wide by 500px tall
@@ -502,9 +512,7 @@ sub wrap_rendered_track {
     }
 
     my $help_url       = "url:?action=cite_track;track=$escaped_label";
-    my $help_click     = "GBox.showTooltip(event,'$help_url',1)"; 
-
-    
+    my $help_click     = "GBox.showTooltip(event,'$help_url',1)";
 
     my $download_click = "GBox.showTooltip(event,'url:?action=download_track_menu;track=$escaped_label;view_start='+TrackPan.get_start()+';view_stop='+TrackPan.get_stop(),true)" unless $label =~ /^(http|ftp)/;
 
@@ -524,8 +532,8 @@ sub wrap_rendered_track {
 	$title = $source->setting( $label => 'key') || $l;
     }
     $title =~ s/:(overview|region|detail)$//;
-   
-    my $balloon_style = $source->global_setting('balloon style') || 'GBubble'; 
+
+    my $balloon_style = $source->global_setting('balloon style') || 'GBubble';
     my $favorite      = $settings->{favorites}{$label};
     my $starIcon      = $favorite ? $favicon_2 : $favicon;
     my $starclass     = $favorite ? "toolbarStar favorite" : "toolbarStar";
@@ -541,7 +549,7 @@ sub wrap_rendered_track {
 				$self->if_not_ipad(-onMouseOver => "$balloon_style.showTooltip(event,'$add_or_remove')"),
 			    })
 	              : '',
-	img({   -src         => $icon, 
+	img({   -src         => $icon,
                 -id          => "${label}_icon",
                 -onClick     =>  "collapse('$label')",
                 -style       => 'cursor:pointer',
@@ -578,76 +586,83 @@ sub wrap_rendered_track {
 				$self->if_not_ipad(-onMouseOver => "$balloon_style.showTooltip(event,'$configure_this_track')"),
 			    })
 	              : '',
+	);
 
-        img({   -src         => $help,
-                 -style       => 'cursor:pointer',
-                 -onmousedown => $help_click,
-                 -onMouseOver =>
-             "$balloon_style.showTooltip(event,'$about_this_track')",
-             }
-        )
-	); 
+    my $help_img = img({   -src         => $help,
+			   -style       => 'cursor:pointer',
+			   -onmousedown => $help_click,
+			   -onMouseOver =>
+			       "$balloon_style.showTooltip(event,'$about_this_track')",
+		       });
+
+    my $pin_img = img({ -src          => $pop_out,
+			-class        => 'pin_button',
+			-onmousedown  => 'Controller.ghost_track(this)',
+			-onmouseover  => "$balloon_style.showTooltip(event,'$popout')",
+		      }
+	);
 
     my $ipad_collapse = $collapsed ? 'Expand':'Collapse';
     my $cancel_ipad = 'Turn off';
-    my $share_ipad = 'Share'; 
+    my $share_ipad = 'Share';
     my $configure_ipad = 'Configure';
     my $download_ipad = 'Download';
     my $about_ipad = 'About track';
 
-    my $bookmark = 'Favorite'; 
-    my $menuicon = img ({-src => $menu, 
+    my $bookmark = 'Favorite';
+    my $menuicon = img ({-src => $menu,
 			 -style => 'padding-right:15px;',},),
-   
-    my $popmenu = div({-id =>"popmenu_${title}", -style => 'display:none'},
-		      div({-class => 'ipadtitle', -id => "${label}_title",}, $title ),
-		      div({-class => 'ipadcollapsed', 
-			   -id    => "${label}_icon", 
-			   -onClick =>  "collapse('$label')",
-			  },
-			  div({-class => 'linkbg', 
-			       -onClick => "swap(this,'Collapse','Expand')", 
-			       -id => "${label}_expandcollapse", },$ipad_collapse)),
-		      div({-class => 'ipadcollapsed',
-			   -id => "${label}_kill",
-			   -onClick     => "ShowHideTrack('$label',false)",
-			  }, div({-class => 'linkbg',},
-				 $cancel_ipad)),
-		      div({-class => 'ipadcollapsed',  
-			   -onMousedown => "Controller.get_sharing(event,'url:?action=share_track;track=$escaped_label',true)",}, 
-			  div({-class => 'linkbg',},$share_ipad)),
-		      div({-class => 'ipadcollapsed',  -
-			       onmousedown => $config_click,}, div({-class => 'linkbg',},$configure_ipad)),
-		      div({-class => 'ipadcollapsed',  
-			   -onmousedown => $fav_click,}, 
-			  div({-class => 'linkbg', -onClick => "swap(this,'Favorite','Unfavorite')"},$bookmark)),
-		      div({-class => 'ipadcollapsed',  
-			   -onmousedown => $download_click,}, 
-			  div({-class => 'linkbg',},$download_ipad)),
-		      div({-class => 'ipadcollapsed', 
-			   -style => 'width:200px',  
-			   -onmousedown => $help_click,}, 
-			  div({-class => 'linkbg', -style => 'position:relative; left:30px;',},$about_ipad)),
- 		  );
-    
+
+    my $popmenu = $self->if_ipad(
+	div({-id =>"popmenu_${title}", -style => 'display:none'},
+	    div({-class => 'ipadtitle', -id => "${label}_title",}, $title ),
+	    div({-class => 'ipadcollapsed',
+		 -id    => "${label}_icon",
+		 -onClick =>  "collapse('$label')",
+		},
+		div({-class => 'linkbg',
+		     -onClick => "swap(this,'Collapse','Expand')",
+		     -id => "${label}_expandcollapse", },$ipad_collapse)),
+	    div({-class => 'ipadcollapsed',
+		 -id => "${label}_kill",
+		 -onClick     => "ShowHideTrack('$label',false)",
+		}, div({-class => 'linkbg',},
+		       $cancel_ipad)),
+	    div({-class => 'ipadcollapsed',
+		 -onMousedown => "Controller.get_sharing(event,'url:?action=share_track;track=$escaped_label',true)",},
+		div({-class => 'linkbg',},$share_ipad)),
+	    div({-class => 'ipadcollapsed',  -
+		     onmousedown => $config_click,}, div({-class => 'linkbg',},$configure_ipad)),
+	    div({-class => 'ipadcollapsed',
+		 -onmousedown => $fav_click,},
+		div({-class => 'linkbg', -onClick => "swap(this,'Favorite','Unfavorite')"},$bookmark)),
+	    div({-class => 'ipadcollapsed',
+		 -onmousedown => $download_click,},
+		div({-class => 'linkbg',},$download_ipad)),
+	    div({-class => 'ipadcollapsed',
+		 -style => 'width:200px',
+		 -onmousedown => $help_click,},
+		div({-class => 'linkbg', -style => 'position:relative; left:30px;',},$about_ipad)),
+	));
+    $popmenu ||= ''; # avoid uninit variable warning
+
     my $clipped_title = $title;
     $clipped_title    = substr($clipped_title,0,MAX_TITLE_LEN-3).'...' if length($clipped_title) > MAX_TITLE_LEN;
 
     # modify the title if it is a track with subtracks
     $self->select_features_menu($label,\$clipped_title);
-    
-    my $titlebar = 
+
+    my $titlebar =
 	span(
 		{   -class => $collapsed ? 'titlebar_inactive' : 'titlebar',
 		    -id => "${label}_title",
 				},
-
- 	    $self->if_not_ipad(@images,),
-	    $self->if_ipad(span({-class => 'menuclick',  -onClick=> "GBox.showTooltip(event,'load:popmenu_${title}')"}, $menuicon,),),	
-	    span({-class => 'drag_region',},$clipped_title),
-
+ 	    $self->if_not_ipad(@images),
+	    $self->if_ipad(span({-class => 'menuclick',  -onClick=> "GBox.showTooltip(event,'load:popmenu_${title}')"}, $menuicon,)),
+	    span({-class => 'drag_region'},
+		 span({-style=>'display:inline-block;width:32px'},' '),
+		 $clipped_title.' '.$help_img.span({-style=>'display:inline-block;width:32px'},' ').$pin_img)
 	);
-
     my $show_titlebar
         = ( ( $source->setting( $label => 'key' ) || '' ) ne 'none' );
     my $is_scalebar = $label =~ /scale/i;
@@ -668,6 +683,7 @@ sub wrap_rendered_track {
             -width  => $pad->width,
             -border => 0,
             -id     => "${label}_pad",
+	    -class  => 'track_image',
             -style  => $collapsed ? "display:inline" : "display:none",
         }
     );
@@ -684,21 +700,21 @@ sub wrap_rendered_track {
 	    -onClick => "Controller.scroll('left',0.5)"
 	});
 
-	my $pan_right  = img({ 
+	my $pan_right  = img({
 	    -style   => $style . ';right:5px',
 	    -class   => 'panright',
 	    -src     => "$buttons/panright.png",
 	    -onClick => "Controller.scroll('right',0.5)",
 	});
-	
-	my $scale_div = div( { -id => "detail_scale_scale", 
+
+	my $scale_div = div( { -id => "detail_scale_scale",
 			       -style => "position:absolute; top:12px", }, "" );
 
-        $overlay_div = div( { -id => "${label}_overlay_div", 
+        $overlay_div = div( { -id => "${label}_overlay_div",
 			      -style => "position:absolute; top:0px; width:100%; left:0px", }, $pan_left . $pan_right . $scale_div);
     }
 
-    my $inner_div = div( { -id => "${label}_inner_div" }, $img . $pad_img ); #Should probably improve this
+    my $inner_div = div( { -id => "${label}_inner_div",-class=>'inner_div' }, $img . $pad_img ); #Should probably improve this
 
 
     my $subtrack_labels = join '',map {
@@ -735,7 +751,7 @@ sub if_ipad {
     return  if !$probably_ipad;
     return @args;
 }
-# This routine is called to hand off the rendering to a remote renderer. 
+# This routine is called to hand off the rendering to a remote renderer.
 # The remote processor does not have to have a copy of the config file installed;
 # the entire DataSource object is sent to it in serialized form via
 # POST. It returns a serialized hash consisting of the GD object and the imagemap.
@@ -791,13 +807,11 @@ sub run_remote_requests {
   }
 
   # sort requests by their renderers
-  my $slave_status = Bio::Graphics::Browser2::Render::Slave::Status->new(
-      $source->globals->slave_status_path
-      );
+  my $slave_status = $self->slave_status;
 
   my %renderers;
   for my $label (@labels_to_generate) {
-      my $url     = $source->remote_renderer or next;
+      my $url     = $source->remote_renderer($label) or next;
       my @urls    = shellwords($url);
       $url        = $slave_status->select(@urls);
       warn "label => $url (selected)" if DEBUG;
@@ -812,26 +826,27 @@ sub run_remote_requests {
   }
 
   my $ua = LWP::UserAgent->new;
-  my $timeout = $source->global_setting('slave_timeout') 
-      || $source->global_setting('global_timeout') 
+  my $timeout = $source->global_setting('slave_timeout')
+      || $source->global_setting('global_timeout')
       || 30;
   $ua->timeout($timeout);
 
   for my $url (keys %renderers) {
 
-      my $child   = $render->fork();
+      my $child   = Bio::Graphics::Browser2::Render->fork();
       next if $child;
 
       my $total_time = time();
 
       # THIS PART IS IN THE CHILD
       my @labels   = keys %{$renderers{$url}};
+      warn "REMOTE FETCH ON @labels" if DEBUG;
       my $s_track  = Storable::nfreeze(\@labels);
 
       foreach (@labels) {
 	  $requests->{$_}->lock();   # flag that request is in process
       }
-  
+
       my $tries = 0;
     FETCH: {
 	my $request = POST ($url,
@@ -857,11 +872,11 @@ sub run_remote_requests {
 	if ($response->is_success) {
 	    my $contents = Storable::thaw($response->content);
 	    for my $label (keys %$contents) {
-		my $map = $contents->{$label}{map}        
+		my $map = $contents->{$label}{map}
 		  or die "Expected a map from remote server, but got nothing!";
-		my $titles = $contents->{$label}{titles}        
+		my $titles = $contents->{$label}{titles}
 		  or die "Expected titles from remote server, but got nothing!";
-		my $gd2 = $contents->{$label}{imagedata}  
+		my $gd2 = $contents->{$label}{imagedata}
 		  or die "Expected imagedata from remote server, but got nothing!";
 		$requests->{$label}->put_data($gd2,$map,$titles);
 	    }
@@ -876,16 +891,16 @@ sub run_remote_requests {
 	    my $uri = $request->uri;
 	    my $response_line = $response->status_line;
 	    $slave_status->mark_down($url);
-	  
+
 	    # try to recover from a transient slave failure; this only works
 	    # right if all of the tracks there are multiple equivalent slaves for the tracks
-	    my %urls    = map {$_=>1} 
+	    my %urls    = map {$_=>1}
   	                    map {
 				shellwords($source->remote_renderer)
 			    } @labels;
 	    my $alternate_url = $slave_status->select(keys %urls);
 	    if ($alternate_url) {
-		warn "retrying fetch of @labels with $alternate_url";
+		warn "[$$] retrying fetch of @labels with $alternate_url";
 		$url = $alternate_url;
 		redo FETCH if $tries++ < SLAVE_RETRIES;
 	    }
@@ -902,6 +917,15 @@ sub run_remote_requests {
   }
 }
 
+sub slave_status {
+    my $self = shift;
+    my $source = $self->source;
+    return $self->{slave_status} ||=
+	Bio::Graphics::Browser2::Render::Slave::Status->new(
+	    $source->globals->slave_status_path
+	);
+}
+
 # Sort requests into those to be performed locally
 # and remotely. Returns two arrayrefs (\@local_labels,\@remote_labels)
 # Our algorithm is very simple. It is a remote request if the "remote renderer"
@@ -928,16 +952,19 @@ sub sort_local_remote {
 	return (\@uncached,[]);
     }
 
+    my $slave_status = $self->slave_status;
+
     my $url;
-    my %is_remote = map { $_ => ( 
+    my %is_remote = map { $_ => (
 			      !/plugin:/ &&
 			      !/file:/   &&
 			      !/^(ftp|http|das):/ &&
 			      !$source->is_usertrack($_) &&
 			      !$source->is_remotetrack($_) &&
-			      (($url = $source->remote_renderer||0) &&
+			      (($url = $source->remote_renderer($_)||0) &&
 			      ($url ne 'none') &&
-			      ($url ne 'local')))
+			      ($url ne 'local') &&
+			      $slave_status->select(shellwords($url))))
                         } @uncached;
 
     my @remote    = grep {$is_remote{$_} } @uncached;
@@ -1032,7 +1059,7 @@ sub render_scale_bar {
     $add_track_extra_args{'-postgrid'} = $args{'postgrid'} if $args{'postgrid'};
 
     my @panel_args = $self->create_panel_args(
-        {   section        => $section, 
+        {   section        => $section,
             segment        => $wide_segment,
             flip           => $flip,
             %add_track_extra_args
@@ -1068,7 +1095,7 @@ sub render_scale_bar {
 	    for my $type (@feature_types) {
 	    my $features = $feats->features($type);
 	    my %options  = $feats->style($type);
-	    $panel->add_track($features,%options);  
+	    $panel->add_track($features,%options);
 	    }
 
 	}
@@ -1262,7 +1289,7 @@ sub make_imagemap_element_inline {
 
     if ($summary) {
 	return {onmouseover => $self->feature_summary_message('mouseover',$label),
-		onmouseeown => $self->feature_summary_message('mousedown',$label),
+		onmousedown => $self->feature_summary_message('mousedown',$label),
 		href        => 'javascript:void(0)',
 		inline      => 1
 	}
@@ -1382,7 +1409,7 @@ sub make_centering_map {
 
     my $url = "?ref=$ref;start=$start;stop=$stop";
     $url .= ";flip=1" if $flip;
-    push @map, join("\t",'ruler',$x2, $ruler->[2], $x2, $ruler->[4], 
+    push @map, join("\t",'ruler',$x2, $ruler->[2], $x2, $ruler->[4],
 		    href  => $url, title => 'recenter', alt   => 'recenter');
   }
   return $label ? \@map : @map;
@@ -1390,7 +1417,7 @@ sub make_centering_map {
 
 # this is the routine that actually does the work!!!!
 # input
-#    arg1: request hashref 
+#    arg1: request hashref
 #                 {label => Bio::Graphics::Browser2::CachedTrack}
 #    arg2: arguments hashref
 #                  {
@@ -1444,7 +1471,7 @@ sub run_local_requests {
 
     #---------------------------------------------------------------------------------
     # Track and panel creation
-    
+
     my %seenit;           # avoid error of putting track on list multiple times
     my %results;          # hash of {$label}{gd} and {$label}{map}
     my %feature_file_offsets;
@@ -1475,7 +1502,7 @@ sub run_local_requests {
         # this shouldn't happen, but let's be paranoid
         next if $seenit{$label}++;
 
-	# don't let there be more than this many processes 
+	# don't let there be more than this many processes
 	# running simultaneously
 	while ((my $c = keys %children) >= $max_processes) {
 	    warn "[$$] too many processes ($c), sleeping" if DEBUG;
@@ -1494,7 +1521,7 @@ sub run_local_requests {
 	(my $base = $label) =~ s/:(overview|region|details?)$//;
 	warn "label=$label, base=$base, file=$feature_files->{$base}" if DEBUG;
 
-	my $multiple_tracks = $base =~ /^(http|ftp|file|das|plugin):/ 
+	my $multiple_tracks = $base =~ /^(http|ftp|file|das|plugin):/
 	    || $source->code_setting($base=>'remote feature');
 
         my @keystyle = ( -key_style    => 'between',
@@ -1504,14 +1531,14 @@ sub run_local_requests {
 	my $key = $source->setting( $base => 'key' ) || '' ;
 	my @nopad = ();
         my $panel_args = $requests->{$label}->panel_args;
-	
+
         my $panel
             = Bio::Graphics::Panel->new( @$panel_args, @keystyle, @nopad );
 
         my %trackmap;
 
 	my $timeout         = $source->global_setting('global_timeout');
-	
+
 	my $oldaction;
 	my $time = time();
 	eval {
@@ -1535,7 +1562,7 @@ sub run_local_requests {
 		    # Add feature files, including remote annotations
 		    my $featurefile_select = $args->{featurefile_select}
 		    || $self->feature_file_select($section);
-			
+
  		    if ( ref $file and $panel ) {
 			$self->add_feature_file(
 			    file     => $file,
@@ -1602,7 +1629,7 @@ sub run_local_requests {
 
 	my $elapsed = time()-$time;
 	warn "render($label): $elapsed seconds ", ($@ ? "(error)" : "(ok)") if BENCHMARK;
-	
+
 	if ($@) {
 	    warn "RenderPanels error: $@";
 	    if ($@ =~ /timeout/) {
@@ -1626,7 +1653,7 @@ sub run_local_requests {
 
     # make sure requests are populated
     # the "1" argument turns off expiration checking
-    $requests->{$_}->get_data(1) foreach keys %$requests;  
+    $requests->{$_}->get_data(1) foreach keys %$requests;
 }
 
 sub render_hidden_track {
@@ -1658,7 +1685,7 @@ sub select_features_menu {
 			   -onClick      => $subtrack_click
 			  },
 			  $self->language->translate('SHOWING_SUBTRACKS',$selected,$total));						#;
-    
+
 }
 
 sub generate_filters {
@@ -1733,10 +1760,10 @@ sub add_features_to_track {
 	  $type2label{$_}{$l}++ foreach @types;
       }
       $self->{_type2label}=\%type2label;
-      
+
       warn "[$$] RenderPanels->get_iterator(@full_types)"  if DEBUG;
       warn "[$$] RenderPanels->get_summary_iterator(@summary_types)" if DEBUG;
-      if (@summary_types && 
+      if (@summary_types &&
 	  (my $iterator = $self->get_summary_iterator($db2db{$db},$segment,\@summary_types))) {
 	  $iterators{$iterator}     = $iterator;
 	  $iterator2dbid{$iterator} = $source->db2id($db);
@@ -1787,14 +1814,14 @@ sub add_features_to_track {
 	  # Handle name-based groupings.
 	  unless (exists $group_pattern{$l}) {
 	      $group_pattern{$l} =  $source->semantic_setting($l => 'group_pattern',$length);
-	      $group_pattern{$l} =~ s!^/(.+)/$!$1! 
+	      $group_pattern{$l} =~ s!^/(.+)/$!$1!
 		  if $group_pattern{$l}; # clean up regexp delimiters
 	  }
-	  
+
 	  # Handle generic grouping (needed for GFF3 database)
- 	  $group_field{$l} = $source->semantic_setting($l => 'group_on',$length) 
+ 	  $group_field{$l} = $source->semantic_setting($l => 'group_on',$length)
 	      unless exists $group_field{$l};
-	  
+
 	  if (my $pattern = $group_pattern{$l}) {
 	      my $name = $feature->name or next;
 	      (my $base = $name) =~ s/$pattern//i;
@@ -1805,7 +1832,7 @@ sub add_features_to_track {
 	      $groups{$l}{$base}->add_segment($feature);
 	      next;
 	  }
-	
+
 	  if (my $field = $group_field{$l}) {
 	      my $base = eval{$feature->$field};
 	      if (defined $base) {
@@ -1858,7 +1885,7 @@ sub add_features_to_track {
 						 -name   => $stt->id2label($_),
 						 -start  => $segment->start,
 						 -end    => $segment->end,
-						 -seq_id => $segment->seq_id) 
+						 -seq_id => $segment->seq_id)
 	    foreach @ids
     }
 
@@ -1904,7 +1931,7 @@ sub add_features_to_track {
       if $limit && $limit > 0;
 
     # essentially make label invisible if we are going to get the label position
-    $tracks->{$l}->configure(-fontcolor   => 'white:0.0') 
+    $tracks->{$l}->configure(-fontcolor   => 'white:0.0')
 	if $tracks->{$l}->parts->[0]->record_label_positions;
 
     if (eval{$tracks->{$l}->features_clipped}) { # may not be present in older Bio::Graphics
@@ -1978,7 +2005,7 @@ Internal use: render a feature file into a panel
 sub add_feature_file {
   my $self = shift;
   my %args = @_;
-  
+
   my $file    = $args{file}    or return;
   my $options = $args{options} or return;
   my $select  = $args{select}  or return;
@@ -2049,7 +2076,7 @@ sub create_panel_args {
   }
   elsif ($section eq 'detail'){
     $postgrid = make_postgrid_callback($settings);
-    $h_region_str = join(':', @{$settings->{h_region}||[]}); 
+    $h_region_str = join(':', @{$settings->{h_region}||[]});
   }
 
   my $keystyle = 'none';
@@ -2073,13 +2100,12 @@ sub create_panel_args {
 	      -postgrid     => $postgrid,
 	      -background   => $args->{background} || '',
 	      -truecolor    => $source->global_setting('truecolor') || 0,
-	      -map_fonts_to_truetype    => $source->global_setting('truetype') || 0,
+              -truetype     => $source->global_setting('truetype') || 0,
 	      -extend_grid  => 1,
               -gridcolor    => $source->global_setting('grid color') || 'lightcyan',
               -gridmajorcolor    => $source->global_setting('grid major color') || 'cyan',
 	      @pass_thru_args,   # position is important here to allow user to override settings
 	     );
-
   push @argv, -flip => 1 if $flip;
   my $p  = $self->image_padding;
   my $pl = $source->global_setting('pad_left');
@@ -2095,7 +2121,7 @@ sub create_panel_args {
 sub image_padding {
   my $self   = shift;
   my $source = $self->source;
-  return defined $source->global_setting('image_padding') 
+  return defined $source->global_setting('image_padding')
       ? $source->global_setting('image_padding')
       : PAD_DETAIL_SIDES;
 }
@@ -2172,7 +2198,7 @@ sub create_track_args {
 
   my @summary_args = ();
   if ($is_summary) {
-      @summary_args = $source->Bio::Graphics::FeatureFile::setting("$label:summary") 
+      @summary_args = $source->Bio::Graphics::FeatureFile::setting("$label:summary")
 	  ? $source->i18n_style("$label:summary",$lang)
 	  : (-glyph     => 'wiggle_density',
 	     -height    => 15,
@@ -2188,10 +2214,10 @@ sub create_track_args {
 
   if (my $stt = $self->subtrack_manager($label)) {
       push @default_args,(-connector   => '');
-      my $left_label = 
+      my $left_label =
 	  $source->semantic_setting($label=>'label_position',$length)||'' eq 'left';
 
-      $left_label++ 
+      $left_label++
 	  if $source->semantic_setting($label=>'label_transcripts',$length);
 
       my $group_label = $source->semantic_setting($label=>'glyph',$length) !~ /xyplot|wiggle|density|whisker|vista/;
@@ -2387,7 +2413,7 @@ sub feature2label {
     my $feature = shift;
     my $type2label = $self->{_type2label} or die "no type2label map defined";
     my $type = eval {$feature->type} || eval{$feature->source_tag} || eval{$feature->primary_tag} or return;
-    (my $basetype = $type) =~ s/:.+$//;
+    (my $basetype = $type) =~ s/:.*$//;
     my $labels = $type2label->{$type}||$type2label->{$basetype} or return;
     my @labels = keys %$labels;
     return @labels;
@@ -2399,7 +2425,7 @@ sub make_link {
   my ($feature,$panel,$label,$track)  = @_;
   my $label_fix = $label;
 
-  if (ref $label && $label->{name}){ 
+  if (ref $label && $label->{name}){
     $label_fix = $label->{name};
     if ($label_fix =~/^(plugin)\:/){$label_fix = join(":",($',$1));}
   }
@@ -2514,7 +2540,7 @@ sub make_title {
     } else {
       my ($start,$end) = ($feature->start,$feature->end);
       ($start,$end)    = ($end,$start) if $feature->strand < 0;
-      my $name         = $feature->can('info') 
+      my $name         = $feature->can('info')
 	                 ? $feature->info
 			 : $feature->display_name;
       my $result;
@@ -2582,6 +2608,7 @@ sub balloon_tip_setting {
   } else {
     $val = $source->link_pattern($value,$feature,$panel);
   }
+  $val ||= '';
 
   if ($val=~ /^\s*\[([\w\s]+)\]\s+(.+)/s) {
     $balloon_type = $1;
@@ -2616,7 +2643,7 @@ sub make_postgrid_callback {
     return hilite_regions_closure(@h_regions);
 }
 
-# this subroutine generates a Bio::Graphics::Panel callback closure 
+# this subroutine generates a Bio::Graphics::Panel callback closure
 # suitable for hilighting a region of a panel.
 # The args are a list of [start,end,bgcolor,fgcolor]
 sub hilite_regions_closure {
@@ -2665,7 +2692,7 @@ sub loaded_segment_outline {
     return $self->source->global_setting('loaded segment outline') || 'gray';
 }
 
-sub details_mult { 
+sub details_mult {
     my $self = shift;
     my $render = $self->render;
     return $render->details_mult if $render;
diff --git a/lib/Bio/Graphics/Browser2/SendMail.pm b/lib/Bio/Graphics/Browser2/SendMail.pm
index 4fbb69b..f14b132 100644
--- a/lib/Bio/Graphics/Browser2/SendMail.pm
+++ b/lib/Bio/Graphics/Browser2/SendMail.pm
@@ -49,7 +49,7 @@ sub do_sendmail {
 	  my $smtp_obj = $smtp_sender->new(
 	      $server,
 	      Port    => $port,
-	      Debug   => 1,
+	      Debug   => 0,
 	      )
 	      or die "Could not connect to outgoing mail server $server";
 	  
diff --git a/lib/Bio/Graphics/Browser2/TrackDumper.pm b/lib/Bio/Graphics/Browser2/TrackDumper.pm
index 1809a84..5d1c248 100644
--- a/lib/Bio/Graphics/Browser2/TrackDumper.pm
+++ b/lib/Bio/Graphics/Browser2/TrackDumper.pm
@@ -375,6 +375,7 @@ sub available_formats {
     push @formats,'vista','vista_wiggle','vista_peaks' if  $glyph =~ /vista/i;
     push @formats,'sam'   if  $db->isa('Bio::DB::Bam')    or $db->isa('Bio::DB::Sam');
     push @formats,'bed'   if  $db->isa('Bio::DB::BigWig') or $db->isa('Bio::DB::BigWigSet');
+    push @formats, qw(gff3 bed) if $db->isa('Bio::DB::BigBed');
     push @formats,'bed'   if  $glyph =~ /wiggle|xyplot|density/;
     my %seenit;
     return grep {!$seenit{$_}++} @formats;
@@ -399,6 +400,7 @@ sub guess_dump_method {
     return 'dump_gff3_autowig'   if $db->isa('Bio::DB::GFF');
     return 'dump_gff3_autowig'   if $db->isa('Bio::DB::Das::Chado');
     return 'dump_gff3_autowig'   if $db->isa('Bio::DB::DasI');
+    return 'dump_gff3_autowig'   if $db->isa('Bio::DB::BigBed');
 
     my $type = $self->guess_file_type();
     return 'dump_sam'    if $type eq 'sam';
diff --git a/lib/Bio/Graphics/Browser2/UserDB.pm b/lib/Bio/Graphics/Browser2/UserDB.pm
index 1df759d..917df6a 100644
--- a/lib/Bio/Graphics/Browser2/UserDB.pm
+++ b/lib/Bio/Graphics/Browser2/UserDB.pm
@@ -755,12 +755,16 @@ sub do_edit_confirmation {
   my $select = $userdb->prepare(<<END );
 SELECT b.username, a.userid, b.sessionid, a.gecos, a.cnfrm_code
     FROM users as a,session as b 
-    WHERE a.email=? AND a.userid=b.userid
+    WHERE a.email=? AND a.userid=b.userid AND a.confirmed=0
 END
   $select->execute($email)
     or return $self->dbi_err;
   my ($username,$userid,$sessionid,$fullname, $confirm) = $select->fetchrow_array();
 
+  unless ($username) {
+      return $self->string_result("Invalid username $username");
+  }
+
   if ($option == 0) { # delete account!
       eval {
 	  local $userdb->{AutoCommit} = 0;
diff --git a/lib/Bio/Graphics/Browser2/UserTracks.pm b/lib/Bio/Graphics/Browser2/UserTracks.pm
index 0ad3f1f..b63915e 100644
--- a/lib/Bio/Graphics/Browser2/UserTracks.pm
+++ b/lib/Bio/Graphics/Browser2/UserTracks.pm
@@ -362,6 +362,9 @@ sub import_url {
     elsif ($url =~ /\.bw$/) {
 		print $f $self->remote_bigwig_conf($file, $url, $key);
     }
+    elsif ($url =~ /\.bb$/) {
+		print $f $self->remote_bigbed_conf($file, $url, $key);
+    }
     else {
 		print $f $self->remote_mirror_conf($file, $url, $key);# Conf Metadata (File) - Returns the modified time and size of a track's configuration file.
     }
@@ -401,7 +404,7 @@ sub mirror_url {
     
     warn "mirroring..." if DEBUG;
 
-    if ($url =~ /\.(bam|bw)$/ or $url =~ /\b(gbgff|das)\b/) {
+    if ($url =~ /\.(bam|bw|bb)$/ or $url =~ /\b(gbgff|das)\b/) {
 		return $self->import_url($url, $overwrite);
     }
 
@@ -501,6 +504,7 @@ sub upload_file {
 
     my $original_name = $file_name;
     $file_name    =~ s/\.(gz|bz2)$//;  # to indicate that it is decompressed
+    $file_name =~ s/\.(tgz|tbz|tbz2)$/.tar/; # change archive extension   
 
     warn "$file_name: OVERWRITE = $overwrite" if DEBUG;
 
@@ -510,9 +514,9 @@ sub upload_file {
     
     if ($original_name =~ /\.sam\.gz/) { # special case compressed sam files - do not uncompress!
 #	$filename = $original_name;
-    } elsif ($content_type eq 'application/gzip' or $original_name =~ /\.gz$/) {
+    } elsif ($content_type eq 'application/gzip' or $original_name =~ /\.(?:tgz|gz)$/) {
 	$fh = $self->install_filter($fh,'gunzip -c');
-    } elsif ($content_type eq 'application/bzip2' or $original_name =~ /\.bz2$/) {
+    } elsif ($content_type eq 'application/bzip2' or $original_name =~ /\.(?:tbz2?|bz2)$/) {
 	$fh = $self->install_filter($fh,'bunzip2 -c');
     }
     
@@ -527,7 +531,8 @@ sub upload_file {
     my $result = eval {
 		local $SIG{TERM} = sub { die "cancelled" };
 		croak "Could not guess the type of the file $file_name"	unless $type;
-		croak "This server does not support BigWig upload" if $type =~ /bigwig/ && !$self->has_bigwig;
+		croak "This server does not support $type uploads" 
+		    if $type =~ /bigwig|bigbed|useq|archive/ && !$self->has_bigwig;
 		my $load = $self->get_loader($type, $file);
 		$load->eol_char($eol);
 		@tracks = $load->load($lines, $fh);
@@ -705,11 +710,22 @@ sub _guess_upload_type {
     my $buffer;
     read($fh,$buffer,1024);
 
-    # first check for binary upload; currently only BAM & bigwig
+    # first check for binary upload
+    my $magic = substr($buffer,0,4);
     return ('bam',[$buffer],undef)
-	if substr($buffer,0,6) eq "\x1f\x8b\x08\x04\x00\x00";
+	  if $magic eq "\x1f\x8b\x08\x04";
     return('bigwig',[$buffer],undef)
-	if substr($buffer,0,4) eq "\x26\xfc\x8f\x88";
+	  if $magic eq "\x26\xfc\x8f\x88";
+    return('bigbed',[$buffer],undef)
+	  if $magic eq "\xeb\xf2\x89\x87";
+	if ($magic eq "\x50\x4B\x03\x04") { # zip file
+		return('useq', [$buffer], undef) if $filename =~ /\.useq$/i;
+		return('archive', [$buffer], undef) if $filename =~ /\.zip$/i;
+	}
+    
+    # check for archives
+    return('archive', [$buffer], undef) if 
+    	$filename =~ /\.(?:tar|tgz|tbz|tbz2)(?:\.(gz|bz2))?$/i;
     
     # everything else is text (for now)
     my $eol = $buffer =~ /\015\012/ ? "\015\012"  # MS-DOS
@@ -729,10 +745,13 @@ sub _guess_upload_type {
 	       :$filename =~ /\.gff3(\.(gz|bz2|Z))?$/i ? 'gff3'
 	       :$filename =~ /\.bed(\.(gz|bz2|Z))?$/i  ? 'bed'
 	       :$filename =~ /\.bw$/i                  ? 'bigwig'
+	       :$filename =~ /\.bb$/i                  ? 'bigbed'
 	       :$filename =~ /\.wig(\.(gz|bz2|Z))?$/i  ? 'wiggle'
 	       :$filename =~ /\.fff(\.(gz|bz2|Z))?$/i  ? 'featurefile'
 	       :$filename =~ /\.bam(\.gz)?$/i          ? 'bam'
 	       :$filename =~ /\.sam(\.gz)?$/i          ? 'sam'
+	       :$filename =~ /\.useq$/i                ? 'useq'
+	       :$filename =~ /\.(?:tar|tgz|tbz|tbz2|zip)(?:\.(gz|bz2))?$/i ? 'archive'
 	       :undef;
     
     return ($ftype,\@lines,$eol) if $ftype;
@@ -896,6 +915,52 @@ height          = 20
 END
 }
 
+sub remote_bigbed_conf {
+    my $self = shift;
+    my $file = shift;
+    my $filename = $self->filename($file);
+    my ($url, $key) = @_;
+    my $id = rand(1000);
+    my $dbname = "remotebb_$id";
+    my $track_id = $filename;
+    my @COLORS = qw(blue red orange brown mauve peach 
+                green cyan yellow coral);
+    my $color = $COLORS[rand @COLORS];
+    warn "remote_bigbed_conf";
+    return <<END;
+[$dbname:database]
+db_adaptor = Bio::DB::BigBed
+db_args    = -bigbed $url
+search options = none
+
+>>>>>>>>>>>>>> cut here <<<<<<<<<<<<
+[$track_id]
+database        = $dbname
+feature  = region
+glyph    = segments
+label density = 50
+feature_limit = 500
+bump     = fast
+stranded = 1
+height   = 4
+bgcolor  = $color
+fgcolor  = $color
+key      = $filename segments
+description = 
+
+[$track_id\_coverage]
+database        = $dbname
+feature  = summary
+glyph    = wiggle_whiskers
+fgcolor  = black
+height   = 50
+autoscale = chromosome
+key      = $filename coverage
+description = 
+
+END
+}
+
 sub _print_url {
     my $self = shift;
     my ($agent, $url, $fh) = @_;
diff --git a/lib/Bio/Graphics/Karyotype.pm b/lib/Bio/Graphics/Karyotype.pm
index a7b9d82..dc9f34b 100644
--- a/lib/Bio/Graphics/Karyotype.pm
+++ b/lib/Bio/Graphics/Karyotype.pm
@@ -307,7 +307,6 @@ sub generate_panels {
 			      ? 'generic'
 			      : 'diamond';
 		      },
-		      -glyph => 'generic',
 		      -maxdepth => 0,
 		      -height  => 6,
 		      -bgcolor => 'red',
diff --git a/lib/Legacy/Graphics/Browser.pm b/lib/Legacy/Graphics/Browser.pm
index a3a77d0..2dedf8e 100644
--- a/lib/Legacy/Graphics/Browser.pm
+++ b/lib/Legacy/Graphics/Browser.pm
@@ -1420,7 +1420,7 @@ sub add_features_to_track {
   my $conf    = $self->config;
 
   my (%groups,%feature_count,%group_pattern,%group_field);
-  my $iterator = $segment->get_feature_stream(-type=>$feature_types);
+  my $iterator = $segment->get_seq_stream(-type=>$feature_types);
 
   while (my $feature = $iterator->next_seq) {
 
diff --git a/lib/Legacy/Graphics/Browser/Synteny.pm b/lib/Legacy/Graphics/Browser/Synteny.pm
index d9fc45f..871861c 100644
--- a/lib/Legacy/Graphics/Browser/Synteny.pm
+++ b/lib/Legacy/Graphics/Browser/Synteny.pm
@@ -115,7 +115,7 @@ sub source_menu {
 			 -values   => \@sources,
 			 -labels   => { map { $_ => $self->description($_) } $self->sources },
 			 -default  => $self->source,
-			 -onChange => 'document.mainform.submit()'
+			 -onChange => 'document.searchform.submit()'
 			 );
   return b( $self->tr('DATA_SOURCE') ) . br
       . ( $sources ? $popup : $self->description( $self->source ) );
@@ -228,7 +228,7 @@ sub zoomBar {
     -labels   => \%labels,
     -default  => $segment->length,
     -force    => 1,
-    -onChange => 'document.mainform.submit()',
+    -onChange => 'document.searchform.submit()',
   );
 }
 
@@ -744,6 +744,7 @@ sub print_page_top {
   my $cookie  = CGI::Cookie->new(-name    => $CGI::Session::NAME,
  			        -value   => $session->id,
 			        -path    => url(-absolute=>1),
+				-httponly => 1,
 			        -expires => '+1d');
 
   print_header(-cookie => [$cookie], -expires => 'now');
@@ -775,14 +776,20 @@ sub print_page_top {
   print start_html(@args);
 
   # make a sham controller to keep the GB2 js happy
-  print <<END;
+  print <<"END";
  <script type="text/javascript">
    var Controller;
    Controller = new Object();
    Controller.gbrowse_syn = true;    
    Controller.update_coordinates = function (segment) {
-     document.mainform.name.value = segment;
-     document.mainform.submit();
+     document.searchform.name.value = segment;
+     document.searchform.submit();
+   };
+   Controller.translate = function (term) {
+     term += '';
+     term = term + term.toLowerCase();
+     var f = term.charAt(0).toUpperCase();
+     return f + term.substr(1);
    };
  </script>
 END
diff --git a/sample_data/yeast_chr1+2/dummy.fa b/sample_data/yeast_chr1+2/dummy.fa
new file mode 100644
index 0000000..e69de29
diff --git a/t/00.compile.t b/t/00.compile.t
index 3fb6d32..91c5d99 100644
--- a/t/00.compile.t
+++ b/t/00.compile.t
@@ -78,7 +78,8 @@ sub skip_file {
 	   gbrowse_attach_slaves.pl
 	   make_das_conf.pl
 	   gbrowse_slave_start_aws.sh
-	   gbrowse_launch_aws_slaves.pl
+	   gbrowse_aws_balancer.pl
+	   gbrowse_sync_aws_slave.pl
           );
 
     return $skip{ basename($file) };
diff --git a/t/04.remoteserver.t b/t/04.remoteserver.t
index 54de983..a014a3f 100644
--- a/t/04.remoteserver.t
+++ b/t/04.remoteserver.t
@@ -1,157 +1,157 @@
-#-*-Perl-*-
-
-# Before `make install' is performed this script should be runnable with
-# `make test'. After `make install' it should work as `perl test.t'
-
-use strict;
-use warnings;
-use Module::Build;
-use Bio::Root::IO;
-use File::Path 'rmtree';
-use IO::String;
-use CGI;
-use FindBin '$Bin';
-
-use constant TEST_COUNT => 47;
-use constant CONF_FILE  => "$Bin/testdata/conf/GBrowse.conf";
-use constant DEBUG => 0;
-
-my $PID;
-
-BEGIN {
-    print STDERR "Sometimes this test gets 'stuck'. If this happens, kill the test and Build test again.\n";
-
-  # to handle systems with no installed Test module
-  # we include the t dir (where a copy of Test.pm is located)
-  # as a fallback
-  eval { require Test; };
-  if( $@ ) {
-    use lib 't';
-  }
-  use Test;
-  plan test => TEST_COUNT;
-
-  $PID = $$;
-
-  rmtree "/tmp/gbrowse_testing";
-  rmtree "/tmp/gbrowse";
-}
-
-$SIG{INT} = sub {exit 0};
-
-%ENV = ();
-$ENV{GBROWSE_DOCS} = $Bin;
-$ENV{TMPDIR}       = '/tmp/gbrowse_testing';
-
-chdir $Bin;
-use lib "$Bin/../lib";
-use Bio::Graphics::Browser2;
-use Bio::Graphics::Browser2::Render::HTML;
-use LWP::UserAgent;
-use HTTP::Request::Common;
-use Storable 'nfreeze','thaw';
-use Bio::Graphics::Browser2::Render::Slave;
-
-use lib "$Bin/testdata";
-use TemplateCopy; # for the template_copy() function
-
-# alignments requires the server at 8100
-my $alignment_server = Bio::Graphics::Browser2::Render::Slave->new(LocalPort=>'dynamic');
-$alignment_server->debug(DEBUG);
-$alignment_server->run();
-
-# cleavage sites track requires the server at 8101
-my $cleavage_server  = Bio::Graphics::Browser2::Render::Slave->new(LocalPort=>'dynamic');
-$cleavage_server->debug(DEBUG);
-$cleavage_server->run();
-
-# rewrite the template config files
-for ('volvox_final.conf','yeast_chr1.conf') {
-    template_copy("testdata/conf/templates/$_",
-		  "testdata/conf/$_",
-		  {'$REMOTE1'=>"http://localhost:".$alignment_server->listen_port,
-		   '$REMOTE2'=>"http://localhost:".$cleavage_server->listen_port});
-}
-
-
-# Test remote rendering
-my $server = Bio::Graphics::Browser2::Render::Slave->new(LocalPort=>'dynamic');
-ok($server);
-$server->debug(DEBUG);
-my $server_pid = $server->run;
-ok($server_pid);
-
-sleep 1; # give slave renderers a chance to settle down
-$ENV{REQUEST_URI}    = 'http://localhost/cgi-bin/gbrowse/volvox';
-$ENV{PATH_INFO}      = '/volvox';
-$ENV{REQUEST_METHOD} = 'GET';
-$CGI::Q    = new CGI('name=ctgA:1..20000;label=Clones-Motifs-Transcripts;cache=1');
-
-# this is the standard initialization, ok?
-my $globals = Bio::Graphics::Browser2->new(CONF_FILE);
-
-my $session = $globals->session;
-my $source  = $globals->create_data_source('volvox');
-my $render  = Bio::Graphics::Browser2::Render::HTML->new($source,$session);
-
-$render->init_database;
-$render->init_plugins;
-$render->update_state;
-$render->segment;  # this sets the segment
-
-# this is what is needed to invoke the remote renderer, ok?
-my @labels   = $render->detail_tracks;
-my $settings = $render->state;
-my $lang     = $render->language;
-
-my $port     = $server->listen_port;
-
-my $request  = POST("http://localhost:$port/",
-		    Content_Type => 'form-data',
-		    Content =>
-		    [
-		     tracks     => nfreeze(\@labels),
-		     settings   => nfreeze($settings),
-		     datasource => nfreeze($source),
-		     data_name  => $source->name,
-		     data_mtime => $source->mtime,
-		     language   => nfreeze($lang),
-		     panel_args => nfreeze({}),
-		     operation  => 'render_tracks',
-		    ]);
-for (1..3) {
-    my $ua        = LWP::UserAgent->new;
-    my $response  = $ua->request($request);
-
-    ok($response->is_success,1,$response->as_string);
-    my $skipit = !$response->is_success;
-    skip($skipit,
-	 $response->header('Content-type'),
-	 'application/gbrowse-encoded-genome');
-    my $content  = thaw $response->content;
-    skip($skipit,ref $content,'HASH');
-    for (qw(Clones Motifs Transcripts)) {
-	    skip($skipit,exists $content->{$_});
-	    skip($skipit,exists $content->{$_}{imagedata});
-	    skip($skipit,length($content->{$_}{imagedata}) > 0);
-	}
-}
-# now we test whether parallel rendering is working
- at labels = qw(CleavageSites Alignments Motifs BindingSites);
-
-$render->set_tracks(@labels);
+ #-*-Perl-*-
+
+ # Before `make install' is performed this script should be runnable with
+ # `make test'. After `make install' it should work as `perl test.t'
+
+ use strict;
+ use warnings;
+ use Module::Build;
+ use Bio::Root::IO;
+ use File::Path 'rmtree';
+ use IO::String;
+ use CGI;
+ use FindBin '$Bin';
+
+ use constant TEST_COUNT => 43;
+ use constant CONF_FILE  => "$Bin/testdata/conf/GBrowse.conf";
+ use constant DEBUG => 0;
+
+ my $PID;
+
+ BEGIN {
+     print STDERR "Sometimes this test gets 'stuck'. If this happens, kill the test and Build test again.\n";
+
+   # to handle systems with no installed Test module
+   # we include the t dir (where a copy of Test.pm is located)
+   # as a fallback
+   eval { require Test; };
+   if( $@ ) {
+     use lib 't';
+   }
+   use Test;
+   plan test => TEST_COUNT;
+
+   $PID = $$;
+
+   rmtree "/tmp/gbrowse_testing";
+   rmtree "/tmp/gbrowse";
+ }
+
+ $SIG{INT} = sub {exit 0};
+
+ %ENV = ();
+ $ENV{GBROWSE_DOCS} = $Bin;
+ $ENV{TMPDIR}       = '/tmp/gbrowse_testing';
+
+ chdir $Bin;
+ use lib "$Bin/../lib";
+ use Bio::Graphics::Browser2;
+ use Bio::Graphics::Browser2::Render::HTML;
+ use LWP::UserAgent;
+ use HTTP::Request::Common;
+ use Storable 'nfreeze','thaw';
+ use Bio::Graphics::Browser2::Render::Slave;
+
+ use lib "$Bin/testdata";
+ use TemplateCopy; # for the template_copy() function
+
+ # alignments requires the server at 8100
+ my $alignment_server = Bio::Graphics::Browser2::Render::Slave->new(LocalPort=>'dynamic');
+ $alignment_server->debug(DEBUG);
+ $alignment_server->run();
+
+ # cleavage sites track requires the server at 8101
+ my $cleavage_server  = Bio::Graphics::Browser2::Render::Slave->new(LocalPort=>'dynamic');
+ $cleavage_server->debug(DEBUG);
+ $cleavage_server->run();
+
+ # rewrite the template config files
+ for ('volvox_final.conf','yeast_chr1.conf') {
+     template_copy("testdata/conf/templates/$_",
+		   "testdata/conf/$_",
+		   {'$REMOTE1'=>"http://localhost:".$alignment_server->listen_port,
+		    '$REMOTE2'=>"http://localhost:".$cleavage_server->listen_port});
+ }
+
+
+ # Test remote rendering
+ my $server = Bio::Graphics::Browser2::Render::Slave->new(LocalPort=>'dynamic');
+ ok($server);
+ $server->debug(DEBUG);
+ my $server_pid = $server->run;
+ ok($server_pid);
+
+ sleep 1; # give slave renderers a chance to settle down
+ $ENV{REQUEST_URI}    = 'http://localhost/cgi-bin/gbrowse/volvox';
+ $ENV{PATH_INFO}      = '/volvox';
+ $ENV{REQUEST_METHOD} = 'GET';
+ $CGI::Q    = new CGI('name=ctgA:1..20000;label=Clones-Motifs-Transcripts;cache=1');
+
+ # this is the standard initialization, ok?
+ my $globals = Bio::Graphics::Browser2->new(CONF_FILE);
+
+ my $session = $globals->session;
+ my $source  = $globals->create_data_source('volvox');
+ my $render  = Bio::Graphics::Browser2::Render::HTML->new($source,$session);
+
+ $render->init_database;
+ $render->init_plugins;
+ $render->update_state;
+ $render->segment;  # this sets the segment
+
+ # this is what is needed to invoke the remote renderer, ok?
+ my @labels   = $render->detail_tracks;
+ my $settings = $render->state;
+ my $lang     = $render->language;
+
+ my $port     = $server->listen_port;
+
+ my $request  = POST("http://localhost:$port/",
+		     Content_Type => 'form-data',
+		     Content =>
+		     [
+		      tracks     => nfreeze(\@labels),
+		      settings   => nfreeze($settings),
+		      datasource => nfreeze($source),
+		      data_name  => $source->name,
+		      data_mtime => $source->mtime,
+		      language   => nfreeze($lang),
+		      panel_args => nfreeze({}),
+		      operation  => 'render_tracks',
+		     ]);
+ for (1..3) {
+     my $ua        = LWP::UserAgent->new;
+     my $response  = $ua->request($request);
+
+     ok($response->is_success,1,$response->as_string);
+     my $skipit = !$response->is_success;
+     skip($skipit,
+	  $response->header('Content-type'),
+	  'application/gbrowse-encoded-genome');
+     my $content  = thaw $response->content;
+     skip($skipit,ref $content,'HASH');
+     for (qw(Clones Motifs Transcripts)) {
+	     skip($skipit,exists $content->{$_});
+	     skip($skipit,exists $content->{$_}{imagedata});
+	     skip($skipit,length($content->{$_}{imagedata}) > 0);
+	 }
+ }
+ # now we test whether parallel rendering is working
+ @labels = qw(CleavageSites Alignments Motifs BindingSites);
+
+ $render->set_tracks(@labels);
+
+ my $view = $render->render_detailview($render->segment);
+ my @images = $view =~ m!src=\"(/gbrowse/i/volvox/[a-z0-9]+\.png)\"!g;
+ foreach (@images) {
+     s!/gbrowse/i!/tmp/gbrowse_testing/images!;
+ }
+ ok (scalar @images,4); # 1 image and 1 pad from each of Alignments and CleavageSites
 
-my $view = $render->render_detailview($render->segment);
-my @images = $view =~ m!src=\"(/gbrowse/i/volvox/[a-z0-9]+\.png)\"!g;
-foreach (@images) {
-    s!/gbrowse/i!/tmp/gbrowse_testing/images!;
-}
 for my $img (@images) {
     ok (-e $img && -s _);
 }
 
-ok (scalar @images,8);
-
 # uncomment to see the images
 #warn join ' ', at images;
 #sleep 5000;
diff --git a/t/07.balancer.t b/t/07.balancer.t
new file mode 100755
index 0000000..bf862ba
--- /dev/null
+++ b/t/07.balancer.t
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.t'
+use strict;
+use warnings;
+use FindBin '$Bin';
+use Test::More;
+
+use constant TESTS      => 12;
+use constant CONF_FILE  => "$Bin/testdata/conf/aws_slave.conf";
+
+
+BEGIN {
+      use lib "$Bin/../lib";
+      if (!eval {require Parse::Apache::ServerStatus;1}) {
+	  plan skip_all => 'Optional module Parse::Apache::ServerStatus not installed';
+      } elsif (!eval "use VM::EC2 1.22; 1") {
+	  plan skip_all => 'Optional module VM::EC2 (v1.22 or higher) not installed';
+      } else {   
+	  plan tests => TESTS;
+      }
+      use_ok('Bio::Graphics::Browser2::Render::Slave::AWS_Balancer');
+}
+
+my $b = Bio::Graphics::Browser2::Render::Slave::AWS_Balancer->new(-conf=>CONF_FILE);
+$b or BAIL_OUT("Couldn't create balancer");
+$b->verbosity(0);
+my $instance = $b->running_as_instance;
+
+is_deeply([$b->slaves_wanted(0.1)],[0,1],'load table test 1');
+is_deeply([$b->slaves_wanted(0.4)],[0,1],'load table test 2');
+is_deeply([$b->slaves_wanted(0.5)],[0,2],'load table test 3');
+is_deeply([$b->slaves_wanted(0.6)],[0,2],'load table test 4');
+is_deeply([$b->slaves_wanted(1.0)],[1,4],'load table test 5');
+is_deeply([$b->slaves_wanted(1.1)],[1,4],'load table test 6');
+is_deeply([$b->slaves_wanted(20)],[6,8],'load table test 8');
+is($b->slave_instance_type,'m1.large','instance type');
+is($b->slave_spot_bid,'0.08','bid price');
+is($b->master_poll,30,'poll interval');
+like($b->master_ip,qr/^\d+\.\d+\.\d+\.\d+$/,'master ip');
+
+
+
+exit;
diff --git a/t/testdata/conf/aws_slave.conf b/t/testdata/conf/aws_slave.conf
new file mode 100644
index 0000000..7c2f744
--- /dev/null
+++ b/t/testdata/conf/aws_slave.conf
@@ -0,0 +1,23 @@
+[LOAD TABLE]
+#req/s  min max
+0.01    0   1
+0.5     0   2
+1.0     1   4
+5.0     3   6
+10.0    6   8 
+
+[MASTER]
+external_ip       =                # optional; will choose if needed
+poll_interval     = 0.5            # minutes between polling steps
+
+
+[SLAVE]
+instance_type     = m1.large
+spot_bid          = 0.08
+ports             = 8101           # can be several space-delimited port numbers
+region            = us-west-2      # needed only when run from a non-AWS computer
+image_id          = ami-ac6ee69c   # needed only when run from a non-AWS computer
+availability_zone =                # optional
+subnet            =                # optional
+security_group    =                # optional; will manage own security group if needed
+
diff --git a/t/testdata/conf/languages/POSIX.pm b/t/testdata/conf/languages/POSIX.pm
index 2a832bd..9754b47 100644
--- a/t/testdata/conf/languages/POSIX.pm
+++ b/t/testdata/conf/languages/POSIX.pm
@@ -168,6 +168,8 @@ END
 
    SELECT_SUBTRACKS   => 'showing %d/%d subtracks',
 
+   TRACK_ID   => 'Track ID=<i>%s</i>',
+
    EDIT       => 'Edit File...',
 
    DELETE     => 'Delete File',
@@ -490,6 +492,8 @@ END
 
  CONFIGURE_THIS_TRACK   => '<b>Configure this track</b>',
 
+ POP_OUT               => '<b>Pop out/in</b>',
+
  DOWNLOAD_THIS_TRACK   => '<b>Download this track</b>',
 
  ABOUT_THIS_TRACK   => '<b>About this track</b>',
diff --git a/t/testdata/data/volvox/dummy.fa b/t/testdata/data/volvox/dummy.fa
new file mode 100644
index 0000000..e69de29
diff --git a/t/testdata/data/volvox2/dummy.fa b/t/testdata/data/volvox2/dummy.fa
new file mode 100644
index 0000000..e69de29
diff --git a/t/testdata/data/volvox3/dummy.fa b/t/testdata/data/volvox3/dummy.fa
new file mode 100644
index 0000000..e69de29
diff --git a/t/testdata/data/volvox4/dummy.fa b/t/testdata/data/volvox4/dummy.fa
new file mode 100644
index 0000000..e69de29

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/gbrowse.git



More information about the debian-med-commit mailing list