galaxy-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions

08 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/2f2870f4f67d/
changeset: 2f2870f4f67d
user: greg
date: 2012-06-08 17:46:29
summary: Ehancements for automatic installation of tool dependencies when installing tool shed repositories; the enhancements support moving files and changing subdirectories during tool dependency installation.
affected #: 5 files
diff -r 2a9c71e1d2209016559b09964c8b5a6f9854a5d5 -r 2f2870f4f67d2198c69b44af688b5b37174459b0 lib/galaxy/tool_shed/tool_dependencies/common_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/common_util.py
+++ b/lib/galaxy/tool_shed/tool_dependencies/common_util.py
@@ -18,14 +18,16 @@
def iszip( file_path ):
return check_zip( file_path )
def tar_extraction_directory( file_path, file_name ):
+ file_name = file_name.strip()
extensions = [ '.tar.gz', '.tgz', '.tar.bz2', '.zip' ]
for extension in extensions:
- if file_name.endswith( extension ):
+ if file_name.find( extension ) > 0:
dir_name = file_name[ :-len( extension ) ]
- full_path = os.path.abspath( os.path.join( file_path, dir_name ) )
- if os.path.exists( full_path ):
+ if os.path.exists( os.path.abspath( os.path.join( file_path, dir_name ) ) ):
return dir_name
- raise ValueError( 'Could not find directory %s' % full_path )
+ if os.path.exists( os.path.abspath( os.path.join( file_path, file_name ) ) ):
+ return os.path.abspath( os.path.join( file_path, file_name ) )
+ raise ValueError( 'Could not find directory %s' % os.path.abspath( os.path.join( file_path, file_name[ :-len( extension ) ] ) ) )
def url_download( install_dir, downloaded_file_name, download_url ):
file_path = os.path.join( install_dir, downloaded_file_name )
src = None
diff -r 2a9c71e1d2209016559b09964c8b5a6f9854a5d5 -r 2f2870f4f67d2198c69b44af688b5b37174459b0 lib/galaxy/tool_shed/tool_dependencies/fabric_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/fabric_util.py
+++ b/lib/galaxy/tool_shed/tool_dependencies/fabric_util.py
@@ -1,7 +1,7 @@
# For Python 2.5
from __future__ import with_statement
-import os
+import os, shutil
from contextlib import contextmanager
import common_util
@@ -11,7 +11,11 @@
pkg_resources.require('ssh' )
pkg_resources.require( 'Fabric' )
-from fabric.api import env, lcd, local
+from fabric.api import env, lcd, local, settings
+
+DIRECTORY_BUILD_COMMAND_NAMES = [ 'change_directory' ]
+MOVE_BUILD_COMMAND_NAMES = [ 'move_directory_files', 'move_file' ]
+ALL_BUILD_COMMAND_NAMES = DIRECTORY_BUILD_COMMAND_NAMES + MOVE_BUILD_COMMAND_NAMES
def check_fabric_version():
version = env.version
@@ -83,11 +87,39 @@
dir = package_name
if build_commands:
with lcd( dir ):
- for build_command in build_commands:
- output = local( build_command, capture=True )
- log_results( build_command, output, os.path.join( install_dir, 'build_commands.log' ) )
- if output.return_code:
- return '%s. ' % str( output.stderr )
+ current_dir = os.path.abspath( os.path.join( work_dir, dir ) )
+ for build_command_tup in build_commands:
+ build_command_key, build_command_dict = build_command_tup
+ if build_command_key.find( 'v^v^v' ) >= 0:
+ build_command_items = build_command_key.split( 'v^v^v' )
+ build_command_name = build_command_items[ 0 ]
+ build_command = build_command_items[ 1 ]
+ elif build_command_key in ALL_BUILD_COMMAND_NAMES:
+ build_command_name = build_command_key
+ else:
+ build_command_name = None
+ if build_command_name:
+ if build_command_name == 'change_directory':
+ current_dir = os.path.join( current_dir, build_command )
+ lcd( current_dir )
+ elif build_command_name == 'move_directory_files':
+ source_directory = os.path.abspath( os.path.join( current_dir, build_command_dict[ 'source_directory' ] ) )
+ destination_directory = build_command_dict[ 'destination_directory' ]
+ for file_name in os.listdir( source_directory ):
+ source_file = os.path.join( source_directory, file_name )
+ destination_file = os.path.join( destination_directory, file_name )
+ shutil.move( source_file, destination_file )
+ elif build_command_name == 'move_file':
+ source_file = os.path.abspath( os.path.join( current_dir, build_command_dict[ 'source' ] ) )
+ destination = build_command_dict[ 'destination' ]
+ shutil.move( source_file, destination )
+ else:
+ build_command = build_command_key
+ with settings( warn_only=True ):
+ output = local( build_command, capture=True )
+ log_results( build_command, output, os.path.join( install_dir, 'build_commands.log' ) )
+ if output.return_code:
+ return '%s. ' % str( output.stderr )
return ''
def log_results( command, fabric_AttributeString, file_path ):
"""
@@ -98,12 +130,12 @@
logfile = open( file_path, 'ab' )
else:
logfile = open( file_path, 'wb' )
- logfile.write( "#############################################" )
+ logfile.write( "\n#############################################" )
logfile.write( '\n%s\nSTDOUT\n' % command )
- logfile.write( "#############################################" )
+ logfile.write( "#############################################\n" )
logfile.write( str( fabric_AttributeString.stdout ) )
- logfile.write( "#############################################" )
+ logfile.write( "\n#############################################" )
logfile.write( '\n%s\nSTDERR\n' % command )
- logfile.write( "#############################################" )
+ logfile.write( "#############################################\n" )
logfile.write( str( fabric_AttributeString.stderr ) )
logfile.close()
diff -r 2a9c71e1d2209016559b09964c8b5a6f9854a5d5 -r 2f2870f4f67d2198c69b44af688b5b37174459b0 lib/galaxy/tool_shed/tool_dependencies/install_util.py
--- a/lib/galaxy/tool_shed/tool_dependencies/install_util.py
+++ b/lib/galaxy/tool_shed/tool_dependencies/install_util.py
@@ -96,7 +96,24 @@
if param_name:
if param_name == 'build_commands':
for build_command_elem in param_elem:
- build_commands.append( build_command_elem.text.replace( '$INSTALL_DIR', install_dir ) )
+ build_command_dict = {}
+ build_command_name = build_command_elem.get( 'name' )
+ if build_command_name:
+ if build_command_name in MOVE_BUILD_COMMAND_NAMES:
+ build_command_key = build_command_name
+ for move_elem in build_command_elem:
+ move_elem_text = move_elem.text.replace( '$INSTALL_DIR', install_dir )
+ if move_elem_text:
+ build_command_dict[ move_elem.tag ] = move_elem_text
+ elif build_command_elem.text:
+ build_command_key = '%sv^v^v%s' % ( build_command_name, build_command_elem.text )
+ else:
+ continue
+ else:
+ build_command_key = build_command_elem.text.replace( '$INSTALL_DIR', install_dir )
+ if not build_command_key:
+ continue
+ build_commands.append( ( build_command_key, build_command_dict ) )
if build_commands:
params_dict[ 'build_commands' ] = build_commands
else:
@@ -122,7 +139,6 @@
return message
except:
return '%s. ' % str( e )
- print package_name, 'installed to', install_dir
return ''
def run_proprietary_fabric_method( app, elem, fabfile_path, tool_dependency_dir, install_dir, package_name=None, **kwd ):
"""
@@ -161,7 +177,6 @@
if message:
return message
else:
- print package_name, 'installed to', install_dir
return ''
def run_subprocess( app, cmd ):
env = os.environ
diff -r 2a9c71e1d2209016559b09964c8b5a6f9854a5d5 -r 2f2870f4f67d2198c69b44af688b5b37174459b0 lib/galaxy/util/shed_util.py
--- a/lib/galaxy/util/shed_util.py
+++ b/lib/galaxy/util/shed_util.py
@@ -485,7 +485,7 @@
requirements_dict[ key ] = fabfiles_dict
return requirements_dict
def generate_metadata_using_disk_files( toolbox, relative_install_dir, repository_clone_url ):
- """generate metadata using only the repository files on disk - files are not retrieved from the repository manifest."""
+ """Generate metadata using only the repository files on disk - files are not retrieved from the repository manifest."""
metadata_dict = {}
tool_dependencies_config = None
datatypes_config = get_config_from_disk( 'datatypes_conf.xml', relative_install_dir )
@@ -1081,9 +1081,8 @@
"""
Install and build tool dependencies defined in the tool_dependencies_config. This config's tag sets can refer to installation
methods in Galaxy's tool_dependencies module or to proprietary fabric scripts contained in the repository. Future enhancements
- to handling tool dependencies may provide installation processes in addition to fabric based processes.
-
- The dependencies will be installed in:
+ to handling tool dependencies may provide installation processes in addition to fabric based processes. The dependencies will be
+ installed in:
~/<app.config.tool_dependency_dir>/<package_name>/<package_version>/<repository_owner>/<repository_name>/<installed_changeset_revision>
"""
status = 'ok'
@@ -1192,10 +1191,6 @@
tool_path,
repository_tools_tups,
work_dir )
- try:
- shutil.rmtree( work_dir )
- except:
- pass
# Handle missing index files for tool parameters that are dynamically generated select lists.
sample_files = metadata_dict.get( 'sample_files', [] )
repository_tools_tups, sample_files_copied = handle_missing_index_file( trans.app, tool_path, sample_files, repository_tools_tups )
@@ -1287,7 +1282,7 @@
commands.pull( get_configured_ui(),
repo,
source=repository_clone_url,
- rev=ctx_rev )
+ rev=[ ctx_rev ] )
def remove_from_shed_tool_config( trans, shed_tool_conf_dict, guids_to_remove ):
# A tool shed repository is being uninstalled so change the shed_tool_conf file. Parse the config file to generate the entire list
# of config_elems instead of using the in-memory list since it will be a subset of the entire list if one or more repositories have
diff -r 2a9c71e1d2209016559b09964c8b5a6f9854a5d5 -r 2f2870f4f67d2198c69b44af688b5b37174459b0 lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -1002,13 +1002,11 @@
metadata_changeset_revision = ancestor_changeset_revision
metadata_dict = ancestor_metadata_dict
create_or_update_repository_metadata( trans, id, repository, metadata_changeset_revision, metadata_dict )
- # Keep track of the changeset_revisions that we've persisted.
changeset_revisions.append( metadata_changeset_revision )
ancestor_changeset_revision = current_changeset_revision
ancestor_metadata_dict = current_metadata_dict
else:
- # We're either at the first change set in the change log or we have just created or updated a repository_metadata record. At
- # this point we set the ancestor changeset to the current changeset for comparison in the next iteration.
+ # We're at the beginning of the change log.
ancestor_changeset_revision = current_changeset_revision
ancestor_metadata_dict = current_metadata_dict
if not ctx.children():
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/c99613c96a70/
changeset: c99613c96a70
user: fubar
date: 2012-06-08 00:26:09
summary: Updates to fastqc tool to wrap the user supplied job name in quotes and sanitize to letters/digits only
Some downstream problems from ( characters reported today but probably needs deeper sanitizing
affected #: 1 file
diff -r 37352829fedd8ef5df912281b91471302cd6e5dd -r c99613c96a707a57cbbbed8e0f9968dede6f518b tools/rgenetics/rgFastQC.xml
--- a/tools/rgenetics/rgFastQC.xml
+++ b/tools/rgenetics/rgFastQC.xml
@@ -1,4 +1,4 @@
-<tool name="Fastqc: Fastqc QC" id="fastqc" version="0.4">
+<tool name="Fastqc: Fastqc QC" id="fastqc" version="0.5"><description>using FastQC from Babraham</description><command interpreter="python">
rgFastQC.py -i $input_file -d $html_file.files_path -o $html_file -n "$out_prefix" -f $input_file.ext -j $input_file.name -e ${GALAXY_DATA_INDEX_DIR}/shared/jars/FastQC/fastqc
@@ -11,7 +11,12 @@
</requirements><inputs><param format="fastqsanger,fastq,bam,sam" name="input_file" type="data" label="Short read data from your current history" />
- <param name="out_prefix" value="FastQC" type="text" label="Title for the output file - to remind you what the job was for" size="80" />
+ <param name="out_prefix" value="FastQC" type="text" label="Title for the output file - to remind you what the job was for" size="80"
+ help="Letters and numbers only please - other characters will be removed">
+ <sanitizer invalid_char="">
+ <valid initial="string.letters,string.digits"/>
+ </sanitizer>
+ </param><param name="contaminants" type="data" format="tabular" optional="true" label="Contaminant list"
help="tab delimited file with 2 columns: name and sequence. For example: Illumina Small RNA RT Primer CAAGCAGAAGACGGCATACGA"/></inputs>
https://bitbucket.org/galaxy/galaxy-central/changeset/fb28365f5daa/
changeset: fb28365f5daa
user: fubar
date: 2012-06-08 00:28:35
summary: Add some more missing quotes to command line.
affected #: 1 file
diff -r c99613c96a707a57cbbbed8e0f9968dede6f518b -r fb28365f5daa4cb74d3043b98a64c015bfbe4e12 tools/rgenetics/rgFastQC.xml
--- a/tools/rgenetics/rgFastQC.xml
+++ b/tools/rgenetics/rgFastQC.xml
@@ -1,7 +1,7 @@
<tool name="Fastqc: Fastqc QC" id="fastqc" version="0.5"><description>using FastQC from Babraham</description><command interpreter="python">
- rgFastQC.py -i $input_file -d $html_file.files_path -o $html_file -n "$out_prefix" -f $input_file.ext -j $input_file.name -e ${GALAXY_DATA_INDEX_DIR}/shared/jars/FastQC/fastqc
+ rgFastQC.py -i "$input_file" -d "$html_file.files_path" -o "$html_file" -n "$out_prefix" -f "$input_file.ext" -j "$input_file.name" -e "${GALAXY_DATA_INDEX_DIR}/shared/jars/FastQC/fastqc"
#if $contaminants.dataset and str($contaminants) > ''
-c "$contaminants"
#end if
https://bitbucket.org/galaxy/galaxy-central/changeset/2a9c71e1d220/
changeset: 2a9c71e1d220
user: fubar
date: 2012-06-08 00:31:56
summary: branch merge
affected #: 1 file
diff -r fb28365f5daa4cb74d3043b98a64c015bfbe4e12 -r 2a9c71e1d2209016559b09964c8b5a6f9854a5d5 static/scripts/viz/trackster.js
--- a/static/scripts/viz/trackster.js
+++ b/static/scripts/viz/trackster.js
@@ -2794,12 +2794,11 @@
/**
* Tiles drawn by tracks.
*/
-var Tile = function(track, index, resolution, canvas, data) {
+var Tile = function(track, region, resolution, canvas, data) {
this.track = track;
- this.index = index;
- var tile_bounds = this.track._get_tile_bounds(index, resolution);
- this.low = tile_bounds[0];
- this.high = tile_bounds[1];
+ this.region = region;
+ this.low = region.get('start');
+ this.high = region.get('end');
this.resolution = resolution;
// Wrap element in div for background and explicitly set height. Use canvas
// height attribute because canvas may not have height if it is not in document yet.
@@ -2813,15 +2812,15 @@
*/
Tile.prototype.predisplay_actions = function() {};
-var SummaryTreeTile = function(track, index, resolution, canvas, data, max_val) {
- Tile.call(this, track, index, resolution, canvas, data);
+var SummaryTreeTile = function(track, region, resolution, canvas, data, max_val) {
+ Tile.call(this, track, region, resolution, canvas, data);
this.max_val = max_val;
};
extend(SummaryTreeTile.prototype, Tile.prototype);
-var FeatureTrackTile = function(track, index, resolution, canvas, data, w_scale, mode, message, all_slotted, feature_mapper) {
+var FeatureTrackTile = function(track, region, resolution, canvas, data, w_scale, mode, message, all_slotted, feature_mapper) {
// Attribute init.
- Tile.call(this, track, index, resolution, canvas, data);
+ Tile.call(this, track, region, resolution, canvas, data);
this.mode = mode;
this.all_slotted = all_slotted;
this.feature_mapper = feature_mapper;
@@ -3586,12 +3585,7 @@
draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, kwargs) {
var track = this,
key = this._gen_tile_cache_key(width, w_scale, tile_index),
- tile_bounds = this._get_tile_bounds(tile_index, resolution),
- region = new GenomeRegion({
- chrom: this.view.chrom,
- start: tile_bounds[0],
- end: tile_bounds[1]
- });
+ region = this._get_tile_bounds(tile_index, resolution);
// Init kwargs if necessary to avoid having to check if kwargs defined.
if (!kwargs) { kwargs = {}; }
@@ -3646,7 +3640,7 @@
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.translate(this.left_offset, 0);
- var tile = track.draw_tile(tile_data, ctx, mode, resolution, tile_index, w_scale, seq_data);
+ var tile = track.draw_tile(tile_data, ctx, mode, resolution, region, w_scale, seq_data);
// Don't cache, show if no tile.
if (tile !== undefined) {
@@ -3679,11 +3673,11 @@
* @param ctx canvas context to draw on
* @param mode mode to draw in
* @param resolution view resolution
- * @param tile_index index of tile to be drawn
+ * @param region region to draw on tile
* @param w_scale pixels per base
* @param ref_seq reference sequence data
*/
- draw_tile: function(result, ctx, mode, resolution, tile_index, w_scale, ref_seq) {
+ draw_tile: function(result, ctx, mode, resolution, region, w_scale, ref_seq) {
console.log("Warning: TiledTrack.draw_tile() not implemented.");
},
/**
@@ -3720,21 +3714,27 @@
track.after_show_tile(tile);
},
+
/**
* Actions to be taken after showing tile.
*/
after_show_tile: function(tile) {},
+
/**
- * Returns tile bounds--tile low and tile high--based on a tile index. Return value is an array
- * with values tile_low and tile_high.
+ * Returns a genome region that corresponds to a tile at a particular resolution
*/
_get_tile_bounds: function(tile_index, resolution) {
var tile_low = Math.floor( tile_index * TILE_SIZE * resolution ),
tile_length = Math.ceil( TILE_SIZE * resolution ),
// Tile high cannot be larger than view.max_high, which the chromosome length.
tile_high = (tile_low + tile_length <= this.view.max_high ? tile_low + tile_length : this.view.max_high);
- return [tile_low, tile_high];
+ return new GenomeRegion({
+ chrom: this.view.chrom,
+ start: tile_low,
+ end: tile_high
+ });
},
+
/**
* Utility function that creates a label string describing the region and parameters of a track's tool.
*/
@@ -3930,8 +3930,8 @@
var track = this,
key = this._gen_tile_cache_key(width, w_scale, tile_index),
tile_bounds = this._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1];
+ tile_low = tile_bounds.get('start'),
+ tile_high = tile_bounds.get('end');
// Init kwargs if necessary to avoid having to check if kwargs defined.
if (!kwargs) { kwargs = {}; }
@@ -3980,8 +3980,8 @@
var
canvas = track.view.canvas_manager.new_canvas(),
tile_bounds = track._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1],
+ tile_low = tile_bounds.get('start'),
+ tile_high = tile_bounds.get('end'),
all_data_index = 0,
width = Math.ceil( (tile_high - tile_low) * w_scale ) + this.left_offset,
height = 0,
@@ -4021,7 +4021,7 @@
track = this.drawables[i];
tile_data = all_data[ all_data_index ];
seq_data = all_data[ all_data_index + 1 ];
- tile = track.draw_tile(tile_data, ctx, track_modes[i], resolution, tile_index, w_scale, seq_data);
+ tile = track.draw_tile(tile_data, ctx, track_modes[i], resolution, tile_bounds, w_scale, seq_data);
}
// Don't cache, show if no tile.
@@ -4148,7 +4148,7 @@
/**
* Draw ReferenceTrack tile.
*/
- draw_tile: function(seq, ctx, mode, resolution, tile_index, w_scale) {
+ draw_tile: function(seq, ctx, mode, resolution, region, w_scale) {
var track = this;
if (w_scale > this.view.canvas_manager.char_width_px) {
@@ -4165,7 +4165,7 @@
ctx.fillText(seq[c], c_start, 10);
}
this.show_contents();
- return new Tile(track, tile_index, resolution, canvas, seq);
+ return new Tile(track, region, resolution, canvas, seq);
}
this.hide_contents();
}
@@ -4294,17 +4294,16 @@
/**
* Draw LineTrack tile.
*/
- draw_tile: function(result, ctx, mode, resolution, tile_index, w_scale) {
+ draw_tile: function(result, ctx, mode, resolution, region, w_scale) {
// Paint onto canvas.
var
canvas = ctx.canvas,
- tile_bounds = this._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1],
+ tile_low = region.get('start'),
+ tile_high = region.get('end'),
painter = new painters.LinePainter(result.data, tile_low, tile_high, this.prefs, mode);
painter.draw(ctx, canvas.width, canvas.height, w_scale);
- return new Tile(this, tile_index, resolution, canvas, result.data);
+ return new Tile(this, region, resolution, canvas, result.data);
},
/**
* LineTrack data cannot currently be subsetted.
@@ -4682,16 +4681,15 @@
* @param cxt canvas context to draw on
* @param mode mode to draw in
* @param resolution view resolution
- * @param tile_index index of tile to be drawn
+ * @param region region to draw on tile
* @param w_scale pixels per base
* @param ref_seq reference sequence data
*/
- draw_tile: function(result, ctx, mode, resolution, tile_index, w_scale, ref_seq) {
+ draw_tile: function(result, ctx, mode, resolution, region, w_scale, ref_seq) {
var track = this,
canvas = ctx.canvas,
- tile_bounds = this._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1],
+ tile_low = region.get('start'),
+ tile_high = region.get('end'),
min_height = 25,
left_offset = this.left_offset;
@@ -4761,7 +4759,7 @@
feature_mapper.translation = -left_offset;
}
- return new FeatureTrackTile(track, tile_index, resolution, canvas, result.data, w_scale, mode, result.message, all_slotted, feature_mapper);
+ return new FeatureTrackTile(track, region, resolution, canvas, result.data, w_scale, mode, result.message, all_slotted, feature_mapper);
},
/**
* Returns true if data is compatible with a given mode.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: jgoecks: Draw tiles based on region rather than tile index because it provides more flexibility.
by Bitbucket 07 Jun '12
by Bitbucket 07 Jun '12
07 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/850e63cc9500/
changeset: 850e63cc9500
user: jgoecks
date: 2012-06-07 23:43:22
summary: Draw tiles based on region rather than tile index because it provides more flexibility.
affected #: 1 file
diff -r 37352829fedd8ef5df912281b91471302cd6e5dd -r 850e63cc9500b4d4b66cfe41ba64fde4ce53b489 static/scripts/viz/trackster.js
--- a/static/scripts/viz/trackster.js
+++ b/static/scripts/viz/trackster.js
@@ -2794,12 +2794,11 @@
/**
* Tiles drawn by tracks.
*/
-var Tile = function(track, index, resolution, canvas, data) {
+var Tile = function(track, region, resolution, canvas, data) {
this.track = track;
- this.index = index;
- var tile_bounds = this.track._get_tile_bounds(index, resolution);
- this.low = tile_bounds[0];
- this.high = tile_bounds[1];
+ this.region = region;
+ this.low = region.get('start');
+ this.high = region.get('end');
this.resolution = resolution;
// Wrap element in div for background and explicitly set height. Use canvas
// height attribute because canvas may not have height if it is not in document yet.
@@ -2813,15 +2812,15 @@
*/
Tile.prototype.predisplay_actions = function() {};
-var SummaryTreeTile = function(track, index, resolution, canvas, data, max_val) {
- Tile.call(this, track, index, resolution, canvas, data);
+var SummaryTreeTile = function(track, region, resolution, canvas, data, max_val) {
+ Tile.call(this, track, region, resolution, canvas, data);
this.max_val = max_val;
};
extend(SummaryTreeTile.prototype, Tile.prototype);
-var FeatureTrackTile = function(track, index, resolution, canvas, data, w_scale, mode, message, all_slotted, feature_mapper) {
+var FeatureTrackTile = function(track, region, resolution, canvas, data, w_scale, mode, message, all_slotted, feature_mapper) {
// Attribute init.
- Tile.call(this, track, index, resolution, canvas, data);
+ Tile.call(this, track, region, resolution, canvas, data);
this.mode = mode;
this.all_slotted = all_slotted;
this.feature_mapper = feature_mapper;
@@ -3586,12 +3585,7 @@
draw_helper: function(force, width, tile_index, resolution, parent_element, w_scale, kwargs) {
var track = this,
key = this._gen_tile_cache_key(width, w_scale, tile_index),
- tile_bounds = this._get_tile_bounds(tile_index, resolution),
- region = new GenomeRegion({
- chrom: this.view.chrom,
- start: tile_bounds[0],
- end: tile_bounds[1]
- });
+ region = this._get_tile_bounds(tile_index, resolution);
// Init kwargs if necessary to avoid having to check if kwargs defined.
if (!kwargs) { kwargs = {}; }
@@ -3646,7 +3640,7 @@
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.translate(this.left_offset, 0);
- var tile = track.draw_tile(tile_data, ctx, mode, resolution, tile_index, w_scale, seq_data);
+ var tile = track.draw_tile(tile_data, ctx, mode, resolution, region, w_scale, seq_data);
// Don't cache, show if no tile.
if (tile !== undefined) {
@@ -3679,11 +3673,11 @@
* @param ctx canvas context to draw on
* @param mode mode to draw in
* @param resolution view resolution
- * @param tile_index index of tile to be drawn
+ * @param region region to draw on tile
* @param w_scale pixels per base
* @param ref_seq reference sequence data
*/
- draw_tile: function(result, ctx, mode, resolution, tile_index, w_scale, ref_seq) {
+ draw_tile: function(result, ctx, mode, resolution, region, w_scale, ref_seq) {
console.log("Warning: TiledTrack.draw_tile() not implemented.");
},
/**
@@ -3720,21 +3714,27 @@
track.after_show_tile(tile);
},
+
/**
* Actions to be taken after showing tile.
*/
after_show_tile: function(tile) {},
+
/**
- * Returns tile bounds--tile low and tile high--based on a tile index. Return value is an array
- * with values tile_low and tile_high.
+ * Returns a genome region that corresponds to a tile at a particular resolution
*/
_get_tile_bounds: function(tile_index, resolution) {
var tile_low = Math.floor( tile_index * TILE_SIZE * resolution ),
tile_length = Math.ceil( TILE_SIZE * resolution ),
// Tile high cannot be larger than view.max_high, which the chromosome length.
tile_high = (tile_low + tile_length <= this.view.max_high ? tile_low + tile_length : this.view.max_high);
- return [tile_low, tile_high];
+ return new GenomeRegion({
+ chrom: this.view.chrom,
+ start: tile_low,
+ end: tile_high
+ });
},
+
/**
* Utility function that creates a label string describing the region and parameters of a track's tool.
*/
@@ -3930,8 +3930,8 @@
var track = this,
key = this._gen_tile_cache_key(width, w_scale, tile_index),
tile_bounds = this._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1];
+ tile_low = tile_bounds.get('start'),
+ tile_high = tile_bounds.get('end');
// Init kwargs if necessary to avoid having to check if kwargs defined.
if (!kwargs) { kwargs = {}; }
@@ -3980,8 +3980,8 @@
var
canvas = track.view.canvas_manager.new_canvas(),
tile_bounds = track._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1],
+ tile_low = tile_bounds.get('start'),
+ tile_high = tile_bounds.get('end'),
all_data_index = 0,
width = Math.ceil( (tile_high - tile_low) * w_scale ) + this.left_offset,
height = 0,
@@ -4021,7 +4021,7 @@
track = this.drawables[i];
tile_data = all_data[ all_data_index ];
seq_data = all_data[ all_data_index + 1 ];
- tile = track.draw_tile(tile_data, ctx, track_modes[i], resolution, tile_index, w_scale, seq_data);
+ tile = track.draw_tile(tile_data, ctx, track_modes[i], resolution, tile_bounds, w_scale, seq_data);
}
// Don't cache, show if no tile.
@@ -4148,7 +4148,7 @@
/**
* Draw ReferenceTrack tile.
*/
- draw_tile: function(seq, ctx, mode, resolution, tile_index, w_scale) {
+ draw_tile: function(seq, ctx, mode, resolution, region, w_scale) {
var track = this;
if (w_scale > this.view.canvas_manager.char_width_px) {
@@ -4165,7 +4165,7 @@
ctx.fillText(seq[c], c_start, 10);
}
this.show_contents();
- return new Tile(track, tile_index, resolution, canvas, seq);
+ return new Tile(track, region, resolution, canvas, seq);
}
this.hide_contents();
}
@@ -4294,17 +4294,16 @@
/**
* Draw LineTrack tile.
*/
- draw_tile: function(result, ctx, mode, resolution, tile_index, w_scale) {
+ draw_tile: function(result, ctx, mode, resolution, region, w_scale) {
// Paint onto canvas.
var
canvas = ctx.canvas,
- tile_bounds = this._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1],
+ tile_low = region.get('start'),
+ tile_high = region.get('end'),
painter = new painters.LinePainter(result.data, tile_low, tile_high, this.prefs, mode);
painter.draw(ctx, canvas.width, canvas.height, w_scale);
- return new Tile(this, tile_index, resolution, canvas, result.data);
+ return new Tile(this, region, resolution, canvas, result.data);
},
/**
* LineTrack data cannot currently be subsetted.
@@ -4682,16 +4681,15 @@
* @param cxt canvas context to draw on
* @param mode mode to draw in
* @param resolution view resolution
- * @param tile_index index of tile to be drawn
+ * @param region region to draw on tile
* @param w_scale pixels per base
* @param ref_seq reference sequence data
*/
- draw_tile: function(result, ctx, mode, resolution, tile_index, w_scale, ref_seq) {
+ draw_tile: function(result, ctx, mode, resolution, region, w_scale, ref_seq) {
var track = this,
canvas = ctx.canvas,
- tile_bounds = this._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1],
+ tile_low = region.get('start'),
+ tile_high = region.get('end'),
min_height = 25,
left_offset = this.left_offset;
@@ -4761,7 +4759,7 @@
feature_mapper.translation = -left_offset;
}
- return new FeatureTrackTile(track, tile_index, resolution, canvas, result.data, w_scale, mode, result.message, all_slotted, feature_mapper);
+ return new FeatureTrackTile(track, region, resolution, canvas, result.data, w_scale, mode, result.message, all_slotted, feature_mapper);
},
/**
* Returns true if data is compatible with a given mode.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: jgoecks: Visualization infrastructure: (a) Backbone-ify cache and data managers; (b) push parameter value sampling code into Tool objects. Pack scripts.
by Bitbucket 07 Jun '12
by Bitbucket 07 Jun '12
07 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/37352829fedd/
changeset: 37352829fedd
user: jgoecks
date: 2012-06-07 22:47:06
summary: Visualization infrastructure: (a) Backbone-ify cache and data managers; (b) push parameter value sampling code into Tool objects. Pack scripts.
affected #: 10 files
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd static/scripts/mvc/tools.js
--- a/static/scripts/mvc/tools.js
+++ b/static/scripts/mvc/tools.js
@@ -63,12 +63,47 @@
* A tool input.
*/
var ToolInput = Backbone.RelationalModel.extend({
+ defaults: {
+ name: null,
+ type: null
+ },
initialize: function() {
this.attributes.html = unescape(this.attributes.html);
+ },
+
+ /**
+ * Returns samples from a tool input.
+ */
+ get_samples: function(num_samples) {
+ var
+ type = this.get('type'),
+ samples = []
+ if (type === 'number') {
+ samples = d3.scale.linear().domain([this.get('min'), this.get('max')]).ticks(num_samples);
+ }
+ else if (type === 'select') {
+ samples = _.map(this.get('options'), function(option) {
+ return option[0];
+ });
+ }
+
+ return new ParamSamples({
+ param: this,
+ samples: samples
+ });
+ }
+});
+
+/**
+ * A tool and parameter samples.
+ */
+var ParamSamples = Backbone.Model.extend({
+ defaults: {
+ param: null,
+ samples: null
}
-
-});
+})
/**
* Wrap collection of tools for fast access/manipulation.
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd static/scripts/packed/mvc/tools.js
--- a/static/scripts/packed/mvc/tools.js
+++ b/static/scripts/packed/mvc/tools.js
@@ -1,1 +1,1 @@
-var BaseModel=Backbone.RelationalModel.extend({defaults:{name:null,hidden:false},show:function(){this.set("hidden",false)},hide:function(){this.set("hidden",true)},is_visible:function(){return !this.attributes.hidden}});var Tool=BaseModel.extend({defaults:{description:null,target:null,inputs:[]},relations:[{type:Backbone.HasMany,key:"inputs",relatedModel:"ToolInput",reverseRelation:{key:"tool"}}],urlRoot:galaxy_paths.attributes.root_path+"api/tools",apply_search_results:function(a){(_.indexOf(a,this.attributes.id)!==-1?this.show():this.hide());return this.is_visible()}});var ToolInput=Backbone.RelationalModel.extend({initialize:function(){this.attributes.html=unescape(this.attributes.html)}});var ToolCollection=Backbone.Collection.extend({model:Tool});var ToolPanelLabel=BaseModel.extend({});var ToolPanelSection=BaseModel.extend({defaults:{elems:[],open:false},clear_search_results:function(){_.each(this.attributes.elems,function(a){a.show()});this.show();this.set("open",false)},apply_search_results:function(b){var c=true,a;_.each(this.attributes.elems,function(d){if(d instanceof ToolPanelLabel){a=d;a.hide()}else{if(d instanceof Tool){if(d.apply_search_results(b)){c=false;if(a){a.show()}}}}});if(c){this.hide()}else{this.show();this.set("open",true)}}});var ToolSearch=BaseModel.extend({defaults:{spinner_url:"",search_url:"",visible:true,query:"",results:null},initialize:function(){this.on("change:query",this.do_search)},do_search:function(){var c=this.attributes.query;if(c.length<3){this.set("results",null);return}var b=c+"*";if(this.timer){clearTimeout(this.timer)}$("#search-spinner").show();var a=this;this.timer=setTimeout(function(){$.get(a.attributes.search_url,{query:b},function(d){a.set("results",d);$("#search-spinner").hide()},"json")},200)}});var ToolPanel=Backbone.Collection.extend({url:"/tools",tools:new ToolCollection(),parse:function(a){var b=function(e){var d=e.type;if(d==="tool"){return new Tool(e)}else{if(d==="section"){var c=_.map(e.elems,b);e.elems=c;return new ToolPanelSection(e)}else{if(d==="label"){return new ToolPanelLabel(e)}}}};return _.map(a,b)},initialize:function(a){this.tool_search=a.tool_search;this.tool_search.on("change:results",this.apply_search_results,this);this.on("reset",this.populate_tools,this)},populate_tools:function(){var a=this;a.tools=new ToolCollection();this.each(function(b){if(b instanceof ToolPanelSection){_.each(b.attributes.elems,function(c){if(c instanceof Tool){a.tools.push(c)}})}else{if(b instanceof Tool){a.tools.push(b)}}})},clear_search_results:function(){this.each(function(a){if(a instanceof ToolPanelSection){a.clear_search_results()}else{a.show()}})},apply_search_results:function(){var b=this.tool_search.attributes.results;if(b===null){this.clear_search_results();return}var a=null;this.each(function(c){if(c instanceof ToolPanelLabel){a=c;a.hide()}else{if(c instanceof Tool){if(c.apply_search_results(b)){if(a){a.show()}}}else{a=null;c.apply_search_results(b)}}})}});var BaseView=Backbone.View.extend({initialize:function(){this.model.on("change:hidden",this.update_visible,this);this.update_visible()},update_visible:function(){(this.model.attributes.hidden?this.$el.hide():this.$el.show())}});var ToolLinkView=BaseView.extend({tagName:"div",template:Handlebars.templates.tool_link,render:function(){this.$el.append(this.template(this.model.toJSON()));return this}});var ToolPanelLabelView=BaseView.extend({tagName:"div",className:"toolPanelLabel",render:function(){this.$el.append($("<span/>").text(this.model.attributes.name));return this}});var ToolPanelSectionView=BaseView.extend({tagName:"div",className:"toolSectionWrapper",template:Handlebars.templates.panel_section,initialize:function(){BaseView.prototype.initialize.call(this);this.model.on("change:open",this.update_open,this)},render:function(){this.$el.append(this.template(this.model.toJSON()));var a=this.$el.find(".toolSectionBody");_.each(this.model.attributes.elems,function(b){if(b instanceof Tool){var c=new ToolLinkView({model:b,className:"toolTitle"});c.render();a.append(c.$el)}else{if(b instanceof ToolPanelLabel){var d=new ToolPanelLabelView({model:b});d.render();a.append(d.$el)}else{}}});return this},events:{"click .toolSectionTitle > a":"toggle"},toggle:function(){this.model.set("open",!this.model.attributes.open)},update_open:function(){(this.model.attributes.open?this.$el.children(".toolSectionBody").slideDown("fast"):this.$el.children(".toolSectionBody").slideUp("fast"))}});var ToolSearchView=Backbone.View.extend({tagName:"div",id:"tool-search",className:"bar",template:Handlebars.templates.tool_search,events:{click:"focus_and_select","keyup :input":"query_changed"},render:function(){this.$el.append(this.template(this.model.toJSON()));if(!this.model.is_visible()){this.$el.hide()}return this},focus_and_select:function(){this.$el.find(":input").focus().select()},query_changed:function(){this.model.set("query",this.$el.find(":input").val())}});var ToolPanelView=Backbone.View.extend({tagName:"div",className:"toolMenu",initialize:function(){this.collection.tool_search.on("change:results",this.handle_search_results,this)},render:function(){var a=this;var b=new ToolSearchView({model:this.collection.tool_search});b.render();a.$el.append(b.$el);this.collection.each(function(d){if(d instanceof ToolPanelSection){var c=new ToolPanelSectionView({model:d});c.render();a.$el.append(c.$el)}else{if(d instanceof Tool){var e=new ToolLinkView({model:d,className:"toolTitleNoSection"});e.render();a.$el.append(e.$el)}else{if(d instanceof ToolPanelLabel){var f=new ToolPanelLabelView({model:d});f.render();a.$el.append(f.$el)}}}});a.$el.find("a.tool-link").click(function(f){var d=$(this).attr("class").split(/\s+/)[0],c=a.collection.tools.get(d);a.trigger("tool_link_click",f,c)});return this},handle_search_results:function(){var a=this.collection.tool_search.attributes.results;if(a&&a.length===0){$("#search-no-results").show()}else{$("#search-no-results").hide()}}});var ToolFormView=Backbone.View.extend({className:"toolForm",template:Handlebars.templates.tool_form,render:function(){this.$el.children().remove();this.$el.append(this.template(this.model.toJSON()))}});var IntegratedToolMenuAndView=Backbone.View.extend({className:"toolMenuAndView",initialize:function(){this.tool_panel_view=new ToolPanelView({collection:this.collection});this.tool_form_view=new ToolFormView()},render:function(){this.tool_panel_view.render();this.tool_panel_view.$el.css("float","left");this.$el.append(this.tool_panel_view.$el);this.tool_form_view.$el.hide();this.$el.append(this.tool_form_view.$el);var a=this;this.tool_panel_view.on("tool_link_click",function(c,b){c.preventDefault();a.show_tool(b)})},show_tool:function(b){var a=this;b.fetch().done(function(){a.tool_form_view.model=b;a.tool_form_view.render();a.tool_form_view.$el.show();$("#left").width("650px")})}});
\ No newline at end of file
+var BaseModel=Backbone.RelationalModel.extend({defaults:{name:null,hidden:false},show:function(){this.set("hidden",false)},hide:function(){this.set("hidden",true)},is_visible:function(){return !this.attributes.hidden}});var Tool=BaseModel.extend({defaults:{description:null,target:null,inputs:[]},relations:[{type:Backbone.HasMany,key:"inputs",relatedModel:"ToolInput",reverseRelation:{key:"tool"}}],urlRoot:galaxy_paths.attributes.root_path+"api/tools",apply_search_results:function(a){(_.indexOf(a,this.attributes.id)!==-1?this.show():this.hide());return this.is_visible()}});var ToolInput=Backbone.RelationalModel.extend({defaults:{name:null,type:null},initialize:function(){this.attributes.html=unescape(this.attributes.html)},get_samples:function(b){var c=this.get("type"),a=[];if(c==="number"){a=d3.scale.linear().domain([this.get("min"),this.get("max")]).ticks(b)}else{if(c==="select"){a=_.map(this.get("options"),function(d){return d[0]})}}return new ParamSamples({param:this,samples:a})}});var ParamSamples=Backbone.Model.extend({defaults:{param:null,samples:null}});var ToolCollection=Backbone.Collection.extend({model:Tool});var ToolPanelLabel=BaseModel.extend({});var ToolPanelSection=BaseModel.extend({defaults:{elems:[],open:false},clear_search_results:function(){_.each(this.attributes.elems,function(a){a.show()});this.show();this.set("open",false)},apply_search_results:function(b){var c=true,a;_.each(this.attributes.elems,function(d){if(d instanceof ToolPanelLabel){a=d;a.hide()}else{if(d instanceof Tool){if(d.apply_search_results(b)){c=false;if(a){a.show()}}}}});if(c){this.hide()}else{this.show();this.set("open",true)}}});var ToolSearch=BaseModel.extend({defaults:{spinner_url:"",search_url:"",visible:true,query:"",results:null},initialize:function(){this.on("change:query",this.do_search)},do_search:function(){var c=this.attributes.query;if(c.length<3){this.set("results",null);return}var b=c+"*";if(this.timer){clearTimeout(this.timer)}$("#search-spinner").show();var a=this;this.timer=setTimeout(function(){$.get(a.attributes.search_url,{query:b},function(d){a.set("results",d);$("#search-spinner").hide()},"json")},200)}});var ToolPanel=Backbone.Collection.extend({url:"/tools",tools:new ToolCollection(),parse:function(a){var b=function(e){var d=e.type;if(d==="tool"){return new Tool(e)}else{if(d==="section"){var c=_.map(e.elems,b);e.elems=c;return new ToolPanelSection(e)}else{if(d==="label"){return new ToolPanelLabel(e)}}}};return _.map(a,b)},initialize:function(a){this.tool_search=a.tool_search;this.tool_search.on("change:results",this.apply_search_results,this);this.on("reset",this.populate_tools,this)},populate_tools:function(){var a=this;a.tools=new ToolCollection();this.each(function(b){if(b instanceof ToolPanelSection){_.each(b.attributes.elems,function(c){if(c instanceof Tool){a.tools.push(c)}})}else{if(b instanceof Tool){a.tools.push(b)}}})},clear_search_results:function(){this.each(function(a){if(a instanceof ToolPanelSection){a.clear_search_results()}else{a.show()}})},apply_search_results:function(){var b=this.tool_search.attributes.results;if(b===null){this.clear_search_results();return}var a=null;this.each(function(c){if(c instanceof ToolPanelLabel){a=c;a.hide()}else{if(c instanceof Tool){if(c.apply_search_results(b)){if(a){a.show()}}}else{a=null;c.apply_search_results(b)}}})}});var BaseView=Backbone.View.extend({initialize:function(){this.model.on("change:hidden",this.update_visible,this);this.update_visible()},update_visible:function(){(this.model.attributes.hidden?this.$el.hide():this.$el.show())}});var ToolLinkView=BaseView.extend({tagName:"div",template:Handlebars.templates.tool_link,render:function(){this.$el.append(this.template(this.model.toJSON()));return this}});var ToolPanelLabelView=BaseView.extend({tagName:"div",className:"toolPanelLabel",render:function(){this.$el.append($("<span/>").text(this.model.attributes.name));return this}});var ToolPanelSectionView=BaseView.extend({tagName:"div",className:"toolSectionWrapper",template:Handlebars.templates.panel_section,initialize:function(){BaseView.prototype.initialize.call(this);this.model.on("change:open",this.update_open,this)},render:function(){this.$el.append(this.template(this.model.toJSON()));var a=this.$el.find(".toolSectionBody");_.each(this.model.attributes.elems,function(b){if(b instanceof Tool){var c=new ToolLinkView({model:b,className:"toolTitle"});c.render();a.append(c.$el)}else{if(b instanceof ToolPanelLabel){var d=new ToolPanelLabelView({model:b});d.render();a.append(d.$el)}else{}}});return this},events:{"click .toolSectionTitle > a":"toggle"},toggle:function(){this.model.set("open",!this.model.attributes.open)},update_open:function(){(this.model.attributes.open?this.$el.children(".toolSectionBody").slideDown("fast"):this.$el.children(".toolSectionBody").slideUp("fast"))}});var ToolSearchView=Backbone.View.extend({tagName:"div",id:"tool-search",className:"bar",template:Handlebars.templates.tool_search,events:{click:"focus_and_select","keyup :input":"query_changed"},render:function(){this.$el.append(this.template(this.model.toJSON()));if(!this.model.is_visible()){this.$el.hide()}return this},focus_and_select:function(){this.$el.find(":input").focus().select()},query_changed:function(){this.model.set("query",this.$el.find(":input").val())}});var ToolPanelView=Backbone.View.extend({tagName:"div",className:"toolMenu",initialize:function(){this.collection.tool_search.on("change:results",this.handle_search_results,this)},render:function(){var a=this;var b=new ToolSearchView({model:this.collection.tool_search});b.render();a.$el.append(b.$el);this.collection.each(function(d){if(d instanceof ToolPanelSection){var c=new ToolPanelSectionView({model:d});c.render();a.$el.append(c.$el)}else{if(d instanceof Tool){var e=new ToolLinkView({model:d,className:"toolTitleNoSection"});e.render();a.$el.append(e.$el)}else{if(d instanceof ToolPanelLabel){var f=new ToolPanelLabelView({model:d});f.render();a.$el.append(f.$el)}}}});a.$el.find("a.tool-link").click(function(f){var d=$(this).attr("class").split(/\s+/)[0],c=a.collection.tools.get(d);a.trigger("tool_link_click",f,c)});return this},handle_search_results:function(){var a=this.collection.tool_search.attributes.results;if(a&&a.length===0){$("#search-no-results").show()}else{$("#search-no-results").hide()}}});var ToolFormView=Backbone.View.extend({className:"toolForm",template:Handlebars.templates.tool_form,render:function(){this.$el.children().remove();this.$el.append(this.template(this.model.toJSON()))}});var IntegratedToolMenuAndView=Backbone.View.extend({className:"toolMenuAndView",initialize:function(){this.tool_panel_view=new ToolPanelView({collection:this.collection});this.tool_form_view=new ToolFormView()},render:function(){this.tool_panel_view.render();this.tool_panel_view.$el.css("float","left");this.$el.append(this.tool_panel_view.$el);this.tool_form_view.$el.hide();this.$el.append(this.tool_form_view.$el);var a=this;this.tool_panel_view.on("tool_link_click",function(c,b){c.preventDefault();a.show_tool(b)})},show_tool:function(b){var a=this;b.fetch().done(function(){a.tool_form_view.model=b;a.tool_form_view.render();a.tool_form_view.$el.show();$("#left").width("650px")})}});
\ No newline at end of file
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd static/scripts/packed/viz/paramamonster.js
--- a/static/scripts/packed/viz/paramamonster.js
+++ b/static/scripts/packed/viz/paramamonster.js
@@ -1,1 +1,1 @@
-var ToolParameterTree=Backbone.Model.extend({defaults:{tool:null,samples:4},initialize:function(d){var c=this.get("tool"),b=this.get("samples"),a=c.get("inputs").filter(function(h){return(["number","select"].indexOf(h.get("type"))!==-1)}),f=_.map(a,function(h){return h.get("name")});sampling=_.map(a,function(h){var i=h.get("type");if(i==="number"){return d3.scale.linear().domain([h.get("min"),h.get("max")]).ticks(b)}else{if(i==="select"){return _.map(h.get("options"),function(j){return j[0]})}}});var g=function(j,h,i){if(h.length-1===i){return _.map(h[i],function(k){return{name:k}})}return _.map(h[i],function(k){return{name:j[i]+":"+k,children:g(j,h,i+1)}})};var e={name:"Parameter Tree for "+c.get("name"),children:g(f,sampling,0)};this.set("valid_inputs",a);this.set("tree_data",e)}});var TileView=Backbone.View.extend({});var ToolParameterTreeView=Backbone.View.extend({className:"paramamonster",initialize:function(a){this.model=a.model},render:function(){var e=960,b=2000;var a=d3.layout.cluster().size([b,e-160]);var d=d3.svg.diagonal().projection(function(i){return[i.y,i.x]});var h=d3.select(this.$el[0]).append("svg").attr("width",e).attr("height",b).append("g").attr("transform","translate(80, 0)");var c=a.nodes(this.model.get("tree_data"));var g=h.selectAll("path.link").data(a.links(c)).enter().append("path").attr("class","link").attr("d",d);var f=h.selectAll("g.node").data(c).enter().append("g").attr("class","node").attr("transform",function(i){return"translate("+i.y+","+i.x+")"});f.append("circle").attr("r",4.5);f.append("text").attr("dx",function(i){return i.children?-8:8}).attr("dy",3).attr("text-anchor",function(i){return i.children?"end":"start"}).text(function(i){return i.name})}});
\ No newline at end of file
+var ToolParameterTree=Backbone.Model.extend({defaults:{tool:null,params:null,num_samples:4},initialize:function(d){var c=this.get("tool"),b=this.get("num_samples"),f=c.get("inputs").map(function(h){return h.get_samples(b)}),a=_.filter(f,function(h){return h.get("samples").length!==0});var g=function(j,h){var m=j[h],l=m.get("param"),k=l.get("name"),i=m.get("samples");if(j.length-1===h){return _.map(i,function(n){return{name:k+"="+n,param:l}})}return _.map(i,function(n){return{name:k+"="+n,param:l,children:g(a,h+1)}})};var e={name:"Parameter Tree for "+c.get("name"),children:g(a,0)};this.set("params",_.map(f,function(h){return h.get("param")}));this.set("tree_data",e)}});var Tile=Backbone.Model.extend({defaults:{track:null,index:null,region:null,resolution:null,data:null,stale:null,html_elt:null},initialize:function(a){}});var Track=Backbone.Model.extend({defaults:{dataset:null}});var FeatureTrack=Track.extend({defaults:{track:null},draw_tile:function(n,r,p,s,d,j,c){var q=this,b=r.canvas,y=d.get("start"),a=d.get("end"),D=25,e=this.left_offset;if(p==="summary_tree"||p==="Histogram"){if(n.dataset_type!=="summary_tree"){var k=this.get_summary_tree_data(n.data,y,a,200);if(n.max){k.max=n.max}n=k}var A=new painters.SummaryTreePainter(n,y,a,this.prefs);A.draw(r,b.width,b.height,j);return new SummaryTreeTile(q,tile_index,s,b,n.data,n.max)}var h=[],o=this.slotters[j].slots;all_slotted=true;if(n.data){var l=this.filters_manager.filters;for(var t=0,v=n.data.length;t<v;t++){var g=n.data[t];var u=false;var m;for(var x=0,C=l.length;x<C;x++){m=l[x];m.update_attrs(g);if(!m.keep(g)){u=true;break}}if(!u){h.push(g);if(!(g[0] in o)){all_slotted=false}}}}var B=(this.filters_manager.alpha_filter?new FilterScaler(this.filters_manager.alpha_filter):null);var z=(this.filters_manager.height_filter?new FilterScaler(this.filters_manager.height_filter):null);var A=new (this.painter)(h,y,a,this.prefs,p,B,z,c);var w=null;r.fillStyle=this.prefs.block_color;r.font=r.canvas.manager.default_font;r.textAlign="right";if(n.data){w=A.draw(r,b.width,b.height,j,o);w.translation=-e}return new FeatureTrackTile(q,tile_index,s,b,n.data,j,p,n.message,all_slotted,w)}});var TileView=Backbone.View.extend({});var ToolParameterTreeView=Backbone.View.extend({className:"tool-parameter-tree",initialize:function(a){this.model=a.model},render:function(){var e=960,b=2000;var a=d3.layout.cluster().size([b,e-160]);var d=d3.svg.diagonal().projection(function(i){return[i.y,i.x]});var h=d3.select(this.$el[0]).append("svg").attr("width",e).attr("height",b).append("g").attr("transform","translate(80, 0)");var c=a.nodes(this.model.get("tree_data"));var g=h.selectAll("path.link").data(a.links(c)).enter().append("path").attr("class","link").attr("d",d);var f=h.selectAll("g.node").data(c).enter().append("g").attr("class","node").attr("transform",function(i){return"translate("+i.y+","+i.x+")"});f.on("click",function(k,j){console.log(k,j)});f.append("circle").attr("r",4.5);f.append("text").attr("dx",function(i){return i.children?-8:8}).attr("dy",3).attr("text-anchor",function(i){return i.children?"end":"start"}).text(function(i){return i.name})}});
\ No newline at end of file
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd static/scripts/packed/viz/trackster.js
--- a/static/scripts/packed/viz/trackster.js
+++ b/static/scripts/packed/viz/trackster.js
@@ -1,1 +1,1 @@
-var class_module=function(b,a){var c=function(){var f=arguments[0];for(var e=1;e<arguments.length;e++){var d=arguments[e];for(key in d){f[key]=d[key]}}return f};a.extend=c};var server_state_deferred=function(d,a,c,f){var b=$.Deferred(),e=function(){$.getJSON(d,a,function(g){if(f(g)){b.resolve(g)}else{setTimeout(e,c)}})};e();return b};var requestAnimationFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(b,a){window.setTimeout(b,1000/60)}})();var BEFORE=1001,CONTAINS=1002,OVERLAP_START=1003,OVERLAP_END=1004,CONTAINED_BY=1005,AFTER=1006;var compute_overlap=function(e,b){var g=e[0],f=e[1],d=b[0],c=b[1],a;if(g<d){if(f<d){a=BEFORE}else{if(f<=c){a=OVERLAP_START}else{a=CONTAINS}}}else{if(g>c){a=AFTER}else{if(f<=c){a=CONTAINED_BY}else{a=OVERLAP_END}}}return a};var is_overlap=function(c,b){var a=compute_overlap(c,b);return(a!==BEFORE&&a!==AFTER)};var is_deferred=function(a){return("isResolved" in a)};var get_random_color=function(a){if(!a){a="#ffffff"}if(typeof(a)==="string"){a=[a]}for(var j=0;j<a.length;j++){a[j]=parseInt(a[j].slice(1),16)}var n=function(t,s,i){return((t*299)+(s*587)+(i*114))/1000};var e=function(v,u,w,s,i,t){return(Math.max(v,s)-Math.min(v,s))+(Math.max(u,i)-Math.min(u,i))+(Math.max(w,t)-Math.min(w,t))};var g,o,f,k,q,h,r,c,d,b,p,m=false,l=0;do{g=Math.round(Math.random()*16777215);o=(g&16711680)>>16;f=(g&65280)>>8;k=g&255;d=n(o,f,k);m=true;for(var j=0;j<a.length;j++){q=a[j];h=(q&16711680)>>16;r=(q&65280)>>8;c=q&255;b=n(h,r,c);p=e(o,f,k,h,r,c);if((Math.abs(d-b)<40)||(p<200)){m=false;break}}l++}while(!m&&l<=10);return"#"+(16777216+g).toString(16).substr(1,6)};var create_action_icon=function(c,b,a){return $("<a/>").attr("href","javascript:void(0);").attr("title",c).addClass("icon-button").addClass(b).tipsy({gravity:"s"}).click(a)};var trackster_module=function(e,U){var p=e("class").extend,s=e("slotting"),J=e("painters");var ab=function(ac,ad){this.document=ac;this.default_font=ad!==undefined?ad:"9px Monaco, Lucida Console, monospace";this.dummy_canvas=this.new_canvas();this.dummy_context=this.dummy_canvas.getContext("2d");this.dummy_context.font=this.default_font;this.char_width_px=this.dummy_context.measureText("A").width;this.patterns={};this.load_pattern("right_strand","/visualization/strand_right.png");this.load_pattern("left_strand","/visualization/strand_left.png");this.load_pattern("right_strand_inv","/visualization/strand_right_inv.png");this.load_pattern("left_strand_inv","/visualization/strand_left_inv.png")};p(ab.prototype,{load_pattern:function(ac,ag){var ad=this.patterns,ae=this.dummy_context,af=new Image();af.src=galaxy_paths.attributes.image_path+ag;af.onload=function(){ad[ac]=ae.createPattern(af,"repeat")}},get_pattern:function(ac){return this.patterns[ac]},new_canvas:function(){var ac=this.document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(ac)}ac.manager=this;return ac}});var n={};var l=function(ac,ad){n[ac.attr("id")]=ad};var m=function(ac,ae,ag,af){ag=".group";var ad={};n[ac.attr("id")]=af;ac.bind("drag",{handle:"."+ae,relative:true},function(ao,ap){var an=$(this);var at=$(this).parent(),ak=at.children(),am=n[$(this).attr("id")],aj,ai,aq,ah,al;ai=$(this).parents(ag);if(ai.length!==0){aq=ai.position().top;ah=aq+ai.outerHeight();if(ap.offsetY<aq){$(this).insertBefore(ai);var ar=n[ai.attr("id")];ar.remove_drawable(am);ar.container.add_drawable_before(am,ar);return}else{if(ap.offsetY>ah){$(this).insertAfter(ai);var ar=n[ai.attr("id")];ar.remove_drawable(am);ar.container.add_drawable(am);return}}}ai=null;for(al=0;al<ak.length;al++){aj=$(ak.get(al));aq=aj.position().top;ah=aq+aj.outerHeight();if(aj.is(ag)&&this!==aj.get(0)&&ap.offsetY>=aq&&ap.offsetY<=ah){if(ap.offsetY-aq<ah-ap.offsetY){aj.find(".content-div").prepend(this)}else{aj.find(".content-div").append(this)}if(am.container){am.container.remove_drawable(am)}n[aj.attr("id")].add_drawable(am);return}}var aj;for(al=0;al<ak.length;al++){aj=$(ak.get(al));if(ap.offsetY<aj.position().top&&!(aj.hasClass("reference-track")||aj.hasClass("intro"))){break}}if(al===ak.length){if(this!==ak.get(al-1)){at.append(this);n[at.attr("id")].move_drawable(am,al)}}else{if(this!==ak.get(al)){$(this).insertBefore(ak.get(al));n[at.attr("id")].move_drawable(am,(ap.deltaY>0?al-1:al))}}}).bind("dragstart",function(){ad["border-top"]=ac.css("border-top");ad["border-bottom"]=ac.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(ad)})};U.moveable=m;var aa=16,D=9,A=20,x=100,G=12000,R=400,I=5000,u=100,o="There was an error in indexing this dataset. ",H="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",B="No data for this chrom/contig.",t="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",v="Tool cannot be rerun: ",a="Loading data...",V="Ready for display",O=10,F=20;function W(ad,ac){if(!ac){ac=0}var ae=Math.pow(10,ac);return Math.round(ad*ae)/ae}var c=function(ac){this.num_elements=ac;this.clear()};p(c.prototype,{get:function(ad){var ac=this.key_ary.indexOf(ad);if(ac!==-1){if(this.obj_cache[ad].stale){this.key_ary.splice(ac,1);delete this.obj_cache[ad]}else{this.move_key_to_end(ad,ac)}}return this.obj_cache[ad]},set:function(ad,ae){if(!this.obj_cache[ad]){if(this.key_ary.length>=this.num_elements){var ac=this.key_ary.shift();delete this.obj_cache[ac]}this.key_ary.push(ad)}this.obj_cache[ad]=ae;return ae},move_key_to_end:function(ad,ac){this.key_ary.splice(ac,1);this.key_ary.push(ad)},clear:function(){this.obj_cache={};this.key_ary=[]},size:function(){return this.key_ary.length}});var P=function(ad,ac){c.call(this,ad);this.track=ac};p(P.prototype,c.prototype,{load_data:function(al,ag,aj,ad,ai){var ak=this.track.view.chrom,af={chrom:ak,low:al,high:ag,mode:aj,resolution:ad,dataset_id:this.track.dataset_id,hda_ldda:this.track.hda_ldda};$.extend(af,ai);if(this.track.filters_manager){var am=[];var ac=this.track.filters_manager.filters;for(var ah=0;ah<ac.length;ah++){am.push(ac[ah].name)}af.filter_cols=JSON.stringify(am)}var ae=this;return $.getJSON(this.track.data_url,af,function(an){ae.set_data(al,ag,an)})},get_data:function(ak,ae,ai,ad,ah){var al=this.get(ak,ae);if(al&&(is_deferred(al)||this.track.data_and_mode_compatible(al,ai))){return al}var am,aj,ac,ag,ai,al;for(var af=0;af<this.key_ary.length;af++){am=this.key_ary[af];aj=this.split_key(am);ac=aj[0];ag=aj[1];if(ak>=ac&&ae<=ag){var al=this.obj_cache[am];if(is_deferred(al)||(this.track.data_and_mode_compatible(al,ai)&&this.track.can_subset(al))){this.move_key_to_end(am,af);return al}}}al=this.load_data(ak,ae,ai,ad,ah);this.set_data(ak,ae,al);return al},DEEP_DATA_REQ:"deep",BROAD_DATA_REQ:"breadth",get_more_data:function(ak,af,aj,ae,ai,ag){var al=this.get(ak,af);if(!(al&&this.track.data_and_mode_compatible(al,aj))){console.log("ERROR: no current data for: ",this.track,ak,af,aj,ae,ai);return}al.stale=true;var ad=ak;if(ag===this.DEEP_DATA_REQ){$.extend(ai,{start_val:al.data.length+1})}else{if(ag===this.BROAD_DATA_REQ){ad=(al.max_high?al.max_high:al.data[al.data.length-1][2])+1}}var ac=this,ah=this.load_data(ad,af,aj,ae,ai);new_data_available=$.Deferred();this.set_data(ak,af,new_data_available);$.when(ah).then(function(am){if(am.data){am.data=al.data.concat(am.data);if(am.max_low){am.max_low=al.max_low}if(am.message){am.message=am.message.replace(/[0-9]+/,am.data.length)}}ac.set_data(ak,af,am);new_data_available.resolve(am)});return new_data_available},get:function(ac,ad){return c.prototype.get.call(this,this.gen_key(ac,ad))},set_data:function(ad,ae,ac){return this.set(this.gen_key(ad,ae),ac)},gen_key:function(ac,ae){var ad=ac+"_"+ae;return ad},split_key:function(ac){return ac.split("_")}});var E=function(ad,ac,ae){P.call(this,ad,ac,ae)};p(E.prototype,P.prototype,c.prototype,{get:P.prototype.get,load_data:function(ac,af,ag,ad,ae){if(ad>1){return{data:null}}return P.prototype.load_data.call(this,ac,af,ag,ad,ae)}});var q=function(ad,ac,af){if(!q.id_counter){q.id_counter=0}this.id=q.id_counter++;this.name=af.name;this.view=ad;this.container=ac;this.config=new C({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name}],saved_values:af.prefs,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=af.drag_handle_class;this.is_overview=false;this.action_icons={};this.content_visible=true;this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").css("float","left").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(ag){ag.stopPropagation()});var ae=this;this.container_div.hover(function(){ae.icons_div.show()},function(){ae.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};q.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(ac){if(ac.content_visible){ac.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");ac.hide_contents();ac.content_visible=false}else{ac.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");ac.content_visible=true;ac.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(ad){var af=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},ac=function(){ad.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},ae=function(ag){if((ag.keyCode||ag.which)===27){af()}else{if((ag.keyCode||ag.which)===13){ac()}}};$(window).bind("keypress.check_enter_esc",ae);show_modal("Configure",ad.config.build_form(),{Cancel:af,OK:ac})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(ac){$(".tipsy").remove();ac.remove()}}];p(q.prototype,{init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(){},to_dict:function(){},update_icons:function(){},set_name:function(ac){this.old_name=this.name;this.name=ac;this.name_div.text(this.name)},revert_name:function(){if(this.old_name){this.name=this.old_name;this.name_div.text(this.name)}},remove:function(){this.changed();this.container.remove_drawable(this);var ac=this.view;this.container_div.hide(0,function(){$(this).remove();ac.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(ad,ai,ah,ag,ac,af){var ae=this;this.action_icons[ad]=$("<a/>").attr("href","javascript:void(0);").attr("title",ai).addClass("icon-button").addClass(ah).tipsy({gravity:"s"}).click(function(){ag(ae)}).appendTo(this.icons_div);if(af){this.action_icons[ad].hide()}},build_action_icons:function(ac){var ae;for(var ad=0;ad<ac.length;ad++){ae=ac[ad];this.add_action_icon(ae.name,ae.title,ae.css_class,ae.on_click_fn,ae.prepend,ae.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){}});var w=function(ad,ac,ae){q.call(this,ad,ac,ae);this.obj_type=ae.obj_type;this.drawables=[]};p(w.prototype,q.prototype,{unpack_drawables:function(ae){this.drawables=[];var ad;for(var ac=0;ac<ae.length;ac++){ad=object_from_template(ae[ac],this);this.add_drawable(ad)}},init:function(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac].init()}},_draw:function(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac]._draw()}},to_dict:function(){var ad=[];for(var ac=0;ac<this.drawables.length;ac++){ad.push(this.drawables[ac].to_dict())}return{name:this.name,prefs:this.prefs,obj_type:this.obj_type,drawables:ad}},add_drawable:function(ac){this.drawables.push(ac);ac.container=this;this.changed()},add_drawable_before:function(ae,ac){this.changed();var ad=this.drawables.indexOf(ac);if(ad!==-1){this.drawables.splice(ad,0,ae);return true}return false},replace_drawable:function(ae,ac,ad){var af=this.drawables.indexOf(ae);if(af!==-1){this.drawables[af]=ac;if(ad){ae.container_div.replaceWith(ac.container_div)}this.changed()}return af},remove_drawable:function(ad){var ac=this.drawables.indexOf(ad);if(ac!==-1){this.drawables.splice(ac,1);ad.container=null;this.changed();return true}return false},move_drawable:function(ad,ae){var ac=this.drawables.indexOf(ad);if(ac!==-1){this.drawables.splice(ac,1);this.drawables.splice(ae,0,ad);this.changed();return true}return false}});var N=function(ad,ac,af){p(af,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});w.call(this,ad,ac,af);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);l(this.container_div,this);l(this.content_div,this);m(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new X(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in af){this.unpack_drawables(af.drawables)}if("filters" in af){var ae=this.filters_manager;this.filters_manager=new X(this,af.filters);ae.parent_div.replaceWith(this.filters_manager.parent_div);if(af.filters.visible){this.setup_multitrack_filtering()}}};p(N.prototype,q.prototype,w.prototype,{action_icons_def:[q.prototype.action_icons_def[0],q.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(ac){$(".tipsy").remove();ac.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ac){if(ac.filters_manager.visible()){ac.filters_manager.clear_filters();ac._restore_filter_managers()}else{ac.setup_multitrack_filtering();ac.request_draw(true)}ac.filters_manager.toggle()}},q.prototype.action_icons_def[2]],build_container_div:function(){var ac=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(ac)}return ac},build_header_div:function(){var ac=$("<div/>").addClass("track-header");ac.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(ac);return ac},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var ae=this.drawables.length;if(ae===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(ae===1){if(this.drawables[0] instanceof h){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var ai,ao=true,ag=this.drawables[0].get_type(),ac=0;for(var al=0;al<ae;al++){ai=this.drawables[al];if(ai.get_type()!==ag){can_composite=false;break}if(ai instanceof d){ac++}}if(ao||ac===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tipsy").remove()}if(ac>1&&ac===this.drawables.length){var ap={},ad;ai=this.drawables[0];for(var ak=0;ak<ai.filters_manager.filters.length;ak++){ad=ai.filters_manager.filters[ak];ap[ad.name]=[ad]}for(var al=1;al<this.drawables.length;al++){ai=this.drawables[al];for(var ak=0;ak<ai.filters_manager.filters.length;ak++){ad=ai.filters_manager.filters[ak];if(ad.name in ap){ap[ad.name].push(ad)}}}this.filters_manager.remove_all();var af,ah,aj,am;for(var an in ap){af=ap[an];if(af.length===ac){ah=new S({name:af[0].name,index:af[0].index});this.filters_manager.add_filter(ah)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var ac=0;ac<this.drawables.length;ac++){this.drawables[ac].filters_manager=this.saved_filters_managers[ac]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var ac=0;ac<this.drawables.length;ac++){drawable=this.drawables[ac];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var ag=[];for(var ad=0;ad<this.drawables.length;ad++){ag.push(this.drawables[ad].name)}var ae="Composite Track of "+this.drawables.length+" tracks ("+ag.join(", ")+")";var af=new h(this.view,this.view,{name:ae,drawables:this.drawables});var ac=this.container.replace_drawable(this,af,true);af.request_draw()},add_drawable:function(ac){w.prototype.add_drawable.call(this,ac);this.update_icons()},remove_drawable:function(ac){w.prototype.remove_drawable.call(this,ac);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var ac=p(w.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return ac},request_draw:function(ac,ae){for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].request_draw(ac,ae)}}});var Z=function(ac){p(ac,{obj_type:"View"});w.call(this,"View",ac.container,ac);this.chrom=null;this.vis_id=ac.vis_id;this.dbkey=ac.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.init();this.canvas_manager=new ab(this.container.get(0).ownerDocument);this.reset()};_.extend(Z.prototype,Backbone.Events);p(Z.prototype,w.prototype,{init:function(){this.requested_redraw=false;var ae=this.container,ac=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ae);this.browser_content_div=$("<div/>").addClass("content").css("position","relative").appendTo(ae);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ae);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;l(this.viewport_container,ac);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var af=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){add_datasets(add_datasets_url,add_track_async_url,function(ag){_.each(ag,function(ah){ac.add_drawable(object_from_template(ah,ac))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("href","javascript:void(0);").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var ad=function(ag){if(ag.type==="focusout"||(ag.keyCode||ag.which)===13||(ag.keyCode||ag.which)===27){if((ag.keyCode||ag.which)!==27){ac.go_to($(this).val())}$(this).hide();$(this).val("");ac.location_span.show();ac.chrom_select.show()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",ad).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-title","Click to change location").tipsy({gravity:"n"}).appendTo(this.nav_controls);this.location_span.click(function(){ac.location_span.hide();ac.chrom_select.hide();ac.nav_input.val(ac.chrom+":"+ac.low+"-"+ac.high);ac.nav_input.css("display","inline-block");ac.nav_input.select();ac.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tipsy({gravity:"n"}).click(function(){ac.zoom_out();ac.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tipsy({gravity:"n"}).click(function(){ac.zoom_in();ac.request_redraw()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){ac.change_chrom(ac.chrom_select.val())});this.browser_content_div.click(function(ag){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(ag){ac.zoom_in(ag.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(ag,ah){this.current_x=ah.offsetX}).bind("drag",function(ag,ai){var aj=ai.offsetX-this.current_x;this.current_x=ai.offsetX;var ah=Math.round(aj/ac.viewport_container.width()*(ac.max_high-ac.max_low));ac.move_delta(-ah)});this.overview_close.click(function(){ac.reset_overview()});this.viewport_container.bind("draginit",function(ag,ah){if(ag.clientX>ac.viewport_container.width()-16){return false}}).bind("dragstart",function(ag,ah){ah.original_low=ac.low;ah.current_height=ag.clientY;ah.current_x=ah.offsetX}).bind("drag",function(ai,ak){var ag=$(this);var al=ak.offsetX-ak.current_x;var ah=ag.scrollTop()-(ai.clientY-ak.current_height);ag.scrollTop(ah);ak.current_height=ai.clientY;ak.current_x=ak.offsetX;var aj=Math.round(al/ac.viewport_container.width()*(ac.high-ac.low));ac.move_delta(aj)}).bind("mousewheel",function(ai,ak,ah,ag){if(ah){ah*=50;var aj=Math.round(-ah/ac.viewport_container.width()*(ac.high-ac.low));ac.move_delta(aj)}});this.top_labeltrack.bind("dragstart",function(ag,ah){return $("<div />").css({height:ac.browser_content_div.height()+ac.top_labeltrack.height()+ac.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(ak,al){$(al.proxy).css({left:Math.min(ak.pageX,al.startX)-ac.container.offset().left,width:Math.abs(ak.pageX-al.startX)});var ah=Math.min(ak.pageX,al.startX)-ac.container.offset().left,ag=Math.max(ak.pageX,al.startX)-ac.container.offset().left,aj=(ac.high-ac.low),ai=ac.viewport_container.width();ac.update_location(Math.round(ah/ai*aj)+ac.low,Math.round(ag/ai*aj)+ac.low)}).bind("dragend",function(al,am){var ah=Math.min(al.pageX,am.startX),ag=Math.max(al.pageX,am.startX),aj=(ac.high-ac.low),ai=ac.viewport_container.width(),ak=ac.low;ac.low=Math.round(ah/ai*aj)+ak;ac.high=Math.round(ag/ai*aj)+ak;$(am.proxy).remove();ac.request_redraw()});this.add_label_track(new Y(this,{content_div:this.top_labeltrack}));this.add_label_track(new Y(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){ac.resize_window()},500)});$(document).bind("redraw",function(){ac.redraw()});this.reset();$(window).trigger("resize")},changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(ad,af,ac,ag){if(this.timer){clearTimeout(this.timer)}if(ag){var ae=this;this.timer=setTimeout(function(){ae.trigger("navigate",ad+":"+af+"-"+ac)},500)}else{view.trigger("navigate",ad+":"+af+"-"+ac)}},update_location:function(ac,ae){this.location_span.text(commatize(ac)+" - "+commatize(ae));this.nav_input.val(this.chrom+":"+commatize(ac)+"-"+commatize(ae));var ad=view.chrom_select.val();if(ad!==""){this.trigger_navigate(ad,view.low,view.high,true)}},load_chroms:function(ae){ae.num=u;ae.dbkey=this.dbkey;var ac=this,ad=$.Deferred();$.ajax({url:chrom_url,data:ae,dataType:"json",success:function(ag){if(ag.chrom_info.length===0){alert("Invalid chromosome: "+ae.chrom);return}if(ag.reference){ac.add_label_track(new y(ac))}ac.chrom_data=ag.chrom_info;var aj='<option value="">Select Chrom/Contig</option>';for(var ai=0,af=ac.chrom_data.length;ai<af;ai++){var ah=ac.chrom_data[ai].chrom;aj+='<option value="'+ah+'">'+ah+"</option>"}if(ag.prev_chroms){aj+='<option value="previous">Previous '+u+"</option>"}if(ag.next_chroms){aj+='<option value="next">Next '+u+"</option>"}ac.chrom_select.html(aj);ac.chrom_start_index=ag.start_index;ad.resolve(ag)},error:function(){alert("Could not load chroms for this dbkey:",ac.dbkey)}});return ad},change_chrom:function(ah,ad,aj){var ae=this;if(!ae.chrom_data){ae.load_chroms_deferred.then(function(){ae.change_chrom(ah,ad,aj)});return}if(!ah||ah==="None"){return}var ae=this;if(ah==="previous"){ae.load_chroms({low:this.chrom_start_index-u});return}if(ah==="next"){ae.load_chroms({low:this.chrom_start_index+u});return}var ai=$.grep(ae.chrom_data,function(ak,al){return ak.chrom===ah})[0];if(ai===undefined){ae.load_chroms({chrom:ah},function(){ae.change_chrom(ah,ad,aj)});return}else{if(ah!==ae.chrom){ae.chrom=ah;ae.chrom_select.val(ae.chrom);ae.max_high=ai.len-1;ae.reset();ae.request_redraw(true);for(var ag=0,ac=ae.drawables.length;ag<ac;ag++){var af=ae.drawables[ag];if(af.init){af.init()}}if(ae.reference_track){ae.reference_track.init()}}if(ad!==undefined&&aj!==undefined){ae.low=Math.max(ad,0);ae.high=Math.min(aj,ae.max_high)}else{ae.low=0;ae.high=ae.max_high}ae.reset_overview();ae.request_redraw()}},go_to:function(ag){ag=ag.replace(/ |,/g,"");var ak=this,ac,af,ad=ag.split(":"),ai=ad[0],aj=ad[1];if(aj!==undefined){try{var ah=aj.split("-");ac=parseInt(ah[0],10);af=parseInt(ah[1],10)}catch(ae){return false}}ak.change_chrom(ai,ac,af)},move_fraction:function(ae){var ac=this;var ad=ac.high-ac.low;this.move_delta(ae*ad)},move_delta:function(af){var ac=this;var ae=ac.high-ac.low;if(ac.low-af<ac.max_low){ac.low=ac.max_low;ac.high=ac.max_low+ae}else{if(ac.high-af>ac.max_high){ac.high=ac.max_high;ac.low=ac.max_high-ae}else{ac.high-=af;ac.low-=af}}ac.request_redraw();var ad=ac.chrom_select.val();this.trigger_navigate(ad,ac.low,ac.high,true)},add_drawable:function(ac){w.prototype.add_drawable.call(this,ac);ac.init();this.changed();this.update_intro_div()},add_label_track:function(ac){ac.view=this;ac.init();this.label_tracks.push(ac)},remove_drawable:function(ae,ad){w.prototype.remove_drawable.call(this,ae);if(ad){var ac=this;ae.container_div.hide(0,function(){$(this).remove();ac.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(ak,ac,aj,ad){var ai=this,ag=(ad?[ad]:ai.drawables),ae;var ad;for(var ah=0;ah<ag.length;ah++){ad=ag[ah];ae=-1;for(var af=0;af<ai.tracks_to_be_redrawn.length;af++){if(ai.tracks_to_be_redrawn[af][0]===ad){ae=af;break}}if(ae<0){ai.tracks_to_be_redrawn.push([ad,ac,aj])}else{ai.tracks_to_be_redrawn[ah][1]=ac;ai.tracks_to_be_redrawn[ah][2]=aj}}if(!this.requested_redraw){requestAnimationFrame(function(){ai._redraw(ak)});this.requested_redraw=true}},_redraw:function(am){this.requested_redraw=false;var aj=this.low,af=this.high;if(aj<this.max_low){aj=this.max_low}if(af>this.max_high){af=this.max_high}var al=this.high-this.low;if(this.high!==0&&al<this.min_separation){af=aj+this.min_separation}this.low=Math.floor(aj);this.high=Math.ceil(af);this.update_location(this.low,this.high);this.resolution_b_px=(this.high-this.low)/this.viewport_container.width();this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var ac=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ai=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var an=13;this.overview_box.css({left:ac,width:Math.max(an,ai)}).show();if(ai<an){this.overview_box.css("left",ac-(an-ai)/2)}if(this.overview_highlight){this.overview_highlight.css({left:ac,width:ai})}if(!am){var ae,ad,ak;for(var ag=0,ah=this.tracks_to_be_redrawn.length;ag<ah;ag++){ae=this.tracks_to_be_redrawn[ag][0];ad=this.tracks_to_be_redrawn[ag][1];ak=this.tracks_to_be_redrawn[ag][2];if(ae){ae._draw(ad,ak)}}this.tracks_to_be_redrawn=[];for(ag=0,ah=this.label_tracks.length;ag<ah;ag++){this.label_tracks[ag]._draw()}}},zoom_in:function(ad,ae){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var af=this.high-this.low,ag=af/2+this.low,ac=(af/this.zoom_factor)/2;if(ad){ag=ad/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(ag-ac);this.high=Math.round(ag+ac);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var ad=this.high-this.low,ae=ad/2+this.low,ac=(ad*this.zoom_factor)/2;this.low=Math.round(ae-ac);this.high=Math.round(ae+ac);this.changed();this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.request_redraw()},set_overview:function(ae){if(this.overview_drawable){if(this.overview_drawable.dataset_id===ae.dataset_id){return}this.overview_viewport.find(".track").remove()}var ad=ae.copy({content_div:this.overview_viewport}),ac=this;ad.header_div.hide();ad.is_overview=true;ac.overview_drawable=ad;this.overview_drawable.postdraw_actions=function(){ac.overview_highlight.show().height(ac.overview_drawable.content_div.height());ac.overview_viewport.height(ac.overview_drawable.content_div.height()+ac.overview_box.outerHeight());ac.overview_close.show();ac.resize_window()};ac.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".tipsy").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var r=function(ae,aj,af){this.track=ae;this.name=aj.name;this.params=[];var aq=aj.params;for(var ag=0;ag<aq.length;ag++){var al=aq[ag],ad=al.name,ap=al.label,ah=unescape(al.html),ar=al.value,an=al.type;if(an==="number"){this.params.push(new f(ad,ap,ah,(ad in af?af[ad]:ar),al.min,al.max))}else{if(an==="select"){this.params.push(new L(ad,ap,ah,(ad in af?af[ad]:ar)))}else{console.log("WARNING: unrecognized tool parameter type:",ad,an)}}}this.parent_div=$("<div/>").addClass("dynamic-tool").hide();this.parent_div.bind("drag",function(au){au.stopPropagation()}).click(function(au){au.stopPropagation()}).bind("dblclick",function(au){au.stopPropagation()});var ao=$("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);var am=this.params;var ak=this;$.each(this.params,function(av,ay){var ax=$("<div>").addClass("param-row").appendTo(ak.parent_div);var au=$("<div>").addClass("param-label").text(ay.label).appendTo(ax);var aw=$("<div/>").addClass("param-input").html(ay.html).appendTo(ax);aw.find(":input").val(ay.value);$("<div style='clear: both;'/>").appendTo(ax)});this.parent_div.find("input").click(function(){$(this).select()});var at=$("<div>").addClass("param-row").appendTo(this.parent_div);var ai=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(at);var ac=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(at);var ak=this;ac.click(function(){ak.run_on_region()});ai.click(function(){ak.run_on_dataset()});if("visible" in af&&af.visible){this.parent_div.show()}};p(r.prototype,{update_params:function(){for(var ac=0;ac<this.params.length;ac++){this.params[ac].update_value()}},state_dict:function(){var ad={};for(var ac=0;ac<this.params.length;ac++){ad[this.params[ac].name]=this.params[ac].value}ad.visible=this.parent_div.is(":visible");return ad},get_param_values_dict:function(){var ac={};this.parent_div.find(":input").each(function(){var ad=$(this).attr("name"),ae=$(this).val();ac[ad]=JSON.stringify(ae)});return ac},get_param_values:function(){var ad=[];var ac={};this.parent_div.find(":input").each(function(){var ae=$(this).attr("name"),af=$(this).val();if(ae){ad[ad.length]=af}});return ad},run_on_dataset:function(){var ac=this;ac.run({dataset_id:this.track.original_dataset_id,tool_id:ac.name},null,function(ad){show_modal(ac.name+" is Running",ac.name+" is running on the complete dataset. Tool outputs are in dataset's history.",{Close:hide_modal})})},run_on_region:function(){var ad={dataset_id:this.track.original_dataset_id,chrom:this.track.view.chrom,low:this.track.view.low,high:this.track.view.high,tool_id:this.name},ah=this.track,ae=ad.tool_id+ah.tool_region_and_parameters_str(ad.chrom,ad.low,ad.high),ac;if(ah.container===view){var ag=new N(view,view,{name:this.name});var af=ah.container.replace_drawable(ah,ag,false);ag.container_div.insertBefore(ah.view.content_div.children()[af]);ag.add_drawable(ah);ah.container_div.appendTo(ag.content_div);ac=ag}else{ac=ah.container}var ai=new ah.constructor(view,ac,{name:ae,hda_ldda:"hda"});ai.init_for_tool_data();ai.change_mode(ah.mode);ai.set_filters_manager(ah.filters_manager.copy(ai));ai.update_icons();ac.add_drawable(ai);ai.tiles_div.text("Starting job.");this.update_params();this.run(ad,ai,function(aj){ai.dataset_id=aj.dataset_id;ai.tiles_div.text("Running job.");ai.init()})},run:function(ad,ae,af){$.extend(ad,this.get_param_values_dict());var ac=function(){$.getJSON(rerun_tool_url,ad,function(ag){if(ag==="no converter"){ae.container_div.addClass("error");ae.content_div.text(H)}else{if(ag.error){ae.container_div.addClass("error");ae.content_div.text(v+ag.message)}else{if(ag==="pending"){ae.container_div.addClass("pending");ae.content_div.text("Converting input data so that it can be used quickly with tool.");setTimeout(ac,2000)}else{af(ag)}}}})};ac()}});var L=function(ad,ac,ae,af){this.name=ad;this.label=ac;this.html=$(ae);this.value=af};p(L.prototype,{update_value:function(){this.value=$(this.html).val()}});var f=function(ae,ad,ag,ah,af,ac){L.call(this,ae,ad,ag,ah);this.min=af;this.max=ac};p(f.prototype,L.prototype,{update_value:function(){L.prototype.update_value.call(this);this.value=parseFloat(this.value)}});var g=function(ac){this.manager=null;this.name=ac.name;this.index=ac.index;this.tool_id=ac.tool_id;this.tool_exp_name=ac.tool_exp_name};p(g.prototype,{to_dict:function(){return{name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name}}});var S=function(al){g.call(this,al);this.low=("low" in al?al.low:-Number.MAX_VALUE);this.high=("high" in al?al.high:Number.MAX_VALUE);this.min=("min" in al?al.min:Number.MAX_VALUE);this.max=("max" in al?al.max:-Number.MAX_VALUE);this.container=null;this.slider=null;this.slider_label=null;var ah=function(am,an,ao){am.click(function(){var au=an.text(),ar=parseFloat(ao.slider("option","max")),aq=(ar<=1?4:ar<=1000000?ar.toString().length:6),at=false,ap=$(this).parents(".slider-row");ap.addClass("input");if(ao.slider("option","values")){aq=2*aq+1;at=true}an.text("");$("<input type='text'/>").attr("size",aq).attr("maxlength",aq).attr("value",au).appendTo(an).focus().select().click(function(av){av.stopPropagation()}).blur(function(){$(this).remove();an.text(au);ap.removeClass("input")}).keyup(function(az){if(az.keyCode===27){$(this).trigger("blur")}else{if(az.keyCode===13){var ax=ao.slider("option","min"),av=ao.slider("option","max"),ay=function(aA){return(isNaN(aA)||aA>av||aA<ax)},aw=$(this).val();if(!at){aw=parseFloat(aw);if(ay(aw)){alert("Parameter value must be in the range ["+ax+"-"+av+"]");return $(this)}}else{aw=aw.split("-");aw=[parseFloat(aw[0]),parseFloat(aw[1])];if(ay(aw[0])||ay(aw[1])){alert("Parameter value must be in the range ["+ax+"-"+av+"]");return $(this)}}ao.slider((at?"values":"value"),aw);ap.removeClass("input")}}})})};var ad=this;ad.parent_div=$("<div/>").addClass("filter-row slider-row");var ac=$("<div/>").addClass("elt-label").appendTo(ad.parent_div),aj=$("<span/>").addClass("slider-name").text(ad.name+" ").appendTo(ac),ae=$("<span/>").text(this.low+"-"+this.high),af=$("<span/>").addClass("slider-value").appendTo(ac).append("[").append(ae).append("]");ad.values_span=ae;var ai=$("<div/>").addClass("slider").appendTo(ad.parent_div);ad.control_element=$("<div/>").attr("id",ad.name+"-filter-control").appendTo(ai);var ag=[0,0];ad.control_element.slider({range:true,min:this.min,max:this.max,step:this.get_slider_step(this.min,this.max),values:[this.low,this.high],slide:function(am,an){ad.slide(am,an)},change:function(am,an){ad.control_element.slider("option","slide").call(ad.control_element,am,an)}});ad.slider=ad.control_element;ad.slider_label=ae;ah(af,ae,ad.control_element);var ak=$("<div/>").addClass("display-controls").appendTo(ad.parent_div);this.transparency_icon=create_action_icon("Use filter for data transparency","layer-transparent",function(){if(ad.manager.alpha_filter!==ad){ad.manager.alpha_filter=ad;ad.manager.parent_div.find(".layer-transparent").removeClass("active").hide();ad.transparency_icon.addClass("active").show()}else{ad.manager.alpha_filter=null;ad.transparency_icon.removeClass("active")}ad.manager.track.request_draw(true,true)}).appendTo(ak).hide();this.height_icon=create_action_icon("Use filter for data height","arrow-resize-090",function(){if(ad.manager.height_filter!==ad){ad.manager.height_filter=ad;ad.manager.parent_div.find(".arrow-resize-090").removeClass("active").hide();ad.height_icon.addClass("active").show()}else{ad.manager.height_filter=null;ad.height_icon.removeClass("active")}ad.manager.track.request_draw(true,true)}).appendTo(ak).hide();ad.parent_div.hover(function(){ad.transparency_icon.show();ad.height_icon.show()},function(){if(ad.manager.alpha_filter!==ad){ad.transparency_icon.hide()}if(ad.manager.height_filter!==ad){ad.height_icon.hide()}});$("<div style='clear: both;'/>").appendTo(ad.parent_div)};p(S.prototype,{to_dict:function(){var ac=g.prototype.to_dict.call(this);return p(ac,{type:"number",min:this.min,max:this.max,low:this.low,high:this.high})},copy:function(){return new S({name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name})},get_slider_step:function(ae,ac){var ad=ac-ae;return(ad<=2?0.01:1)},slide:function(ad,ae){var ac=ae.values;this.values_span.text(ac[0]+"-"+ac[1]);this.low=ac[0];this.high=ac[1];this.manager.track.request_draw(true,true)},applies_to:function(ac){if(ac.length>this.index){return true}return false},_keep_val:function(ac){return(isNaN(ac)||(ac>=this.low&&ac<=this.high))},keep:function(ad){if(!this.applies_to(ad)){return true}var af=this;var ag=ad[this.index];if(ag instanceof Array){var ae=true;for(var ac=0;ac<ag.length;ac++){if(!this._keep_val(ag[ac])){ae=false;break}}return ae}else{return this._keep_val(ad[this.index])}},update_attrs:function(af){var ac=false;if(!this.applies_to(af)){return ac}var ad=af[this.index];if(!(ad instanceof Array)){ad=[ad]}for(var ae=0;ae<ad.length;ae++){var ag=ad[ae];if(ag<this.min){this.min=Math.floor(ag);ac=true}if(ag>this.max){this.max=Math.ceil(ag);ac=true}}return ac},update_ui_elt:function(){if(this.min<this.max){this.parent_div.show()}else{this.parent_div.hide()}var ad=this.slider.slider("option","min"),ac=this.slider.slider("option","max");if(this.min<ad||this.max>ac){this.slider.slider("option","min",this.min);this.slider.slider("option","max",this.max);this.slider.slider("option","step",this.get_slider_step(this.min,this.max));this.slider.slider("option","values",[this.min,this.max])}}});var X=function(ae,ak){this.track=ae;this.alpha_filter=null;this.height_filter=null;this.filters=[];this.parent_div=$("<div/>").addClass("filters").hide();this.parent_div.bind("drag",function(am){am.stopPropagation()}).click(function(am){am.stopPropagation()}).bind("dblclick",function(am){am.stopPropagation()}).bind("keydown",function(am){am.stopPropagation()});if(ak&&"filters" in ak){var ac=("alpha_filter" in ak?ak.alpha_filter:null),af=("height_filter" in ak?ak.height_filter:null),ah=ak.filters,ad;for(var ai=0;ai<ah.length;ai++){if(ah[ai].type==="number"){ad=new S(ah[ai]);this.add_filter(ad);if(ad.name===ac){this.alpha_filter=ad;ad.transparency_icon.addClass("active").show()}if(ad.name===af){this.height_filter=ad;ad.height_icon.addClass("active").show()}}else{console.log("ERROR: unsupported filter: ",name,type)}}if("visible" in ak&&ak.visible){this.parent_div.show()}}if(this.filters.length!==0){var al=$("<div/>").addClass("param-row").appendTo(this.parent_div);var aj=$("<input type='submit'/>").attr("value","Run on complete dataset").appendTo(al);var ag=this;aj.click(function(){ag.run_on_dataset()})}};p(X.prototype,{show:function(){this.parent_div.show()},hide:function(){this.parent_div.hide()},toggle:function(){this.parent_div.toggle()},visible:function(){return this.parent_div.is(":visible")},to_dict:function(){var af={},ae=[],ad;for(var ac=0;ac<this.filters.length;ac++){ad=this.filters[ac];ae.push(ad.to_dict())}af.filters=ae;af.alpha_filter=(this.alpha_filter?this.alpha_filter.name:null);af.height_filter=(this.height_filter?this.height_filter.name:null);af.visible=this.parent_div.is(":visible");return af},copy:function(ad){var ae=new X(ad);for(var ac=0;ac<this.filters.length;ac++){ae.add_filter(this.filters[ac].copy())}return ae},add_filter:function(ac){ac.manager=this;this.parent_div.append(ac.parent_div);this.filters.push(ac)},remove_all:function(){this.filters=[];this.parent_div.children().remove()},init_filters:function(){for(var ac=0;ac<this.filters.length;ac++){var ad=this.filters[ac];ad.update_ui_elt()}},clear_filters:function(){for(var ac=0;ac<this.filters.length;ac++){var ad=this.filters[ac];ad.slider.slider("option","values",[ad.min,ad.max])}this.alpha_filter=null;this.height_filter=null;this.parent_div.find(".icon-button").hide()},run_on_dataset:function(){var ak=function(ao,am,an){if(!(am in ao)){ao[am]=an}return ao[am]};var ae={},ac,ad,af;for(var ag=0;ag<this.filters.length;ag++){ac=this.filters[ag];if(ac.tool_id){if(ac.min!==ac.low){ad=ak(ae,ac.tool_id,[]);ad[ad.length]=ac.tool_exp_name+" >= "+ac.low}if(ac.max!==ac.high){ad=ak(ae,ac.tool_id,[]);ad[ad.length]=ac.tool_exp_name+" <= "+ac.high}}}var ai=[];for(var al in ae){ai[ai.length]=[al,ae[al]]}var aj=ai.length;(function ah(at,ap){var an=ap[0],ao=an[0],ar=an[1],aq="("+ar.join(") and (")+")",am={cond:aq,input:at,target_dataset_id:at,tool_id:ao},ap=ap.slice(1);$.getJSON(run_tool_url,am,function(au){if(au.error){show_modal("Filter Dataset","Error running tool "+ao,{Close:hide_modal})}else{if(ap.length===0){show_modal("Filtering Dataset","Filter(s) are running on the complete dataset. Outputs are in dataset's history.",{Close:hide_modal})}else{ah(au.dataset_id,ap)}}})})(this.track.dataset_id,ai)}});var z=function(ac,ad){J.Scaler.call(this,ad);this.filter=ac};z.prototype.gen_val=function(ac){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(ac[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var C=function(ac){this.track=ac.track;this.params=ac.params;this.values={};this.restore_values((ac.saved_values?ac.saved_values:{}));this.onchange=ac.onchange};p(C.prototype,{restore_values:function(ac){var ad=this;$.each(this.params,function(ae,af){if(ac[af.key]!==undefined){ad.values[af.key]=ac[af.key]}else{ad.values[af.key]=af.default_value}})},build_form:function(){var af=this;var ac=$("<div />");var ae;function ad(aj,ag){for(var an=0;an<aj.length;an++){ae=aj[an];if(ae.hidden){continue}var ah="param_"+an;var ar=af.values[ae.key];var av=$("<div class='form-row' />").appendTo(ag);av.append($("<label />").attr("for",ah).text(ae.label+":"));if(ae.type==="bool"){av.append($('<input type="checkbox" />').attr("id",ah).attr("name",ah).attr("checked",ar))}else{if(ae.type==="text"){av.append($('<input type="text"/>').attr("id",ah).val(ar).click(function(){$(this).select()}))}else{if(ae.type==="select"){var ap=$("<select />").attr("id",ah);for(var al=0;al<ae.options.length;al++){$("<option/>").text(ae.options[al].label).attr("value",ae.options[al].value).appendTo(ap)}ap.val(ar);av.append(ap)}else{if(ae.type==="color"){var au=$("<div/>").appendTo(av),ao=$("<input />").attr("id",ah).attr("name",ah).val(ar).css("float","left").appendTo(au).click(function(ax){$(".tipsy").hide();var aw=$(this).siblings(".tipsy");aw.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(aw).height()/2)+($(this).height()/2)}).show();aw.click(function(ay){ay.stopPropagation()});$(document).bind("click.color-picker",function(){aw.hide();$(document).unbind("click.color-picker")});ax.stopPropagation()}),am=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(au).attr("title","Set new random color").tipsy({gravity:"s"}),aq=$("<div class='tipsy tipsy-west' style='position: absolute;' />").appendTo(au).hide(),ai=$("<div style='background-color: black; padding: 10px;'></div>").appendTo(aq),at=$("<div/>").appendTo(ai),ak=$.farbtastic(at,{width:100,height:100,callback:ao,color:ar});au.append($("<div/>").css("clear","both"));(function(aw){am.click(function(){aw.setColor(get_random_color())})})(ak)}else{av.append($("<input />").attr("id",ah).attr("name",ah).val(ar))}}}}if(ae.help){av.append($("<div class='help'/>").text(ae.help))}}}ad(this.params,ac);return ac},update_from_form:function(ac){var ae=this;var ad=false;$.each(this.params,function(af,ah){if(!ah.hidden){var ai="param_"+af;var ag=ac.find("#"+ai).val();if(ah.type==="float"){ag=parseFloat(ag)}else{if(ah.type==="int"){ag=parseInt(ag)}else{if(ah.type==="bool"){ag=ac.find("#"+ai).is(":checked")}}}if(ag!==ae.values[ah.key]){ae.values[ah.key]=ag;ad=true}}});if(ad){this.onchange();this.track.changed()}}});var b=function(ac,af,ae,ad,ag){this.track=ac;this.index=af;var ah=this.track._get_tile_bounds(af,ae);this.low=ah[0];this.high=ah[1];this.resolution=ae;this.html_elt=$("<div class='track-tile'/>").append(ad).height($(ad).attr("height"));this.data=ag;this.stale=false};b.prototype.predisplay_actions=function(){};var k=function(ac,af,ae,ad,ag,ah){b.call(this,ac,af,ae,ad,ag);this.max_val=ah};p(k.prototype,b.prototype);var M=function(af,ak,ag,ae,ai,ao,aj,ap,ad,am){b.call(this,af,ak,ag,ae,ai);this.mode=aj;this.all_slotted=ad;this.feature_mapper=am;this.has_icons=false;if(ap){this.has_icons=true;var al=this;ae=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:A-1,width:ae.width}).prependTo(this.html_elt);var an=ai.length,ah=$("<a href='javascript:void(0);'/>").addClass("icon more-down").attr("title","For speed, only the first "+an+" features in this region were obtained from server. Click to get more data including depth").tipsy({gravity:"s"}).appendTo(message_div),ac=$("<a href='javascript:void(0);'/>").addClass("icon more-across").attr("title","For speed, only the first "+an+" features in this region were obtained from server. Click to get more data excluding depth").tipsy({gravity:"s"}).appendTo(message_div);ah.click(function(){al.stale=true;af.data_manager.get_more_data(al.low,al.high,af.mode,al.resolution,{},af.data_manager.DEEP_DATA_REQ);$(".tipsy").hide();af.request_draw(true)}).dblclick(function(aq){aq.stopPropagation()});ac.click(function(){al.stale=true;af.data_manager.get_more_data(al.low,al.high,af.mode,al.resolution,{},af.data_manager.BROAD_DATA_REQ);$(".tipsy").hide();af.request_draw(true)}).dblclick(function(aq){aq.stopPropagation()})}};p(M.prototype,b.prototype);M.prototype.predisplay_actions=function(){var ad=this,ac={};if(ad.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(ao){if(!this.hovered){return}var aj=$(this).offset(),an=ao.pageX-aj.left,am=ao.pageY-aj.top,at=ad.feature_mapper.get_feature_data(an,am),ak=(at?at[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!ak||$(this).attr("id")!==ak.toString()){$(this).remove()}});if(at){var af=ac[ak];if(!af){var ak=at[0],ap={name:at[3],start:at[1],end:at[2],strand:at[4]},ai=ad.track.filters_manager.filters,ah;for(var al=0;al<ai.length;al++){ah=ai[al];ap[ah.name]=at[ah.index]}var af=$("<div/>").attr("id",ak).addClass("feature-popup"),au=$("<table/>"),ar,aq,av;for(ar in ap){aq=ap[ar];av=$("<tr/>").appendTo(au);$("<th/>").appendTo(av).text(ar);$("<td/>").attr("align","left").appendTo(av).text(typeof(aq)==="number"?W(aq,2):aq)}af.append($("<div class='feature-popup-inner'>").append(au));ac[ak]=af}af.appendTo($(this).parents(".track-content").children(".overlay"));var ag=an+parseInt(ad.html_elt.css("left"))-af.width()/2,ae=am+parseInt(ad.html_elt.css("top"))+7;af.css("left",ag+"px").css("top",ae+"px")}else{if(!ao.isPropagationStopped()){ao.stopPropagation();$(this).siblings().each(function(){$(this).trigger(ao)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var i=function(ad,ac,ae){p(ae,{drag_handle_class:"draghandle"});q.call(this,ad,ac,ae);this.data_url=("data_url" in ae?ae.data_url:default_data_url);this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in ae?ae.data_query_wait:I);this.dataset_check_url=converted_datasets_state_url;this.data_manager=("data_manager" in ae?ae.data_manager:new P(F,this));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=0;this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in ae)||ae.resize){this.add_resize_handle()}}};p(i.prototype,q.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},q.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(ac){ac.view.set_overview(ac)}},q.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(ac){if(ac.filters_manager.visible()){ac.filters_manager.clear_filters()}else{ac.filters_manager.init_filters()}ac.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(ac){ac.dynamic_tool_div.toggle();if(ac.dynamic_tool_div.is(":visible")){ac.set_name(ac.name+ac.tool_region_and_parameters_str())}else{ac.revert_name()}$(".tipsy").remove()}},q.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&q.prototype.can_draw.call(this)){return true}return false},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var ac=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(ac)}this.name_div=$("<div/>").addClass("track-name").appendTo(ac).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return ac},on_resize:function(){},add_resize_handle:function(){var ac=this;var af=false;var ae=false;var ad=$("<div class='track-resize'>");$(ac.container_div).hover(function(){if(ac.content_visible){af=true;ad.show()}},function(){af=false;if(!ae){ad.hide()}});ad.hide().bind("dragstart",function(ag,ah){ae=true;ah.original_height=$(ac.content_div).height()}).bind("drag",function(ah,ai){var ag=Math.min(Math.max(ai.original_height+ai.deltaY,ac.min_height_px),ac.max_height_px);$(ac.tiles_div).css("height",ag);ac.visible_height_px=(ac.max_height_px===ag?0:ag);ac.on_resize()}).bind("dragend",function(ag,ah){ac.tile_cache.clear();ae=false;if(!af){ad.hide()}ac.config.values.height=ac.visible_height_px;ac.changed()}).appendTo(ac.container_div)},set_display_modes:function(af,ai){this.display_modes=af;this.mode=(ai?ai:(this.config&&this.config.values.mode?this.config.values.mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var ad=this,ag={};for(var ae=0,ac=ad.display_modes.length;ae<ac;ae++){var ah=ad.display_modes[ae];ag[ah]=function(aj){return function(){ad.change_mode(aj);ad.icons_div.show();ad.container_div.mouseleave(function(){ad.icons_div.hide()})}}(ah)}make_popupmenu(this.action_icons.mode_icon,ag)},build_action_icons:function(){q.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof Y){return"LabelTrack"}else{if(this instanceof y){return"ReferenceTrack"}else{if(this instanceof j){return"LineTrack"}else{if(this instanceof T){return"ReadTrack"}else{if(this instanceof Q){return"VcfTrack"}else{if(this instanceof h){return"CompositeTrack"}else{if(this instanceof d){return"FeatureTrack"}}}}}}}return""},init:function(){var ad=this;ad.enabled=false;ad.tile_cache.clear();ad.data_manager.clear();ad.content_div.css("height","auto");ad.tiles_div.children().remove();ad.container_div.removeClass("nodata error pending");if(!ad.dataset_id){return}var ac=$.Deferred();$.getJSON(this.dataset_check_url,{hda_ldda:ad.hda_ldda,dataset_id:ad.dataset_id,chrom:ad.view.chrom},function(ae){if(!ae||ae==="error"||ae.kind==="error"){ad.container_div.addClass("error");ad.tiles_div.text(o);if(ae.message){var af=$(" <a href='javascript:void(0);'></a>").text("View error").click(function(){show_modal("Trackster Error","<pre>"+ae.message+"</pre>",{Close:hide_modal})});ad.tiles_div.append(af)}}else{if(ae==="no converter"){ad.container_div.addClass("error");ad.tiles_div.text(H)}else{if(ae==="no data"||(ae.data!==undefined&&(ae.data===null||ae.data.length===0))){ad.container_div.addClass("nodata");ad.tiles_div.text(B)}else{if(ae==="pending"){ad.container_div.addClass("pending");ad.tiles_div.html(t);setTimeout(function(){ad.init()},ad.data_query_wait)}else{if(ae==="data"||ae.status==="data"){if(ae.valid_chroms){ad.valid_chroms=ae.valid_chroms;ad.update_icons()}ad.tiles_div.text(V);if(ad.view.chrom){ad.tiles_div.text("");ad.tiles_div.css("height",ad.visible_height_px+"px");ad.enabled=true;$.when(ad.predraw_init()).done(function(){ac.resolve();ad.container_div.removeClass("nodata error pending");ad.request_draw()})}else{ac.resolve()}}}}}}});this.update_icons();return ac},predraw_init:function(){}});var K=function(ae,ad,af){i.call(this,ae,ad,af);var ac=this,ae=ac.view;m(ac.container_div,ac.drag_handle_class,".group",ac);this.filters_manager=new X(this,("filters" in af?af.filters:null));this.filters_available=false;this.tool=("tool" in af&&af.tool?new r(this,af.tool,af.tool_state):null);this.tile_cache=new c(O);if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(af.mode){this.change_mode(af.mode)}};p(K.prototype,q.prototype,i.prototype,{action_icons_def:i.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(ac){$(".tipsy").remove();ac.slotters[ac.view.resolution_px_b].max_rows*=2;ac.request_draw(true)},hide:true}]),copy:function(ac){var ad=this.to_dict();p(ad,{data_manager:this.data_manager});var ae=new this.constructor(this.view,ac,ad);ae.change_mode(this.mode);ae.enabled=this.enabled;return ae},set_filters_manager:function(ac){this.filters_manager=ac;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),name:this.name,hda_ldda:this.hda_ldda,dataset_id:this.dataset_id,prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},change_mode:function(ad){var ac=this;ac.mode=ad;ac.config.values.mode=ad;ac.tile_cache.clear();ac.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+ac.mode+")");return ac},update_icons:function(){var ac=this;if(ac.filters_available){ac.action_icons.filters_icon.show()}else{ac.action_icons.filters_icon.hide()}if(ac.tool){ac.action_icons.tools_icon.show()}else{ac.action_icons.tools_icon.hide()}},_gen_tile_cache_key:function(ad,ae,ac){return ad+"_"+ae+"_"+ac},request_draw:function(ad,ac){this.view.request_redraw(false,ad,ac,this)},before_draw:function(){},_draw:function(ad,an){if(!this.can_draw()){return}var al=this.view.low,ah=this.view.high,aj=ah-al,ae=this.view.container.width(),ap=this.view.resolution_px_b,ag=this.view.resolution_b_px;if(this.is_overview){al=this.view.max_low;ah=this.view.max_high;ag=Math.pow(RESOLUTION,Math.ceil(Math.log((view.max_high-view.max_low)/R)/Math.log(RESOLUTION)));ap=ae/(view.max_high-view.max_low)}this.before_draw();this.tiles_div.children().addClass("remove");var ac=Math.floor(al/(ag*R)),ak=true,ao=[],ai=function(aq){return(aq&&"track" in aq)};while((ac*R*ag)<ah){var am=this.draw_helper(ad,ae,ac,ag,this.tiles_div,ap);if(ai(am)){ao.push(am)}else{ak=false}ac+=1}if(!an){this.tiles_div.children(".remove").removeClass("remove").remove()}var af=this;if(ak){this.tiles_div.children(".remove").remove();af.postdraw_actions(ao,ae,ap,an)}},postdraw_actions:function(af,ag,ai,ac){var ae=this;var ah=false;for(var ad=0;ad<af.length;ad++){if(af[ad].has_icons){ah=true;break}}if(ah){for(var ad=0;ad<af.length;ad++){tile=af[ad];if(!tile.has_icons){tile.html_elt.css("padding-top",A)}}}},draw_helper:function(ac,ao,au,ar,ah,ai,ap){var an=this,ax=this._gen_tile_cache_key(ao,ai,au),af=this._get_tile_bounds(au,ar),av=af[0],ad=af[1];if(!ap){ap={}}var aw=(ac?undefined:an.tile_cache.get(ax));if(aw){an.show_tile(aw,ah,ai);return aw}var al=true;var at=an.data_manager.get_data(av,ad,an.mode,ar,an.data_url_extra_params);if(is_deferred(at)){al=false}var aj;if(view.reference_track&&ai>view.canvas_manager.char_width_px){aj=view.reference_track.data_manager.get_data(av,ad,an.mode,ar,view.reference_track.data_url_extra_params);if(is_deferred(aj)){al=false}}if(al){p(at,ap.more_tile_data);var ak=an.mode;if(ak==="Auto"){ak=an.get_mode(at);an.update_auto_mode(ak)}var ae=an.view.canvas_manager.new_canvas(),af=an._get_tile_bounds(au,ar),av=af[0],ad=af[1],ao=Math.ceil((ad-av)*ai)+an.left_offset,am=an.get_canvas_height(at,ak,ai,ao);ae.width=ao;ae.height=am;var aq=ae.getContext("2d");aq.translate(this.left_offset,0);var aw=an.draw_tile(at,aq,ak,ar,au,ai,aj);if(aw!==undefined){an.tile_cache.set(ax,aw);an.show_tile(aw,ah,ai)}return aw}var ag=$.Deferred();$.when(at,aj).then(function(){view.request_redraw(false,false,false,an);ag.resolve()});return ag},get_canvas_height:function(ac,ae,af,ad){return this.visible_height_px},draw_tile:function(ac,ae,ah,ag,ad,ai,af){console.log("Warning: TiledTrack.draw_tile() not implemented.")},show_tile:function(ae,ag,ah){var ad=this,ac=ae.html_elt;ae.predisplay_actions();var af=(ae.low-(this.is_overview?this.view.max_low:this.view.low))*ah;if(this.left_offset){af-=this.left_offset}ac.css({position:"absolute",top:0,left:af});if(ac.hasClass("remove")){ac.removeClass("remove")}else{ag.append(ac)}ad.after_show_tile(ae)},after_show_tile:function(ac){},_get_tile_bounds:function(ac,ad){var af=Math.floor(ac*R*ad),ag=Math.ceil(R*ad),ae=(af+ag<=this.view.max_high?af+ag:this.view.max_high);return[af,ae]},tool_region_and_parameters_str:function(ae,ac,af){var ad=this,ag=(ae!==undefined&&ac!==undefined&&af!==undefined?ae+":"+ac+"-"+af:"all");return" - region=["+ag+"], parameters=["+ad.tool.get_param_values().join(", ")+"]"},data_and_mode_compatible:function(ac,ad){return true},can_subset:function(ac){return false},init_for_tool_data:function(){this.data_url=raw_data_url;this.data_query_wait=1000;this.dataset_check_url=dataset_state_url;this.normal_postdraw_actions=this.postdraw_actions;this.postdraw_actions=function(ae,af,ag,ac){var ad=this;ad.normal_postdraw_actions(ae,af,ag,ac);ad.dataset_state_url=converted_datasets_state_url;ad.data_query_wait=I;$.when(server_state_deferred(ad.dataset_state_url,{dataset_id:ad.dataset_id,hda_ldda:ad.hda_ldda},ad.data_query_wait,function(ah){return ah!=="pending"})).then(function(){ad.data_url=default_data_url});ad.postdraw_actions=ad.normal_postdraw_actions}}});var Y=function(ad,ac){var ae={resize:false};i.call(this,ad,ac,ae);this.container_div.addClass("label-track")};p(Y.prototype,i.prototype,{build_header_div:function(){},init:function(){this.enabled=true},_draw:function(){var ae=this.view,af=ae.high-ae.low,ai=Math.floor(Math.pow(10,Math.floor(Math.log(af)/Math.log(10)))),ac=Math.floor(ae.low/ai)*ai,ag=this.view.container.width(),ad=$("<div style='position: relative; height: 1.3em;'></div>");while(ac<ae.high){var ah=(ac-ae.low)/af*ag;ad.append($("<div class='label'>"+commatize(ac)+"</div>").css({position:"absolute",left:ah-1}));ac+=ai}this.content_div.children(":first").remove();this.content_div.append(ad)}});var h=function(ad,ac,ag){K.call(this,ad,ac,ag);this.drawables=[];this.left_offset=0;if("drawables" in ag){var af;for(var ae=0;ae<ag.drawables.length;ae++){af=ag.drawables[ae];this.drawables[ae]=object_from_template(af);if(af.left_offset>this.left_offset){this.left_offset=af.left_offset}}this.enabled=true}if(this.drawables.length!==0){this.set_display_modes(this.drawables[0].display_modes,this.drawables[0].mode)}this.update_icons();this.obj_type="CompositeTrack"};p(h.prototype,K.prototype,{action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(ac){$(".tipsy").remove();ac.show_group()}}].concat(K.prototype.action_icons_def),to_dict:w.prototype.to_dict,add_drawable:w.prototype.add_drawable,unpack_drawables:w.prototype.unpack_drawables,change_mode:function(ac){K.prototype.change_mode.call(this,ac);for(var ad=0;ad<this.drawables.length;ad++){this.drawables[ad].change_mode(ac)}},init:function(){var ae=[];for(var ad=0;ad<this.drawables.length;ad++){ae.push(this.drawables[ad].init())}var ac=this;$.when.apply($,ae).then(function(){ac.enabled=true;ac.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide()},can_draw:q.prototype.can_draw,draw_helper:function(ad,ar,ay,av,aj,al,at){var aq=this,aC=this._gen_tile_cache_key(ar,al,ay),ah=this._get_tile_bounds(ay,av),az=ah[0],ae=ah[1];if(!at){at={}}var aB=(ad?undefined:aq.tile_cache.get(aC));if(aB){aq.show_tile(aB,aj,al);return aB}var ak=[],aq,ao=true,aw,am;for(var ax=0;ax<this.drawables.length;ax++){aq=this.drawables[ax];aw=aq.data_manager.get_data(az,ae,aq.mode,av,aq.data_url_extra_params);if(is_deferred(aw)){ao=false}ak.push(aw);am=null;if(view.reference_track&&al>view.canvas_manager.char_width_px){am=view.reference_track.data_manager.get_data(az,ae,aq.mode,av,view.reference_track.data_url_extra_params);if(is_deferred(am)){ao=false}}ak.push(am)}if(ao){p(aw,at.more_tile_data);this.tile_predraw_init();var ag=aq.view.canvas_manager.new_canvas(),ah=aq._get_tile_bounds(ay,av),az=ah[0],ae=ah[1],aA=0,ar=Math.ceil((ae-az)*al)+this.left_offset,ap=0,af=[];var ac=0;for(var ax=0;ax<this.drawables.length;ax++,aA+=2){aq=this.drawables[ax];aw=ak[aA];var an=aq.mode;if(an==="Auto"){an=aq.get_mode(aw);aq.update_auto_mode(an)}af.push(an);ac=aq.get_canvas_height(aw,an,al,ar);if(ac>ap){ap=ac}}ag.width=ar;ag.height=(at.height?at.height:ap);aA=0;var au=ag.getContext("2d");au.translate(this.left_offset,0);au.globalAlpha=0.5;au.globalCompositeOperation="source-over";for(var ax=0;ax<this.drawables.length;ax++,aA+=2){aq=this.drawables[ax];aw=ak[aA];am=ak[aA+1];aB=aq.draw_tile(aw,au,af[ax],av,ay,al,am)}this.tile_cache.set(aC,aB);this.show_tile(aB,aj,al);return aB}var ai=$.Deferred(),aq=this;$.when.apply($,ak).then(function(){view.request_redraw(false,false,false,aq);ai.resolve()});return ai},show_group:function(){var af=new N(this.view,this.container,{name:this.name}),ac;for(var ae=0;ae<this.drawables.length;ae++){ac=this.drawables[ae];af.add_drawable(ac);ac.container=af;af.content_div.append(ac.container_div)}var ad=this.container.replace_drawable(this,af,true);af.request_draw()},tile_predraw_init:function(){var af=Number.MAX_VALUE,ac=-af,ad;for(var ae=0;ae<this.drawables.length;ae++){ad=this.drawables[ae];if(ad instanceof j){if(ad.prefs.min_value<af){af=ad.prefs.min_value}if(ad.prefs.max_value>ac){ac=ad.prefs.max_value}}}for(var ae=0;ae<this.drawables.length;ae++){ad=this.drawables[ae];ad.prefs.min_value=af;ad.prefs.max_value=ac}},postdraw_actions:function(ae,ah,aj,ad){K.prototype.postdraw_actions.call(this,ae,ah,aj,ad);var ag=-1;for(var af=0;af<ae.length;af++){var ac=ae[af].html_elt.find("canvas").height();if(ac>ag){ag=ac}}for(var af=0;af<ae.length;af++){var ai=ae[af];if(ai.html_elt.find("canvas").height()!==ag){this.draw_helper(true,ah,ai.index,ai.resolution,ai.html_elt.parent(),aj,{height:ag});ai.html_elt.remove()}}}});var y=function(ac){K.call(this,ac,{content_div:ac.top_labeltrack},{resize:false});ac.reference_track=this;this.left_offset=200;this.visible_height_px=12;this.container_div.addClass("reference-track");this.content_div.css("background","none");this.content_div.css("min-height","0px");this.content_div.css("border","none");this.data_url=reference_url;this.data_url_extra_params={dbkey:ac.dbkey};this.data_manager=new E(F,this,false);this.hide_contents()};p(y.prototype,q.prototype,K.prototype,{build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},can_draw:q.prototype.can_draw,draw_tile:function(ak,al,ah,ag,ad,am){var af=this;if(am>this.view.canvas_manager.char_width_px){if(ak.data===null){this.hide_contents();return}var ae=al.canvas;al.font=al.canvas.manager.default_font;al.textAlign="center";ak=ak.data;for(var ai=0,aj=ak.length;ai<aj;ai++){var ac=Math.floor(ai*am);al.fillText(ak[ai],ac,10)}this.show_contents();return new b(af,ad,ag,ae,ak)}this.hide_contents()}});var j=function(ae,ad,af){var ac=this;this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";K.call(this,ae,ad,af);this.hda_ldda=af.hda_ldda;this.dataset_id=af.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new C({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Color",type:"color",default_value:get_random_color()},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true}],saved_values:af.prefs,onchange:function(){ac.set_name(ac.prefs.name);ac.vertical_range=ac.prefs.max_value-ac.prefs.min_value;ac.set_min_value(ac.prefs.min_value);ac.set_max_value(ac.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};p(j.prototype,q.prototype,K.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(ac){this.prefs.min_value=ac;$("#linetrack_"+this.dataset_id+"_minval").text(this.prefs.min_value);this.tile_cache.clear();this.request_draw()},set_max_value:function(ac){this.prefs.max_value=ac;$("#linetrack_"+this.dataset_id+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.request_draw()},predraw_init:function(){var ac=this;ac.vertical_range=undefined;return $.getJSON(ac.data_url,{stats:true,chrom:ac.view.chrom,low:null,high:null,hda_ldda:ac.hda_ldda,dataset_id:ac.dataset_id},function(ad){ac.container_div.addClass("line-track");var ag=ad.data;if(isNaN(parseFloat(ac.prefs.min_value))||isNaN(parseFloat(ac.prefs.max_value))){var ae=ag.min;var ai=ag.max;ae=Math.floor(Math.min(0,Math.max(ae,ag.mean-2*ag.sd)));ai=Math.ceil(Math.max(0,Math.min(ai,ag.mean+2*ag.sd)));ac.prefs.min_value=ae;ac.prefs.max_value=ai;$("#track_"+ac.dataset_id+"_minval").val(ac.prefs.min_value);$("#track_"+ac.dataset_id+"_maxval").val(ac.prefs.max_value)}ac.vertical_range=ac.prefs.max_value-ac.prefs.min_value;ac.total_frequency=ag.total_frequency;ac.container_div.find(".yaxislabel").remove();var ah=$("<div/>").text(W(ac.prefs.min_value,3)).make_text_editable({num_cols:6,on_finish:function(aj){$(".tipsy").remove();var aj=parseFloat(aj);if(!isNaN(aj)){ac.set_min_value(aj)}},help_text:"Set min value"}).addClass("yaxislabel bottom").attr("id","linetrack_"+ac.dataset_id+"_minval").prependTo(ac.container_div),af=$("<div/>").text(W(ac.prefs.max_value,3)).make_text_editable({num_cols:6,on_finish:function(aj){$(".tipsy").remove();var aj=parseFloat(aj);if(!isNaN(aj)){ac.set_max_value(aj)}},help_text:"Set max value"}).addClass("yaxislabel top").attr("id","linetrack_"+ac.dataset_id+"_maxval").prependTo(ac.container_div)})},draw_tile:function(am,ak,ah,af,ad,al){var ae=ak.canvas,ac=this._get_tile_bounds(ad,af),ag=ac[0],aj=ac[1],ai=new J.LinePainter(am.data,ag,aj,this.prefs,ah);ai.draw(ak,ae.width,ae.height,al);return new b(this,ad,af,ae,am.data)},can_subset:function(ac){return false},});var d=function(af,ae,ah){var ad=this;this.display_modes=["Auto","Histogram","Dense","Squish","Pack"];K.call(this,af,ae,ah);var ag=get_random_color(),ac=get_random_color([ag,"#ffffff"]);this.config=new C({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:ag},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ac},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:this.visible_height_px,hidden:true}],saved_values:ah.prefs,onchange:function(){ad.set_name(ad.prefs.name);ad.tile_cache.clear();ad.set_painter_from_config();ad.request_draw()}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.container_div.addClass("feature-track");this.hda_ldda=ah.hda_ldda;this.dataset_id=ah.dataset_id;this.original_dataset_id=ah.dataset_id;this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};p(d.prototype,q.prototype,K.prototype,{set_painter_from_config:function(){if(this.config.values.connector_style==="arcs"){this.painter=J.ArcLinkedFeaturePainter}else{this.painter=J.LinkedFeaturePainter}},before_draw:function(){this.max_height_px=0},after_show_tile:function(ac){this.max_height_px=Math.max(this.max_height_px,ac.html_elt.height());ac.html_elt.parent().children().css("height",this.max_height_px+"px");var ad=this.max_height_px;if(this.visible_height_px!==0){ad=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",ad+"px")},postdraw_actions:function(ar,am,ah,ag){K.prototype.postdraw_actions.call(this,ar,ag);var al=this;if(al.mode==="Histogram"){var ad=-1;for(var ao=0;ao<ar.length;ao++){var an=ar[ao].max_val;if(an>ad){ad=an}}for(var ao=0;ao<ar.length;ao++){var au=ar[ao];if(au.max_val!==ad){au.html_elt.remove();al.draw_helper(true,am,au.index,au.resolution,au.html_elt.parent(),ah,{more_tile_data:{max:ad}})}}}if(al.filters_manager){var ai=al.filters_manager.filters;for(var aq=0;aq<ai.length;aq++){ai[aq].update_ui_elt()}var at=false,ac,aj;for(var ao=0;ao<ar.length;ao++){if(ar[ao].data.length){ac=ar[ao].data[0];for(var aq=0;aq<ai.length;aq++){aj=ai[aq];if(aj.applies_to(ac)&&aj.min!==aj.max){at=true;break}}}}if(al.filters_available!==at){al.filters_available=at;if(!al.filters_available){al.filters_manager.hide()}al.update_icons()}}this.container_div.find(".yaxislabel").remove();var af=ar[0];if(af instanceof k){var ak=(this.prefs.histogram_max?this.prefs.histogram_max:af.max_val),ae=$("<div/>").text(ak).make_text_editable({num_cols:12,on_finish:function(av){$(".tipsy").remove();var av=parseFloat(av);al.prefs.histogram_max=(!isNaN(av)?av:null);al.tile_cache.clear();al.request_draw()},help_text:"Set max value; leave blank to use default"}).addClass("yaxislabel top").css("color",this.prefs.label_color);this.container_div.prepend(ae)}if(af instanceof M){var ap=true;for(var ao=0;ao<ar.length;ao++){if(!ar[ao].all_slotted){ap=false;break}}if(!ap){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(ac){var ac;if(this.mode==="Auto"){if(ac==="no_detail"){ac="feature spans"}else{if(ac==="summary_tree"){ac="coverage histogram"}}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+ac+")")}},incremental_slots:function(ag,ac,af){var ad=this.view.canvas_manager.dummy_context,ae=this.slotters[ag];if(!ae||(ae.mode!==af)){ae=new (s.FeatureSlotter)(ag,af,x,function(ah){return ad.measureText(ah)});this.slotters[ag]=ae}return ae.slot_features(ac)},get_summary_tree_data:function(ag,aj,ae,ar){if(ar>ae-aj){ar=ae-aj}var an=Math.floor((ae-aj)/ar),aq=[],af=0;var ah=0,ai=0,am,ap=0,ak=[],ao,al;var ad=function(av,au,aw,at){av[0]=au+aw*at;av[1]=au+(aw+1)*at};while(ap<ar&&ah!==ag.length){var ac=false;for(;ap<ar&&!ac;ap++){ad(ak,aj,ap,an);for(ai=ah;ai<ag.length;ai++){am=ag[ai].slice(1,3);if(is_overlap(am,ak)){ac=true;break}}if(ac){break}}data_start_index=ai;aq[aq.length]=ao=[ak[0],0];for(;ai<ag.length;ai++){am=ag[ai].slice(1,3);if(is_overlap(am,ak)){ao[1]++}else{break}}if(ao[1]>af){af=ao[1]}ap++}return{max:af,delta:an,data:aq}},get_mode:function(ac){if(ac.dataset_type==="summary_tree"){mode="summary_tree"}else{if(ac.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>G){mode="Squish"}else{mode="Pack"}}}return mode},get_canvas_height:function(ac,ag,ah,ad){if(ag==="summary_tree"||ag==="Histogram"){return this.summary_draw_height}else{var af=this.incremental_slots(ah,ac.data,ag);var ae=new (this.painter)(null,null,null,this.prefs,ag);return Math.max(aa,ae.get_required_height(af,ad))}},draw_tile:function(an,ar,ap,at,ax,aj,ae){var aq=this,ad=ar.canvas,ag=this._get_tile_bounds(ax,at),aA=ag[0],ac=ag[1],aF=25,af=this.left_offset;if(ap==="summary_tree"||ap==="Histogram"){if(an.dataset_type!=="summary_tree"){var ak=this.get_summary_tree_data(an.data,aA,ac,200);if(an.max){ak.max=an.max}an=ak}var aC=new J.SummaryTreePainter(an,aA,ac,this.prefs);aC.draw(ar,ad.width,ad.height,aj);return new k(aq,ax,at,ad,an.data,an.max)}var ai=[],ao=this.slotters[aj].slots;all_slotted=true;if(an.data){var al=this.filters_manager.filters;for(var au=0,aw=an.data.length;au<aw;au++){var ah=an.data[au];var av=false;var am;for(var az=0,aE=al.length;az<aE;az++){am=al[az];am.update_attrs(ah);if(!am.keep(ah)){av=true;break}}if(!av){ai.push(ah);if(!(ah[0] in ao)){all_slotted=false}}}}var aD=(this.filters_manager.alpha_filter?new z(this.filters_manager.alpha_filter):null);var aB=(this.filters_manager.height_filter?new z(this.filters_manager.height_filter):null);var aC=new (this.painter)(ai,aA,ac,this.prefs,ap,aD,aB,ae);var ay=null;ar.fillStyle=this.prefs.block_color;ar.font=ar.canvas.manager.default_font;ar.textAlign="right";if(an.data){ay=aC.draw(ar,ad.width,ad.height,aj,ao);ay.translation=-af}return new M(aq,ax,at,ad,an.data,aj,ap,an.message,all_slotted,ay)},data_and_mode_compatible:function(ac,ad){if(ad==="Auto"){return true}else{if(ac.extra_info==="no_detail"||ac.dataset_type==="summary_tree"){return false}else{return true}}},can_subset:function(ac){if(ac.dataset_type==="summary_tree"||ac.message||ac.extra_info==="no_detail"){return false}return true},});var Q=function(ad,ac,ae){d.call(this,ad,ac,ae);this.config=new C({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:get_random_color()},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},],saved_values:ae.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=J.ReadPainter};p(Q.prototype,q.prototype,K.prototype,d.prototype);var T=function(ae,ad,ag){d.call(this,ae,ad,ag);var af=get_random_color(),ac=get_random_color([af,"#ffffff"]);this.config=new C({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:af},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:ac},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"Clear value to set automatically"},{key:"mode",type:"string",default_value:this.mode,hidden:true},],saved_values:ag.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=J.ReadPainter;this.update_icons()};p(T.prototype,q.prototype,K.prototype,d.prototype);U.View=Z;U.DrawableGroup=N;U.LineTrack=j;U.FeatureTrack=d;U.ReadTrack=T;U.VcfTrack=Q;U.CompositeTrack=h};var slotting_module=function(c,b){var e=c("class").extend;var d=2,a=5;b.FeatureSlotter=function(i,h,f,g){this.slots={};this.start_end_dct={};this.w_scale=i;this.mode=h;this.include_label=(h==="Pack");this.max_rows=f;this.measureText=g};e(b.FeatureSlotter.prototype,{slot_features:function(m){var p=this.w_scale,h=this.start_end_dct,x=[],z=[],n=0,y=this.max_rows;for(var v=0,w=m.length;v<w;v++){var k=m[v],o=k[0];if(this.slots[o]!==undefined){n=Math.max(n,this.slots[o]);z.push(this.slots[o])}else{x.push(v)}}var q=function(E,F){for(var D=0;D<=y;D++){var B=false,G=h[D];if(G!==undefined){for(var A=0,C=G.length;A<C;A++){var i=G[A];if(F>i[0]&&E<i[1]){B=true;break}}}if(!B){return D}}return -1};for(var v=0,w=x.length;v<w;v++){var k=m[x[v]],o=k[0],t=k[1],f=k[2],r=k[3],g=Math.floor(t*p),l=Math.ceil(f*p),u=this.measureText(r).width,j;if(r!==undefined&&this.include_label){u+=(d+a);if(g-u>=0){g-=u;j="left"}else{l+=u;j="right"}}var s=q(g,l);if(s>=0){if(h[s]===undefined){h[s]=[]}h[s].push([g,l]);this.slots[o]=s;n=Math.max(n,s)}}return n+1}})};var painters_module=function(i,x){var u=i("class").extend;var p=function(I,A,G,z,F,D){if(D===undefined){D=4}var C=z-A;var B=F-G;var E=Math.floor(Math.sqrt(C*C+B*B)/D);var J=C/E;var H=B/E;var y;for(y=0;y<E;y++,A+=J,G+=H){if(y%2!==0){continue}I.fillRect(A,G,D,1)}};var q=function(B,A,z,E){var D=A-E/2,C=A+E/2,F=z-Math.sqrt(E*3/2);B.beginPath();B.moveTo(D,F);B.lineTo(C,F);B.lineTo(A,z);B.lineTo(D,F);B.strokeStyle=this.fillStyle;B.fill();B.stroke();B.closePath()};var d=function(y){this.default_val=(y?y:1)};d.prototype.gen_val=function(y){return this.default_val};var l=function(A,C,y,z,B){this.data=A;this.view_start=C;this.view_end=y;this.prefs=u({},this.default_prefs,z);this.mode=B};l.prototype.default_prefs={};l.prototype.draw=function(z,A,y,B){};var v=function(A,C,y,z,B){l.call(this,A,C,y,z,B)};v.prototype.default_prefs={show_counts:false};v.prototype.draw=function(L,z,K,M){var E=this.view_start,N=this.view_end-this.view_start,I=this.data.data,G=(this.prefs.histogram_max?this.prefs.histogram_max:this.data.max),B=K;delta_x_px=Math.ceil(this.data.delta*M);L.save();for(var C=0,D=I.length;C<D;C++){var H=Math.floor((I[C][0]-E)*M);var F=I[C][1];if(!F){continue}var J=F/G*K;if(F!==0&&J<1){J=1}L.fillStyle=this.prefs.block_color;L.fillRect(H,B-J,delta_x_px,J);var A=4;if(this.prefs.show_counts&&(L.measureText(F).width+A)<delta_x_px){L.fillStyle=this.prefs.label_color;L.textAlign="center";L.fillText(F,H+(delta_x_px/2),10)}}L.restore()};var b=function(y,C,E,F,A){l.call(this,y,C,E,F,A);if(this.prefs.min_value===undefined){var G=Infinity;for(var z=0,B=this.data.length;z<B;z++){G=Math.min(G,this.data[z][1])}this.prefs.min_value=G}if(this.prefs.max_value===undefined){var D=-Infinity;for(var z=0,B=this.data.length;z<B;z++){D=Math.max(D,this.data[z][1])}this.prefs.max_value=D}};b.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Histogram",color:"#000",overflow_color:"#F66"};b.prototype.draw=function(S,Q,N,D){var I=false,K=this.prefs.min_value,F=this.prefs.max_value,M=F-K,B=N,C=this.view_start,P=this.view_end-this.view_start,L=this.mode,aa=this.data;S.save();var ac=Math.round(N+K/M*N);if(L!=="Intensity"){S.fillStyle="#aaa";S.fillRect(0,ac,Q,1)}S.beginPath();var Y,G,E;if(aa.length>1){E=Math.ceil((aa[1][0]-aa[0][0])*D)}else{E=10}var A=parseInt(this.prefs.color.slice(1),16),H=(A&16711680)>>16,R=(A&65280)>>8,V=A&255;for(var T=0,U=aa.length;T<U;T++){S.fillStyle=S.strokeStyle=this.prefs.color;Y=Math.round((aa[T][0]-C)*D);G=aa[T][1];var W=false,J=false;if(G===null){if(I&&L==="Filled"){S.lineTo(Y,B)}I=false;continue}if(G<K){J=true;G=K}else{if(G>F){W=true;G=F}}if(L==="Histogram"){G=Math.round(G/M*B);S.fillRect(Y,ac,E,-G)}else{if(L==="Intensity"){var z=(G-K)/M,O=Math.round(H+(255-H)*(1-z)),X=Math.round(R+(255-R)*(1-z)),ab=Math.round(V+(255-V)*(1-z));S.fillStyle="rgb("+O+","+X+","+ab+")";S.fillRect(Y,0,E,B)}else{G=Math.round(B-(G-K)/M*B);if(I){S.lineTo(Y,G)}else{I=true;if(L==="Filled"){S.moveTo(Y,B);S.lineTo(Y,G)}else{S.moveTo(Y,G)}}}}S.fillStyle=this.prefs.overflow_color;if(W||J){var Z;if(L==="Histogram"||L==="Intensity"){Z=E}else{Y-=2;Z=4}if(W){S.fillRect(Y,0,Z,3)}if(J){S.fillRect(Y,B-3,Z,3)}}S.fillStyle=this.prefs.color}if(L==="Filled"){if(I){S.lineTo(Y,ac);S.lineTo(0,ac)}S.fill()}else{S.stroke()}S.restore()};var m=function(y){this.feature_positions={};this.slot_height=y;this.translation=0;this.y_translation=0};m.prototype.map_feature_data=function(z,B,y,A){if(!this.feature_positions[B]){this.feature_positions[B]=[]}this.feature_positions[B].push({data:z,x_start:y,x_end:A})};m.prototype.get_feature_data=function(z,D){var C=Math.floor((D-this.y_translation)/this.slot_height),B;if(!this.feature_positions[C]){return null}z+=this.translation;for(var A=0;A<this.feature_positions[C].length;A++){B=this.feature_positions[C][A];if(z>=B.x_start&&z<=B.x_end){return B.data}}};var o=function(A,D,y,z,C,E,B){l.call(this,A,D,y,z,C);this.alpha_scaler=(E?E:new d());this.height_scaler=(B?B:new d())};o.prototype.default_prefs={block_color:"#FFF",connector_color:"#FFF"};u(o.prototype,{get_required_height:function(A,z){var y=y_scale=this.get_row_height(),B=this.mode;if(B==="no_detail"||B==="Squish"||B==="Pack"){y=A*y_scale}return y+this.get_top_padding(z)+this.get_bottom_padding(z)},get_top_padding:function(y){return 0},get_bottom_padding:function(y){return Math.max(Math.round(this.get_row_height()/2),5)},draw:function(K,I,G,E,F){var Q=this.data,D=this.view_start,M=this.view_end;K.save();K.fillStyle=this.prefs.block_color;K.textAlign="right";var H=this.view_end-this.view_start,L=this.get_row_height(),P=new m(L),B;for(var N=0,O=Q.length;N<O;N++){var A=Q[N],C=A[0],J=A[1],y=A[2],z=(F&&F[C]!==undefined?F[C]:null);if((J<M&&y>D)&&(this.mode==="Dense"||z!==null)){B=this.draw_element(K,this.mode,A,z,D,M,E,L,I);P.map_feature_data(A,z,B[0],B[1])}}K.restore();P.y_translation=this.get_top_padding(I);return P},draw_element:function(E,A,G,C,B,D,F,z,y){console.log("WARNING: Unimplemented function.");return[0,0]}});var c=10,h=3,k=5,w=10,f=1,s=9,e=3,a=9,j=2,g="#ccc";var r=function(A,D,y,z,C,E,B){o.call(this,A,D,y,z,C,E,B);this.draw_background_connector=true;this.draw_individual_connectors=false};u(r.prototype,o.prototype,{get_row_height:function(){var z=this.mode,y;if(z==="Dense"){y=c}else{if(z==="no_detail"){y=h}else{if(z==="Squish"){y=k}else{y=w}}}return y},draw_element:function(M,D,X,H,O,ai,am,ao,y){var T=X[0],ak=X[1],ad=X[2]-1,Q=X[3],K=X[4],ae=Math.floor(Math.max(0,(ak-O)*am)),N=Math.ceil(Math.min(y,Math.max(0,(ad-O)*am))),ac=ae,an=N,aa=(D==="Dense"?0:(0+H))*ao+this.get_top_padding(y),L,ag,R=null,aq=null,B=B=(!K||K==="+"||K==="."?this.prefs.block_color:this.prefs.reverse_strand_color);label_color=this.prefs.label_color;M.globalAlpha=this.alpha_scaler.gen_val(X);if(D==="Dense"){H=1}if(D==="no_detail"){M.fillStyle=B;M.fillRect(ae,aa+5,N-ae,f)}else{var Z=X[5],af=X[6],C=X[7],V=true;if(Z&&af){R=Math.floor(Math.max(0,(Z-O)*am));aq=Math.ceil(Math.min(y,Math.max(0,(af-O)*am)))}var al,U;if(D==="Squish"){al=1;U=e;V=false}else{if(D==="Dense"){al=5;U=s}else{al=5;U=a}}if(!C){M.fillStyle=B;M.fillRect(ae,aa+1,N-ae,U);if(K&&V){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand_inv")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand_inv")}}M.fillRect(ae,aa+1,N-ae,U)}}else{var J,W;if(D==="Squish"||D==="Dense"){J=aa+Math.floor(e/2)+1;W=1}else{if(K){J=aa;W=U}else{J+=(e/2)+1;W=1}}if(this.draw_background_connector){if(D==="Squish"||D==="Dense"){M.fillStyle=g}else{if(K){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand")}}}else{M.fillStyle=g}}M.fillRect(ae,J,N-ae,W)}var E;for(var aj=0,A=C.length;aj<A;aj++){var F=C[aj],z=Math.floor(Math.max(0,(F[0]-O)*am)),Y=Math.ceil(Math.min(y,Math.max((F[1]-1-O)*am))),S,ab;if(z>Y){continue}M.fillStyle=B;M.fillRect(z,aa+(U-al)/2+1,Y-z,al);if(R!==undefined&&af>Z&&!(z>aq||Y<R)){var ah=Math.max(z,R),I=Math.min(Y,aq);M.fillRect(ah,aa+1,I-ah,U)}if(this.draw_individual_connectors&&S){this.draw_connector(M,S,ab,z,Y,aa)}S=z;ab=Y}if(D==="Pack"){M.globalAlpha=1;M.fillStyle="white";var G=this.height_scaler.gen_val(X),P=Math.ceil(U*G),ap=Math.round((U-P)/2);if(G!==1){M.fillRect(ae,J+1,N-ae,ap);M.fillRect(ae,J+U-ap+1,N-ae,ap)}}}M.globalAlpha=1;if(D==="Pack"&&ak>O){M.fillStyle=label_color;if(O===0&&ae-M.measureText(Q).width<0){M.textAlign="left";M.fillText(Q,N+j,aa+8);an+=M.measureText(Q).width+j}else{M.textAlign="right";M.fillText(Q,ae-j,aa+8);ac-=M.measureText(Q).width+j}}}M.globalAlpha=1;return[ac,an]}});var t=function(B,E,y,A,D,F,C,z){o.call(this,B,E,y,A,D,F,C);this.ref_seq=(z?z.data:null)};u(t.prototype,o.prototype,{get_row_height:function(){var y,z=this.mode;if(z==="Dense"){y=c}else{if(z==="Squish"){y=k}else{y=w;if(this.prefs.show_insertions){y*=2}}}return y},draw_read:function(K,A,ag,V,L,aa,ad,C,B,M){K.textAlign="center";var J=this,R=[L,aa],Z=0,W=0,D=0,F=K.canvas.manager.char_width_px,y=(B==="+"?this.prefs.block_color:this.prefs.reverse_strand_color);var O=[];if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){D=Math.round(ag/2)}if(!C){C=[[0,M.length]]}for(var G=0,I=C.length;G<I;G++){var z=C[G],E="MIDNSHP=X"[z[0]],S=z[1];if(E==="H"||E==="S"){Z-=S}var U=ad+Z,Y=Math.floor(Math.max(0,(U-L)*ag)),ab=Math.floor(Math.max(0,(U+S-L)*ag));if(Y===ab){ab+=1}switch(E){case"H":break;case"S":case"M":case"=":if(is_overlap([U,U+S],R)){var N=M.slice(W,W+S);if(D>0){K.fillStyle=y;K.fillRect(Y-D,V+1,ab-Y,9);K.fillStyle=g;for(var af=0,H=N.length;af<H;af++){if(this.prefs.show_differences){if(this.ref_seq){var P=this.ref_seq[U-L+af];if(!P||P.toLowerCase()===N[af].toLowerCase()){continue}}else{continue}}if(U+af>=L&&U+af<=aa){var X=Math.floor(Math.max(0,(U+af-L)*ag));K.fillText(N[af],X,V+9)}}}else{K.fillStyle=y;K.fillRect(Y,V+4,ab-Y,e)}}W+=S;Z+=S;break;case"N":K.fillStyle=g;K.fillRect(Y-D,V+5,ab-Y,1);Z+=S;break;case"D":K.fillStyle="red";K.fillRect(Y-D,V+4,ab-Y,3);Z+=S;break;case"P":break;case"I":var ah=Y-D;if(is_overlap([U,U+S],R)){var N=M.slice(W,W+S);if(this.prefs.show_insertions){var T=Y-(ab-Y)/2;if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){K.fillStyle="yellow";K.fillRect(T-D,V-9,ab-Y,9);O[O.length]={type:"triangle",data:[ah,V+4,5]};K.fillStyle=g;switch(compute_overlap([U,U+S],R)){case (OVERLAP_START):N=N.slice(L-U);break;case (OVERLAP_END):N=N.slice(0,U-aa);break;case (CONTAINED_BY):break;case (CONTAINS):N=N.slice(L-U,U-aa);break}for(var af=0,H=N.length;af<H;af++){var X=Math.floor(Math.max(0,(U+af-L)*ag));K.fillText(N[af],X-(ab-Y)/2,V)}}else{K.fillStyle="yellow";K.fillRect(T,V+(this.mode!=="Dense"?2:5),ab-Y,(A!=="Dense"?e:s))}}else{if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){O.push({type:"text",data:[N.length,ah,V+9]})}else{}}}W+=S;break;case"X":W+=S;break}}K.fillStyle="yellow";var Q,ai,ae;for(var ac=0;ac<O.length;ac++){Q=O[ac];ai=Q.type;ae=Q.data;if(ai==="text"){K.save();K.font="bold "+K.font;K.fillText(ae[0],ae[1],ae[2]);K.restore()}else{if(ai==="triangle"){q(K,ae[0],ae[1],ae[2])}}}},draw_element:function(R,M,E,B,U,z,I,S,P){var H=E[0],Q=E[1],A=E[2],J=E[3],D=Math.floor(Math.max(0,(Q-U)*I)),F=Math.ceil(Math.min(P,Math.max(0,(A-U)*I))),C=(M==="Dense"?0:(0+B))*S,G=this.prefs.label_color,O=0;if((M==="Pack"||this.mode==="Auto")&&I>R.canvas.manager.char_width_px){var O=Math.round(I/2)}if(E[5] instanceof Array){var N=Math.floor(Math.max(0,(E[4][0]-U)*I)),L=Math.ceil(Math.min(P,Math.max(0,(E[4][1]-U)*I))),K=Math.floor(Math.max(0,(E[5][0]-U)*I)),y=Math.ceil(Math.min(P,Math.max(0,(E[5][1]-U)*I)));if(E[4][1]>=U&&E[4][0]<=z&&E[4][2]){this.draw_read(R,M,I,C,U,z,E[4][0],E[4][2],E[4][3],E[4][4])}if(E[5][1]>=U&&E[5][0]<=z&&E[5][2]){this.draw_read(R,M,I,C,U,z,E[5][0],E[5][2],E[5][3],E[5][4])}if(K>L){R.fillStyle=g;p(R,L-O,C+5,K-O,C+5)}}else{this.draw_read(R,M,I,C,U,z,Q,E[4],E[5],E[6])}if(M==="Pack"&&Q>U&&J!=="."){R.fillStyle=this.prefs.label_color;var T=1;if(T===0&&D-R.measureText(J).width<0){R.textAlign="left";R.fillText(J,F+j-O,C+8)}else{R.textAlign="right";R.fillText(J,D-j-O,C+8)}}return[0,0]}});var n=function(A,D,y,z,C,E,B){r.call(this,A,D,y,z,C,E,B);this.longest_feature_length=this.calculate_longest_feature_length();this.draw_background_connector=false;this.draw_individual_connectors=true};u(n.prototype,o.prototype,r.prototype,{calculate_longest_feature_length:function(){var z=0;for(var C=0,y=this.data.length;C<y;C++){var B=this.data[C],A=B[1],D=B[2];z=Math.max(z,D-A)}return z},get_top_padding:function(z){var y=this.view_end-this.view_start,A=z/y;return Math.min(128,Math.ceil((this.longest_feature_length/2)*A))},draw_connector:function(G,B,F,H,E,D){var y=(F+H)/2,C=H-y;var A=Math.PI,z=0;if(C>0){G.beginPath();G.arc(y,D,H-y,Math.PI,0);G.stroke()}}});x.Scaler=d;x.SummaryTreePainter=v;x.LinePainter=b;x.LinkedFeaturePainter=r;x.ReadPainter=t;x.ArcLinkedFeaturePainter=n};(function(d){var c={};var b=function(e){return c[e]};var a=function(f,g){var e={};g(b,e);c[f]=e};a("class",class_module);a("slotting",slotting_module);a("painters",painters_module);a("trackster",trackster_module);for(key in c.trackster){d[key]=c.trackster[key]}})(window);
\ No newline at end of file
+var class_module=function(b,a){var c=function(){var f=arguments[0];for(var e=1;e<arguments.length;e++){var d=arguments[e];for(key in d){f[key]=d[key]}}return f};a.extend=c};var server_state_deferred=function(d,a,c,f){var b=$.Deferred(),e=function(){$.getJSON(d,a,function(g){if(f(g)){b.resolve(g)}else{setTimeout(e,c)}})};e();return b};var requestAnimationFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(b,a){window.setTimeout(b,1000/60)}})();var BEFORE=1001,CONTAINS=1002,OVERLAP_START=1003,OVERLAP_END=1004,CONTAINED_BY=1005,AFTER=1006;var compute_overlap=function(e,b){var g=e[0],f=e[1],d=b[0],c=b[1],a;if(g<d){if(f<d){a=BEFORE}else{if(f<=c){a=OVERLAP_START}else{a=CONTAINS}}}else{if(g>c){a=AFTER}else{if(f<=c){a=CONTAINED_BY}else{a=OVERLAP_END}}}return a};var is_overlap=function(c,b){var a=compute_overlap(c,b);return(a!==BEFORE&&a!==AFTER)};var is_deferred=function(a){return("isResolved" in a)};var get_random_color=function(a){if(!a){a="#ffffff"}if(typeof(a)==="string"){a=[a]}for(var j=0;j<a.length;j++){a[j]=parseInt(a[j].slice(1),16)}var n=function(t,s,i){return((t*299)+(s*587)+(i*114))/1000};var e=function(v,u,w,s,i,t){return(Math.max(v,s)-Math.min(v,s))+(Math.max(u,i)-Math.min(u,i))+(Math.max(w,t)-Math.min(w,t))};var g,o,f,k,q,h,r,c,d,b,p,m=false,l=0;do{g=Math.round(Math.random()*16777215);o=(g&16711680)>>16;f=(g&65280)>>8;k=g&255;d=n(o,f,k);m=true;for(var j=0;j<a.length;j++){q=a[j];h=(q&16711680)>>16;r=(q&65280)>>8;c=q&255;b=n(h,r,c);p=e(o,f,k,h,r,c);if((Math.abs(d-b)<40)||(p<200)){m=false;break}}l++}while(!m&&l<=10);return"#"+(16777216+g).toString(16).substr(1,6)};var create_action_icon=function(c,b,a){return $("<a/>").attr("href","javascript:void(0);").attr("title",c).addClass("icon-button").addClass(b).tipsy({gravity:"s"}).click(a)};var trackster_module=function(d,R){var o=d("class").extend,r=d("slotting"),H=d("painters");var Y=function(Z,aa){this.document=Z;this.default_font=aa!==undefined?aa:"9px Monaco, Lucida Console, monospace";this.dummy_canvas=this.new_canvas();this.dummy_context=this.dummy_canvas.getContext("2d");this.dummy_context.font=this.default_font;this.char_width_px=this.dummy_context.measureText("A").width;this.patterns={};this.load_pattern("right_strand","/visualization/strand_right.png");this.load_pattern("left_strand","/visualization/strand_left.png");this.load_pattern("right_strand_inv","/visualization/strand_right_inv.png");this.load_pattern("left_strand_inv","/visualization/strand_left_inv.png")};o(Y.prototype,{load_pattern:function(Z,ad){var aa=this.patterns,ab=this.dummy_context,ac=new Image();ac.src=galaxy_paths.attributes.image_path+ad;ac.onload=function(){aa[Z]=ab.createPattern(ac,"repeat")}},get_pattern:function(Z){return this.patterns[Z]},new_canvas:function(){var Z=this.document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(Z)}Z.manager=this;return Z}});var m={};var k=function(Z,aa){m[Z.attr("id")]=aa};var l=function(Z,ab,ad,ac){ad=".group";var aa={};m[Z.attr("id")]=ac;Z.bind("drag",{handle:"."+ab,relative:true},function(al,am){var ak=$(this);var ap=$(this).parent(),ah=ap.children(),aj=m[$(this).attr("id")],ag,af,an,ae,ai;af=$(this).parents(ad);if(af.length!==0){an=af.position().top;ae=an+af.outerHeight();if(am.offsetY<an){$(this).insertBefore(af);var ao=m[af.attr("id")];ao.remove_drawable(aj);ao.container.add_drawable_before(aj,ao);return}else{if(am.offsetY>ae){$(this).insertAfter(af);var ao=m[af.attr("id")];ao.remove_drawable(aj);ao.container.add_drawable(aj);return}}}af=null;for(ai=0;ai<ah.length;ai++){ag=$(ah.get(ai));an=ag.position().top;ae=an+ag.outerHeight();if(ag.is(ad)&&this!==ag.get(0)&&am.offsetY>=an&&am.offsetY<=ae){if(am.offsetY-an<ae-am.offsetY){ag.find(".content-div").prepend(this)}else{ag.find(".content-div").append(this)}if(aj.container){aj.container.remove_drawable(aj)}m[ag.attr("id")].add_drawable(aj);return}}var ag;for(ai=0;ai<ah.length;ai++){ag=$(ah.get(ai));if(am.offsetY<ag.position().top&&!(ag.hasClass("reference-track")||ag.hasClass("intro"))){break}}if(ai===ah.length){if(this!==ah.get(ai-1)){ap.append(this);m[ap.attr("id")].move_drawable(aj,ai)}}else{if(this!==ah.get(ai)){$(this).insertBefore(ah.get(ai));m[ap.attr("id")].move_drawable(aj,(am.deltaY>0?ai-1:ai))}}}).bind("dragstart",function(){aa["border-top"]=Z.css("border-top");aa["border-bottom"]=Z.css("border-bottom");$(this).css({"border-top":"1px solid blue","border-bottom":"1px solid blue"})}).bind("dragend",function(){$(this).css(aa)})};R.moveable=l;var X=16,C=9,z=20,w=100,E=12000,O=400,G=5000,t=100,n="There was an error in indexing this dataset. ",F="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",A="No data for this chrom/contig.",s="Preparing data. This can take a while for a large dataset. If the visualization is saved and closed, preparation will continue in the background.",u="Tool cannot be rerun: ",a="Loading data...",S="Ready for display",M=10,D=20;function T(aa,Z){if(!Z){Z=0}var ab=Math.pow(10,Z);return Math.round(aa*ab)/ab}var p=function(aa,Z,ac){if(!p.id_counter){p.id_counter=0}this.id=p.id_counter++;this.name=ac.name;this.view=aa;this.container=Z;this.config=new B({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name}],saved_values:ac.prefs,onchange:function(){this.track.set_name(this.track.config.values.name)}});this.prefs=this.config.values;this.drag_handle_class=ac.drag_handle_class;this.is_overview=false;this.action_icons={};this.content_visible=true;this.container_div=this.build_container_div();this.header_div=this.build_header_div();if(this.header_div){this.container_div.append(this.header_div);this.icons_div=$("<div/>").css("float","left").hide().appendTo(this.header_div);this.build_action_icons(this.action_icons_def);this.header_div.append($("<div style='clear: both'/>"));this.header_div.dblclick(function(ad){ad.stopPropagation()});var ab=this;this.container_div.hover(function(){ab.icons_div.show()},function(){ab.icons_div.hide()});$("<div style='clear: both'/>").appendTo(this.container_div)}};p.prototype.action_icons_def=[{name:"toggle_icon",title:"Hide/show content",css_class:"toggle",on_click_fn:function(Z){if(Z.content_visible){Z.action_icons.toggle_icon.addClass("toggle-expand").removeClass("toggle");Z.hide_contents();Z.content_visible=false}else{Z.action_icons.toggle_icon.addClass("toggle").removeClass("toggle-expand");Z.content_visible=true;Z.show_contents()}}},{name:"settings_icon",title:"Edit settings",css_class:"settings-icon",on_click_fn:function(aa){var ac=function(){hide_modal();$(window).unbind("keypress.check_enter_esc")},Z=function(){aa.config.update_from_form($(".dialog-box"));hide_modal();$(window).unbind("keypress.check_enter_esc")},ab=function(ad){if((ad.keyCode||ad.which)===27){ac()}else{if((ad.keyCode||ad.which)===13){Z()}}};$(window).bind("keypress.check_enter_esc",ab);show_modal("Configure",aa.config.build_form(),{Cancel:ac,OK:Z})}},{name:"remove_icon",title:"Remove",css_class:"remove-icon",on_click_fn:function(Z){$(".tipsy").remove();Z.remove()}}];o(p.prototype,{init:function(){},changed:function(){this.view.changed()},can_draw:function(){if(this.enabled&&this.content_visible){return true}return false},request_draw:function(){},_draw:function(){},to_dict:function(){},update_icons:function(){},set_name:function(Z){this.old_name=this.name;this.name=Z;this.name_div.text(this.name)},revert_name:function(){if(this.old_name){this.name=this.old_name;this.name_div.text(this.name)}},remove:function(){this.changed();this.container.remove_drawable(this);var Z=this.view;this.container_div.hide(0,function(){$(this).remove();Z.update_intro_div()})},build_container_div:function(){},build_header_div:function(){},add_action_icon:function(aa,af,ae,ad,Z,ac){var ab=this;this.action_icons[aa]=$("<a/>").attr("href","javascript:void(0);").attr("title",af).addClass("icon-button").addClass(ae).tipsy({gravity:"s"}).click(function(){ad(ab)}).appendTo(this.icons_div);if(ac){this.action_icons[aa].hide()}},build_action_icons:function(Z){var ab;for(var aa=0;aa<Z.length;aa++){ab=Z[aa];this.add_action_icon(ab.name,ab.title,ab.css_class,ab.on_click_fn,ab.prepend,ab.hide)}},update_icons:function(){},hide_contents:function(){},show_contents:function(){}});var v=function(aa,Z,ab){p.call(this,aa,Z,ab);this.obj_type=ab.obj_type;this.drawables=[]};o(v.prototype,p.prototype,{unpack_drawables:function(ab){this.drawables=[];var aa;for(var Z=0;Z<ab.length;Z++){aa=object_from_template(ab[Z],this);this.add_drawable(aa)}},init:function(){for(var Z=0;Z<this.drawables.length;Z++){this.drawables[Z].init()}},_draw:function(){for(var Z=0;Z<this.drawables.length;Z++){this.drawables[Z]._draw()}},to_dict:function(){var aa=[];for(var Z=0;Z<this.drawables.length;Z++){aa.push(this.drawables[Z].to_dict())}return{name:this.name,prefs:this.prefs,obj_type:this.obj_type,drawables:aa}},add_drawable:function(Z){this.drawables.push(Z);Z.container=this;this.changed()},add_drawable_before:function(ab,Z){this.changed();var aa=this.drawables.indexOf(Z);if(aa!==-1){this.drawables.splice(aa,0,ab);return true}return false},replace_drawable:function(ab,Z,aa){var ac=this.drawables.indexOf(ab);if(ac!==-1){this.drawables[ac]=Z;if(aa){ab.container_div.replaceWith(Z.container_div)}this.changed()}return ac},remove_drawable:function(aa){var Z=this.drawables.indexOf(aa);if(Z!==-1){this.drawables.splice(Z,1);aa.container=null;this.changed();return true}return false},move_drawable:function(aa,ab){var Z=this.drawables.indexOf(aa);if(Z!==-1){this.drawables.splice(Z,1);this.drawables.splice(ab,0,aa);this.changed();return true}return false}});var L=function(aa,Z,ac){o(ac,{obj_type:"DrawableGroup",drag_handle_class:"group-handle"});v.call(this,aa,Z,ac);this.content_div=$("<div/>").addClass("content-div").attr("id","group_"+this.id+"_content_div").appendTo(this.container_div);k(this.container_div,this);k(this.content_div,this);l(this.container_div,this.drag_handle_class,".group",this);this.filters_manager=new U(this);this.header_div.after(this.filters_manager.parent_div);this.saved_filters_managers=[];if("drawables" in ac){this.unpack_drawables(ac.drawables)}if("filters" in ac){var ab=this.filters_manager;this.filters_manager=new U(this,ac.filters);ab.parent_div.replaceWith(this.filters_manager.parent_div);if(ac.filters.visible){this.setup_multitrack_filtering()}}};o(L.prototype,p.prototype,v.prototype,{action_icons_def:[p.prototype.action_icons_def[0],p.prototype.action_icons_def[1],{name:"composite_icon",title:"Show composite track",css_class:"layers-stack",on_click_fn:function(Z){$(".tipsy").remove();Z.show_composite_track()}},{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(Z){if(Z.filters_manager.visible()){Z.filters_manager.clear_filters();Z._restore_filter_managers()}else{Z.setup_multitrack_filtering();Z.request_draw(true)}Z.filters_manager.toggle()}},p.prototype.action_icons_def[2]],build_container_div:function(){var Z=$("<div/>").addClass("group").attr("id","group_"+this.id);if(this.container){this.container.content_div.append(Z)}return Z},build_header_div:function(){var Z=$("<div/>").addClass("track-header");Z.append($("<div/>").addClass(this.drag_handle_class));this.name_div=$("<div/>").addClass("track-name").text(this.name).appendTo(Z);return Z},hide_contents:function(){this.tiles_div.hide()},show_contents:function(){this.tiles_div.show();this.request_draw()},update_icons:function(){var ab=this.drawables.length;if(ab===0){this.action_icons.composite_icon.hide();this.action_icons.filters_icon.hide()}else{if(ab===1){if(this.drawables[0] instanceof g){this.action_icons.composite_icon.show()}this.action_icons.filters_icon.hide()}else{var af,al=true,ad=this.drawables[0].get_type(),Z=0;for(var ai=0;ai<ab;ai++){af=this.drawables[ai];if(af.get_type()!==ad){can_composite=false;break}if(af instanceof c){Z++}}if(al||Z===1){this.action_icons.composite_icon.show()}else{this.action_icons.composite_icon.hide();$(".tipsy").remove()}if(Z>1&&Z===this.drawables.length){var am={},aa;af=this.drawables[0];for(var ah=0;ah<af.filters_manager.filters.length;ah++){aa=af.filters_manager.filters[ah];am[aa.name]=[aa]}for(var ai=1;ai<this.drawables.length;ai++){af=this.drawables[ai];for(var ah=0;ah<af.filters_manager.filters.length;ah++){aa=af.filters_manager.filters[ah];if(aa.name in am){am[aa.name].push(aa)}}}this.filters_manager.remove_all();var ac,ae,ag,aj;for(var ak in am){ac=am[ak];if(ac.length===Z){ae=new P({name:ac[0].name,index:ac[0].index});this.filters_manager.add_filter(ae)}}if(this.filters_manager.filters.length>0){this.action_icons.filters_icon.show()}else{this.action_icons.filters_icon.hide()}}else{this.action_icons.filters_icon.hide()}}}},_restore_filter_managers:function(){for(var Z=0;Z<this.drawables.length;Z++){this.drawables[Z].filters_manager=this.saved_filters_managers[Z]}this.saved_filters_managers=[]},setup_multitrack_filtering:function(){if(this.filters_manager.filters.length>0){this.saved_filters_managers=[];for(var Z=0;Z<this.drawables.length;Z++){drawable=this.drawables[Z];this.saved_filters_managers.push(drawable.filters_manager);drawable.filters_manager=this.filters_manager}}this.filters_manager.init_filters()},show_composite_track:function(){var ad=[];for(var aa=0;aa<this.drawables.length;aa++){ad.push(this.drawables[aa].name)}var ab="Composite Track of "+this.drawables.length+" tracks ("+ad.join(", ")+")";var ac=new g(this.view,this.view,{name:ab,drawables:this.drawables});var Z=this.container.replace_drawable(this,ac,true);ac.request_draw()},add_drawable:function(Z){v.prototype.add_drawable.call(this,Z);this.update_icons()},remove_drawable:function(Z){v.prototype.remove_drawable.call(this,Z);this.update_icons()},to_dict:function(){if(this.filters_manager.visible()){this._restore_filter_managers()}var Z=o(v.prototype.to_dict.call(this),{filters:this.filters_manager.to_dict()});if(this.filters_manager.visible()){this.setup_multitrack_filtering()}return Z},request_draw:function(Z,ab){for(var aa=0;aa<this.drawables.length;aa++){this.drawables[aa].request_draw(Z,ab)}}});var W=function(Z){o(Z,{obj_type:"View"});v.call(this,"View",Z.container,Z);this.chrom=null;this.vis_id=Z.vis_id;this.dbkey=Z.dbkey;this.label_tracks=[];this.tracks_to_be_redrawn=[];this.max_low=0;this.max_high=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.load_chroms_deferred=null;this.init();this.canvas_manager=new Y(this.container.get(0).ownerDocument);this.reset()};_.extend(W.prototype,Backbone.Events);o(W.prototype,v.prototype,{init:function(){this.requested_redraw=false;var ab=this.container,Z=this;this.top_container=$("<div/>").addClass("top-container").appendTo(ab);this.browser_content_div=$("<div/>").addClass("content").css("position","relative").appendTo(ab);this.bottom_container=$("<div/>").addClass("bottom-container").appendTo(ab);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("<div/>").addClass("viewport-container").attr("id","viewport-container").appendTo(this.browser_content_div);this.content_div=this.viewport_container;k(this.viewport_container,Z);this.intro_div=$("<div/>").addClass("intro").appendTo(this.viewport_container).hide();var ac=$("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function(){add_datasets(add_datasets_url,add_track_async_url,function(ad){_.each(ad,function(ae){Z.add_drawable(object_from_template(ae,Z))})})});this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("<div/>").addClass("trackster-nav-container").prependTo(this.top_container);this.nav=$("<div/>").addClass("trackster-nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("<a/>").attr("href","javascript:void(0);").attr("title","Close overview").addClass("icon-button overview-close tooltip").hide().appendTo(this.overview_viewport);this.overview_highlight=$("<div/>").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("<div/>").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.nav_controls);var aa=function(ad){if(ad.type==="focusout"||(ad.keyCode||ad.which)===13||(ad.keyCode||ad.which)===27){if((ad.keyCode||ad.which)!==27){Z.go_to($(this).val())}$(this).hide();$(this).val("");Z.location_span.show();Z.chrom_select.show()}};this.nav_input=$("<input/>").addClass("nav-input").hide().bind("keyup focusout",aa).appendTo(this.nav_controls);this.location_span=$("<span/>").addClass("location").attr("original-title","Click to change location").tipsy({gravity:"n"}).appendTo(this.nav_controls);this.location_span.click(function(){Z.location_span.hide();Z.chrom_select.hide();Z.nav_input.val(Z.chrom+":"+Z.low+"-"+Z.high);Z.nav_input.css("display","inline-block");Z.nav_input.select();Z.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.nav_controls)}this.zo_link=$("<a/>").attr("id","zoom-out").attr("title","Zoom out").tipsy({gravity:"n"}).click(function(){Z.zoom_out();Z.request_redraw()}).appendTo(this.nav_controls);this.zi_link=$("<a/>").attr("id","zoom-in").attr("title","Zoom in").tipsy({gravity:"n"}).click(function(){Z.zoom_in();Z.request_redraw()}).appendTo(this.nav_controls);this.load_chroms_deferred=this.load_chroms({low:0});this.chrom_select.bind("change",function(){Z.change_chrom(Z.chrom_select.val())});this.browser_content_div.click(function(ad){$(this).find("input").trigger("blur")});this.browser_content_div.bind("dblclick",function(ad){Z.zoom_in(ad.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(ad,ae){this.current_x=ae.offsetX}).bind("drag",function(ad,af){var ag=af.offsetX-this.current_x;this.current_x=af.offsetX;var ae=Math.round(ag/Z.viewport_container.width()*(Z.max_high-Z.max_low));Z.move_delta(-ae)});this.overview_close.click(function(){Z.reset_overview()});this.viewport_container.bind("draginit",function(ad,ae){if(ad.clientX>Z.viewport_container.width()-16){return false}}).bind("dragstart",function(ad,ae){ae.original_low=Z.low;ae.current_height=ad.clientY;ae.current_x=ae.offsetX}).bind("drag",function(af,ah){var ad=$(this);var ai=ah.offsetX-ah.current_x;var ae=ad.scrollTop()-(af.clientY-ah.current_height);ad.scrollTop(ae);ah.current_height=af.clientY;ah.current_x=ah.offsetX;var ag=Math.round(ai/Z.viewport_container.width()*(Z.high-Z.low));Z.move_delta(ag)}).bind("mousewheel",function(af,ah,ae,ad){if(ae){ae*=50;var ag=Math.round(-ae/Z.viewport_container.width()*(Z.high-Z.low));Z.move_delta(ag)}});this.top_labeltrack.bind("dragstart",function(ad,ae){return $("<div />").css({height:Z.browser_content_div.height()+Z.top_labeltrack.height()+Z.nav_labeltrack.height()+1,top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(ah,ai){$(ai.proxy).css({left:Math.min(ah.pageX,ai.startX)-Z.container.offset().left,width:Math.abs(ah.pageX-ai.startX)});var ae=Math.min(ah.pageX,ai.startX)-Z.container.offset().left,ad=Math.max(ah.pageX,ai.startX)-Z.container.offset().left,ag=(Z.high-Z.low),af=Z.viewport_container.width();Z.update_location(Math.round(ae/af*ag)+Z.low,Math.round(ad/af*ag)+Z.low)}).bind("dragend",function(ai,aj){var ae=Math.min(ai.pageX,aj.startX),ad=Math.max(ai.pageX,aj.startX),ag=(Z.high-Z.low),af=Z.viewport_container.width(),ah=Z.low;Z.low=Math.round(ae/af*ag)+ah;Z.high=Math.round(ad/af*ag)+ah;$(aj.proxy).remove();Z.request_redraw()});this.add_label_track(new V(this,{content_div:this.top_labeltrack}));this.add_label_track(new V(this,{content_div:this.nav_labeltrack}));$(window).bind("resize",function(){if(this.resize_timer){clearTimeout(this.resize_timer)}this.resize_timer=setTimeout(function(){Z.resize_window()},500)});$(document).bind("redraw",function(){Z.redraw()});this.reset();$(window).trigger("resize")},changed:function(){this.has_changes=true},update_intro_div:function(){if(this.drawables.length===0){this.intro_div.show()}else{this.intro_div.hide()}},trigger_navigate:function(aa,ac,Z,ad){if(this.timer){clearTimeout(this.timer)}if(ad){var ab=this;this.timer=setTimeout(function(){ab.trigger("navigate",aa+":"+ac+"-"+Z)},500)}else{view.trigger("navigate",aa+":"+ac+"-"+Z)}},update_location:function(Z,ab){this.location_span.text(commatize(Z)+" - "+commatize(ab));this.nav_input.val(this.chrom+":"+commatize(Z)+"-"+commatize(ab));var aa=view.chrom_select.val();if(aa!==""){this.trigger_navigate(aa,view.low,view.high,true)}},load_chroms:function(ab){ab.num=t;ab.dbkey=this.dbkey;var Z=this,aa=$.Deferred();$.ajax({url:chrom_url,data:ab,dataType:"json",success:function(ad){if(ad.chrom_info.length===0){alert("Invalid chromosome: "+ab.chrom);return}if(ad.reference){Z.add_label_track(new x(Z))}Z.chrom_data=ad.chrom_info;var ag='<option value="">Select Chrom/Contig</option>';for(var af=0,ac=Z.chrom_data.length;af<ac;af++){var ae=Z.chrom_data[af].chrom;ag+='<option value="'+ae+'">'+ae+"</option>"}if(ad.prev_chroms){ag+='<option value="previous">Previous '+t+"</option>"}if(ad.next_chroms){ag+='<option value="next">Next '+t+"</option>"}Z.chrom_select.html(ag);Z.chrom_start_index=ad.start_index;aa.resolve(ad)},error:function(){alert("Could not load chroms for this dbkey:",Z.dbkey)}});return aa},change_chrom:function(ae,aa,ag){var ab=this;if(!ab.chrom_data){ab.load_chroms_deferred.then(function(){ab.change_chrom(ae,aa,ag)});return}if(!ae||ae==="None"){return}var ab=this;if(ae==="previous"){ab.load_chroms({low:this.chrom_start_index-t});return}if(ae==="next"){ab.load_chroms({low:this.chrom_start_index+t});return}var af=$.grep(ab.chrom_data,function(ah,ai){return ah.chrom===ae})[0];if(af===undefined){ab.load_chroms({chrom:ae},function(){ab.change_chrom(ae,aa,ag)});return}else{if(ae!==ab.chrom){ab.chrom=ae;ab.chrom_select.val(ab.chrom);ab.max_high=af.len-1;ab.reset();ab.request_redraw(true);for(var ad=0,Z=ab.drawables.length;ad<Z;ad++){var ac=ab.drawables[ad];if(ac.init){ac.init()}}if(ab.reference_track){ab.reference_track.init()}}if(aa!==undefined&&ag!==undefined){ab.low=Math.max(aa,0);ab.high=Math.min(ag,ab.max_high)}else{ab.low=0;ab.high=ab.max_high}ab.reset_overview();ab.request_redraw()}},go_to:function(ad){ad=ad.replace(/ |,/g,"");var ah=this,Z,ac,aa=ad.split(":"),af=aa[0],ag=aa[1];if(ag!==undefined){try{var ae=ag.split("-");Z=parseInt(ae[0],10);ac=parseInt(ae[1],10)}catch(ab){return false}}ah.change_chrom(af,Z,ac)},move_fraction:function(ab){var Z=this;var aa=Z.high-Z.low;this.move_delta(ab*aa)},move_delta:function(ac){var Z=this;var ab=Z.high-Z.low;if(Z.low-ac<Z.max_low){Z.low=Z.max_low;Z.high=Z.max_low+ab}else{if(Z.high-ac>Z.max_high){Z.high=Z.max_high;Z.low=Z.max_high-ab}else{Z.high-=ac;Z.low-=ac}}Z.request_redraw();var aa=Z.chrom_select.val();this.trigger_navigate(aa,Z.low,Z.high,true)},add_drawable:function(Z){v.prototype.add_drawable.call(this,Z);Z.init();this.changed();this.update_intro_div()},add_label_track:function(Z){Z.view=this;Z.init();this.label_tracks.push(Z)},remove_drawable:function(ab,aa){v.prototype.remove_drawable.call(this,ab);if(aa){var Z=this;ab.container_div.hide(0,function(){$(this).remove();Z.update_intro_div()})}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},request_redraw:function(ah,Z,ag,aa){var af=this,ad=(aa?[aa]:af.drawables),ab;var aa;for(var ae=0;ae<ad.length;ae++){aa=ad[ae];ab=-1;for(var ac=0;ac<af.tracks_to_be_redrawn.length;ac++){if(af.tracks_to_be_redrawn[ac][0]===aa){ab=ac;break}}if(ab<0){af.tracks_to_be_redrawn.push([aa,Z,ag])}else{af.tracks_to_be_redrawn[ae][1]=Z;af.tracks_to_be_redrawn[ae][2]=ag}}if(!this.requested_redraw){requestAnimationFrame(function(){af._redraw(ah)});this.requested_redraw=true}},_redraw:function(aj){this.requested_redraw=false;var ag=this.low,ac=this.high;if(ag<this.max_low){ag=this.max_low}if(ac>this.max_high){ac=this.max_high}var ai=this.high-this.low;if(this.high!==0&&ai<this.min_separation){ac=ag+this.min_separation}this.low=Math.floor(ag);this.high=Math.ceil(ac);this.update_location(this.low,this.high);this.resolution_b_px=(this.high-this.low)/this.viewport_container.width();this.resolution_px_b=this.viewport_container.width()/(this.high-this.low);var Z=(this.low/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var af=((this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())||0;var ak=13;this.overview_box.css({left:Z,width:Math.max(ak,af)}).show();if(af<ak){this.overview_box.css("left",Z-(ak-af)/2)}if(this.overview_highlight){this.overview_highlight.css({left:Z,width:af})}if(!aj){var ab,aa,ah;for(var ad=0,ae=this.tracks_to_be_redrawn.length;ad<ae;ad++){ab=this.tracks_to_be_redrawn[ad][0];aa=this.tracks_to_be_redrawn[ad][1];ah=this.tracks_to_be_redrawn[ad][2];if(ab){ab._draw(aa,ah)}}this.tracks_to_be_redrawn=[];for(ad=0,ae=this.label_tracks.length;ad<ae;ad++){this.label_tracks[ad]._draw()}}},zoom_in:function(aa,ab){if(this.max_high===0||this.high-this.low<=this.min_separation){return}var ac=this.high-this.low,ad=ac/2+this.low,Z=(ac/this.zoom_factor)/2;if(aa){ad=aa/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(ad-Z);this.high=Math.round(ad+Z);this.changed();this.request_redraw()},zoom_out:function(){if(this.max_high===0){return}var aa=this.high-this.low,ab=aa/2+this.low,Z=(aa*this.zoom_factor)/2;this.low=Math.round(ab-Z);this.high=Math.round(ab+Z);this.changed();this.request_redraw()},resize_window:function(){this.viewport_container.height(this.container.height()-this.top_container.height()-this.bottom_container.height());this.request_redraw()},set_overview:function(ab){if(this.overview_drawable){if(this.overview_drawable.dataset_id===ab.dataset_id){return}this.overview_viewport.find(".track").remove()}var aa=ab.copy({content_div:this.overview_viewport}),Z=this;aa.header_div.hide();aa.is_overview=true;Z.overview_drawable=aa;this.overview_drawable.postdraw_actions=function(){Z.overview_highlight.show().height(Z.overview_drawable.content_div.height());Z.overview_viewport.height(Z.overview_drawable.content_div.height()+Z.overview_box.outerHeight());Z.overview_close.show();Z.resize_window()};Z.overview_drawable.request_draw();this.changed()},reset_overview:function(){$(".tipsy").remove();this.overview_viewport.find(".track-tile").remove();this.overview_viewport.height(this.default_overview_height);this.overview_box.height(this.default_overview_height);this.overview_close.hide();this.overview_highlight.hide();view.resize_window();view.overview_drawable=null}});var q=function(ab,ag,ac){this.track=ab;this.name=ag.name;this.params=[];var an=ag.params;for(var ad=0;ad<an.length;ad++){var ai=an[ad],aa=ai.name,am=ai.label,ae=unescape(ai.html),ao=ai.value,ak=ai.type;if(ak==="number"){this.params.push(new e(aa,am,ae,(aa in ac?ac[aa]:ao),ai.min,ai.max))}else{if(ak==="select"){this.params.push(new J(aa,am,ae,(aa in ac?ac[aa]:ao)))}else{console.log("WARNING: unrecognized tool parameter type:",aa,ak)}}}this.parent_div=$("<div/>").addClass("dynamic-tool").hide();this.parent_div.bind("drag",function(aq){aq.stopPropagation()}).click(function(aq){aq.stopPropagation()}).bind("dblclick",function(aq){aq.stopPropagation()});var al=$("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);var aj=this.params;var ah=this;$.each(this.params,function(ar,av){var au=$("<div>").addClass("param-row").appendTo(ah.parent_div);var aq=$("<div>").addClass("param-label").text(av.label).appendTo(au);var at=$("<div/>").addClass("param-input").html(av.html).appendTo(au);at.find(":input").val(av.value);$("<div style='clear: both;'/>").appendTo(au)});this.parent_div.find("input").click(function(){$(this).select()});var ap=$("<div>").addClass("param-row").appendTo(this.parent_div);var af=$("<input type='submit'>").attr("value","Run on complete dataset").appendTo(ap);var Z=$("<input type='submit'>").attr("value","Run on visible region").css("margin-left","3em").appendTo(ap);var ah=this;Z.click(function(){ah.run_on_region()});af.click(function(){ah.run_on_dataset()});if("visible" in ac&&ac.visible){this.parent_div.show()}};o(q.prototype,{update_params:function(){for(var Z=0;Z<this.params.length;Z++){this.params[Z].update_value()}},state_dict:function(){var aa={};for(var Z=0;Z<this.params.length;Z++){aa[this.params[Z].name]=this.params[Z].value}aa.visible=this.parent_div.is(":visible");return aa},get_param_values_dict:function(){var Z={};this.parent_div.find(":input").each(function(){var aa=$(this).attr("name"),ab=$(this).val();Z[aa]=JSON.stringify(ab)});return Z},get_param_values:function(){var aa=[];var Z={};this.parent_div.find(":input").each(function(){var ab=$(this).attr("name"),ac=$(this).val();if(ab){aa[aa.length]=ac}});return aa},run_on_dataset:function(){var Z=this;Z.run({dataset_id:this.track.original_dataset_id,tool_id:Z.name},null,function(aa){show_modal(Z.name+" is Running",Z.name+" is running on the complete dataset. Tool outputs are in dataset's history.",{Close:hide_modal})})},run_on_region:function(){var aa={dataset_id:this.track.original_dataset_id,chrom:this.track.view.chrom,low:this.track.view.low,high:this.track.view.high,tool_id:this.name},ae=this.track,ab=aa.tool_id+ae.tool_region_and_parameters_str(aa.chrom,aa.low,aa.high),Z;if(ae.container===view){var ad=new L(view,view,{name:this.name});var ac=ae.container.replace_drawable(ae,ad,false);ad.container_div.insertBefore(ae.view.content_div.children()[ac]);ad.add_drawable(ae);ae.container_div.appendTo(ad.content_div);Z=ad}else{Z=ae.container}var af=new ae.constructor(view,Z,{name:ab,hda_ldda:"hda"});af.init_for_tool_data();af.change_mode(ae.mode);af.set_filters_manager(ae.filters_manager.copy(af));af.update_icons();Z.add_drawable(af);af.tiles_div.text("Starting job.");this.update_params();this.run(aa,af,function(ag){af.dataset_id=ag.dataset_id;af.tiles_div.text("Running job.");af.init()})},run:function(aa,ab,ac){$.extend(aa,this.get_param_values_dict());var Z=function(){$.getJSON(rerun_tool_url,aa,function(ad){if(ad==="no converter"){ab.container_div.addClass("error");ab.content_div.text(F)}else{if(ad.error){ab.container_div.addClass("error");ab.content_div.text(u+ad.message)}else{if(ad==="pending"){ab.container_div.addClass("pending");ab.content_div.text("Converting input data so that it can be used quickly with tool.");setTimeout(Z,2000)}else{ac(ad)}}}})};Z()}});var J=function(aa,Z,ab,ac){this.name=aa;this.label=Z;this.html=$(ab);this.value=ac};o(J.prototype,{update_value:function(){this.value=$(this.html).val()}});var e=function(ab,aa,ad,ae,ac,Z){J.call(this,ab,aa,ad,ae);this.min=ac;this.max=Z};o(e.prototype,J.prototype,{update_value:function(){J.prototype.update_value.call(this);this.value=parseFloat(this.value)}});var f=function(Z){this.manager=null;this.name=Z.name;this.index=Z.index;this.tool_id=Z.tool_id;this.tool_exp_name=Z.tool_exp_name};o(f.prototype,{to_dict:function(){return{name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name}}});var P=function(ai){f.call(this,ai);this.low=("low" in ai?ai.low:-Number.MAX_VALUE);this.high=("high" in ai?ai.high:Number.MAX_VALUE);this.min=("min" in ai?ai.min:Number.MAX_VALUE);this.max=("max" in ai?ai.max:-Number.MAX_VALUE);this.container=null;this.slider=null;this.slider_label=null;var ae=function(aj,ak,al){aj.click(function(){var aq=ak.text(),ao=parseFloat(al.slider("option","max")),an=(ao<=1?4:ao<=1000000?ao.toString().length:6),ap=false,am=$(this).parents(".slider-row");am.addClass("input");if(al.slider("option","values")){an=2*an+1;ap=true}ak.text("");$("<input type='text'/>").attr("size",an).attr("maxlength",an).attr("value",aq).appendTo(ak).focus().select().click(function(ar){ar.stopPropagation()}).blur(function(){$(this).remove();ak.text(aq);am.removeClass("input")}).keyup(function(aw){if(aw.keyCode===27){$(this).trigger("blur")}else{if(aw.keyCode===13){var au=al.slider("option","min"),ar=al.slider("option","max"),av=function(ax){return(isNaN(ax)||ax>ar||ax<au)},at=$(this).val();if(!ap){at=parseFloat(at);if(av(at)){alert("Parameter value must be in the range ["+au+"-"+ar+"]");return $(this)}}else{at=at.split("-");at=[parseFloat(at[0]),parseFloat(at[1])];if(av(at[0])||av(at[1])){alert("Parameter value must be in the range ["+au+"-"+ar+"]");return $(this)}}al.slider((ap?"values":"value"),at);am.removeClass("input")}}})})};var aa=this;aa.parent_div=$("<div/>").addClass("filter-row slider-row");var Z=$("<div/>").addClass("elt-label").appendTo(aa.parent_div),ag=$("<span/>").addClass("slider-name").text(aa.name+" ").appendTo(Z),ab=$("<span/>").text(this.low+"-"+this.high),ac=$("<span/>").addClass("slider-value").appendTo(Z).append("[").append(ab).append("]");aa.values_span=ab;var af=$("<div/>").addClass("slider").appendTo(aa.parent_div);aa.control_element=$("<div/>").attr("id",aa.name+"-filter-control").appendTo(af);var ad=[0,0];aa.control_element.slider({range:true,min:this.min,max:this.max,step:this.get_slider_step(this.min,this.max),values:[this.low,this.high],slide:function(aj,ak){aa.slide(aj,ak)},change:function(aj,ak){aa.control_element.slider("option","slide").call(aa.control_element,aj,ak)}});aa.slider=aa.control_element;aa.slider_label=ab;ae(ac,ab,aa.control_element);var ah=$("<div/>").addClass("display-controls").appendTo(aa.parent_div);this.transparency_icon=create_action_icon("Use filter for data transparency","layer-transparent",function(){if(aa.manager.alpha_filter!==aa){aa.manager.alpha_filter=aa;aa.manager.parent_div.find(".layer-transparent").removeClass("active").hide();aa.transparency_icon.addClass("active").show()}else{aa.manager.alpha_filter=null;aa.transparency_icon.removeClass("active")}aa.manager.track.request_draw(true,true)}).appendTo(ah).hide();this.height_icon=create_action_icon("Use filter for data height","arrow-resize-090",function(){if(aa.manager.height_filter!==aa){aa.manager.height_filter=aa;aa.manager.parent_div.find(".arrow-resize-090").removeClass("active").hide();aa.height_icon.addClass("active").show()}else{aa.manager.height_filter=null;aa.height_icon.removeClass("active")}aa.manager.track.request_draw(true,true)}).appendTo(ah).hide();aa.parent_div.hover(function(){aa.transparency_icon.show();aa.height_icon.show()},function(){if(aa.manager.alpha_filter!==aa){aa.transparency_icon.hide()}if(aa.manager.height_filter!==aa){aa.height_icon.hide()}});$("<div style='clear: both;'/>").appendTo(aa.parent_div)};o(P.prototype,{to_dict:function(){var Z=f.prototype.to_dict.call(this);return o(Z,{type:"number",min:this.min,max:this.max,low:this.low,high:this.high})},copy:function(){return new P({name:this.name,index:this.index,tool_id:this.tool_id,tool_exp_name:this.tool_exp_name})},get_slider_step:function(ab,Z){var aa=Z-ab;return(aa<=2?0.01:1)},slide:function(aa,ab){var Z=ab.values;this.values_span.text(Z[0]+"-"+Z[1]);this.low=Z[0];this.high=Z[1];this.manager.track.request_draw(true,true)},applies_to:function(Z){if(Z.length>this.index){return true}return false},_keep_val:function(Z){return(isNaN(Z)||(Z>=this.low&&Z<=this.high))},keep:function(aa){if(!this.applies_to(aa)){return true}var ac=this;var ad=aa[this.index];if(ad instanceof Array){var ab=true;for(var Z=0;Z<ad.length;Z++){if(!this._keep_val(ad[Z])){ab=false;break}}return ab}else{return this._keep_val(aa[this.index])}},update_attrs:function(ac){var Z=false;if(!this.applies_to(ac)){return Z}var aa=ac[this.index];if(!(aa instanceof Array)){aa=[aa]}for(var ab=0;ab<aa.length;ab++){var ad=aa[ab];if(ad<this.min){this.min=Math.floor(ad);Z=true}if(ad>this.max){this.max=Math.ceil(ad);Z=true}}return Z},update_ui_elt:function(){if(this.min<this.max){this.parent_div.show()}else{this.parent_div.hide()}var aa=this.slider.slider("option","min"),Z=this.slider.slider("option","max");if(this.min<aa||this.max>Z){this.slider.slider("option","min",this.min);this.slider.slider("option","max",this.max);this.slider.slider("option","step",this.get_slider_step(this.min,this.max));this.slider.slider("option","values",[this.min,this.max])}}});var U=function(ab,ah){this.track=ab;this.alpha_filter=null;this.height_filter=null;this.filters=[];this.parent_div=$("<div/>").addClass("filters").hide();this.parent_div.bind("drag",function(aj){aj.stopPropagation()}).click(function(aj){aj.stopPropagation()}).bind("dblclick",function(aj){aj.stopPropagation()}).bind("keydown",function(aj){aj.stopPropagation()});if(ah&&"filters" in ah){var Z=("alpha_filter" in ah?ah.alpha_filter:null),ac=("height_filter" in ah?ah.height_filter:null),ae=ah.filters,aa;for(var af=0;af<ae.length;af++){if(ae[af].type==="number"){aa=new P(ae[af]);this.add_filter(aa);if(aa.name===Z){this.alpha_filter=aa;aa.transparency_icon.addClass("active").show()}if(aa.name===ac){this.height_filter=aa;aa.height_icon.addClass("active").show()}}else{console.log("ERROR: unsupported filter: ",name,type)}}if("visible" in ah&&ah.visible){this.parent_div.show()}}if(this.filters.length!==0){var ai=$("<div/>").addClass("param-row").appendTo(this.parent_div);var ag=$("<input type='submit'/>").attr("value","Run on complete dataset").appendTo(ai);var ad=this;ag.click(function(){ad.run_on_dataset()})}};o(U.prototype,{show:function(){this.parent_div.show()},hide:function(){this.parent_div.hide()},toggle:function(){this.parent_div.toggle()},visible:function(){return this.parent_div.is(":visible")},to_dict:function(){var ac={},ab=[],aa;for(var Z=0;Z<this.filters.length;Z++){aa=this.filters[Z];ab.push(aa.to_dict())}ac.filters=ab;ac.alpha_filter=(this.alpha_filter?this.alpha_filter.name:null);ac.height_filter=(this.height_filter?this.height_filter.name:null);ac.visible=this.parent_div.is(":visible");return ac},copy:function(aa){var ab=new U(aa);for(var Z=0;Z<this.filters.length;Z++){ab.add_filter(this.filters[Z].copy())}return ab},add_filter:function(Z){Z.manager=this;this.parent_div.append(Z.parent_div);this.filters.push(Z)},remove_all:function(){this.filters=[];this.parent_div.children().remove()},init_filters:function(){for(var Z=0;Z<this.filters.length;Z++){var aa=this.filters[Z];aa.update_ui_elt()}},clear_filters:function(){for(var Z=0;Z<this.filters.length;Z++){var aa=this.filters[Z];aa.slider.slider("option","values",[aa.min,aa.max])}this.alpha_filter=null;this.height_filter=null;this.parent_div.find(".icon-button").hide()},run_on_dataset:function(){var ah=function(al,aj,ak){if(!(aj in al)){al[aj]=ak}return al[aj]};var ab={},Z,aa,ac;for(var ad=0;ad<this.filters.length;ad++){Z=this.filters[ad];if(Z.tool_id){if(Z.min!==Z.low){aa=ah(ab,Z.tool_id,[]);aa[aa.length]=Z.tool_exp_name+" >= "+Z.low}if(Z.max!==Z.high){aa=ah(ab,Z.tool_id,[]);aa[aa.length]=Z.tool_exp_name+" <= "+Z.high}}}var af=[];for(var ai in ab){af[af.length]=[ai,ab[ai]]}var ag=af.length;(function ae(ap,am){var ak=am[0],al=ak[0],ao=ak[1],an="("+ao.join(") and (")+")",aj={cond:an,input:ap,target_dataset_id:ap,tool_id:al},am=am.slice(1);$.getJSON(run_tool_url,aj,function(aq){if(aq.error){show_modal("Filter Dataset","Error running tool "+al,{Close:hide_modal})}else{if(am.length===0){show_modal("Filtering Dataset","Filter(s) are running on the complete dataset. Outputs are in dataset's history.",{Close:hide_modal})}else{ae(aq.dataset_id,am)}}})})(this.track.dataset_id,af)}});var y=function(Z,aa){H.Scaler.call(this,aa);this.filter=Z};y.prototype.gen_val=function(Z){if(this.filter.high===Number.MAX_VALUE||this.filter.low===-Number.MAX_VALUE||this.filter.low===this.filter.high){return this.default_val}return((parseFloat(Z[this.filter.index])-this.filter.low)/(this.filter.high-this.filter.low))};var B=function(Z){this.track=Z.track;this.params=Z.params;this.values={};this.restore_values((Z.saved_values?Z.saved_values:{}));this.onchange=Z.onchange};o(B.prototype,{restore_values:function(Z){var aa=this;$.each(this.params,function(ab,ac){if(Z[ac.key]!==undefined){aa.values[ac.key]=Z[ac.key]}else{aa.values[ac.key]=ac.default_value}})},build_form:function(){var ac=this;var Z=$("<div />");var ab;function aa(ag,ad){for(var ak=0;ak<ag.length;ak++){ab=ag[ak];if(ab.hidden){continue}var ae="param_"+ak;var ao=ac.values[ab.key];var ar=$("<div class='form-row' />").appendTo(ad);ar.append($("<label />").attr("for",ae).text(ab.label+":"));if(ab.type==="bool"){ar.append($('<input type="checkbox" />').attr("id",ae).attr("name",ae).attr("checked",ao))}else{if(ab.type==="text"){ar.append($('<input type="text"/>').attr("id",ae).val(ao).click(function(){$(this).select()}))}else{if(ab.type==="select"){var am=$("<select />").attr("id",ae);for(var ai=0;ai<ab.options.length;ai++){$("<option/>").text(ab.options[ai].label).attr("value",ab.options[ai].value).appendTo(am)}am.val(ao);ar.append(am)}else{if(ab.type==="color"){var aq=$("<div/>").appendTo(ar),al=$("<input />").attr("id",ae).attr("name",ae).val(ao).css("float","left").appendTo(aq).click(function(au){$(".tipsy").hide();var at=$(this).siblings(".tipsy");at.css({left:$(this).position().left+$(this).width()+5,top:$(this).position().top-($(at).height()/2)+($(this).height()/2)}).show();at.click(function(av){av.stopPropagation()});$(document).bind("click.color-picker",function(){at.hide();$(document).unbind("click.color-picker")});au.stopPropagation()}),aj=$("<a href='javascript:void(0)'/>").addClass("icon-button arrow-circle").appendTo(aq).attr("title","Set new random color").tipsy({gravity:"s"}),an=$("<div class='tipsy tipsy-west' style='position: absolute;' />").appendTo(aq).hide(),af=$("<div style='background-color: black; padding: 10px;'></div>").appendTo(an),ap=$("<div/>").appendTo(af),ah=$.farbtastic(ap,{width:100,height:100,callback:al,color:ao});aq.append($("<div/>").css("clear","both"));(function(at){aj.click(function(){at.setColor(get_random_color())})})(ah)}else{ar.append($("<input />").attr("id",ae).attr("name",ae).val(ao))}}}}if(ab.help){ar.append($("<div class='help'/>").text(ab.help))}}}aa(this.params,Z);return Z},update_from_form:function(Z){var ab=this;var aa=false;$.each(this.params,function(ac,ae){if(!ae.hidden){var af="param_"+ac;var ad=Z.find("#"+af).val();if(ae.type==="float"){ad=parseFloat(ad)}else{if(ae.type==="int"){ad=parseInt(ad)}else{if(ae.type==="bool"){ad=Z.find("#"+af).is(":checked")}}}if(ad!==ab.values[ae.key]){ab.values[ae.key]=ad;aa=true}}});if(aa){this.onchange();this.track.changed()}}});var b=function(Z,ac,ab,aa,ad){this.track=Z;this.index=ac;var ae=this.track._get_tile_bounds(ac,ab);this.low=ae[0];this.high=ae[1];this.resolution=ab;this.html_elt=$("<div class='track-tile'/>").append(aa).height($(aa).attr("height"));this.data=ad;this.stale=false};b.prototype.predisplay_actions=function(){};var j=function(Z,ac,ab,aa,ad,ae){b.call(this,Z,ac,ab,aa,ad);this.max_val=ae};o(j.prototype,b.prototype);var K=function(ac,ah,ad,ab,af,am,ag,an,aa,ak){b.call(this,ac,ah,ad,ab,af);this.mode=ag;this.all_slotted=aa;this.feature_mapper=ak;this.has_icons=false;if(an){this.has_icons=true;var ai=this;ab=this.html_elt.children()[0],message_div=$("<div/>").addClass("tile-message").css({height:z-1,width:ab.width}).prependTo(this.html_elt);var aj=new GenomeRegion({chrom:ac.view.chrom,start:this.low,end:this.high}),al=af.length,ae=$("<a href='javascript:void(0);'/>").addClass("icon more-down").attr("title","For speed, only the first "+al+" features in this region were obtained from server. Click to get more data including depth").tipsy({gravity:"s"}).appendTo(message_div),Z=$("<a href='javascript:void(0);'/>").addClass("icon more-across").attr("title","For speed, only the first "+al+" features in this region were obtained from server. Click to get more data excluding depth").tipsy({gravity:"s"}).appendTo(message_div);ae.click(function(){ai.stale=true;ac.data_manager.get_more_data(aj,ac.mode,ai.resolution,{},ac.data_manager.DEEP_DATA_REQ);$(".tipsy").hide();ac.request_draw(true)}).dblclick(function(ao){ao.stopPropagation()});Z.click(function(){ai.stale=true;ac.data_manager.get_more_data(aj,ac.mode,ai.resolution,{},ac.data_manager.BROAD_DATA_REQ);$(".tipsy").hide();ac.request_draw(true)}).dblclick(function(ao){ao.stopPropagation()})}};o(K.prototype,b.prototype);K.prototype.predisplay_actions=function(){var aa=this,Z={};if(aa.mode!=="Pack"){return}$(this.html_elt).hover(function(){this.hovered=true;$(this).mousemove()},function(){this.hovered=false;$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()}).mousemove(function(al){if(!this.hovered){return}var ag=$(this).offset(),ak=al.pageX-ag.left,aj=al.pageY-ag.top,ap=aa.feature_mapper.get_feature_data(ak,aj),ah=(ap?ap[0]:null);$(this).parents(".track-content").children(".overlay").children(".feature-popup").each(function(){if(!ah||$(this).attr("id")!==ah.toString()){$(this).remove()}});if(ap){var ac=Z[ah];if(!ac){var ah=ap[0],am={name:ap[3],start:ap[1],end:ap[2],strand:ap[4]},af=aa.track.filters_manager.filters,ae;for(var ai=0;ai<af.length;ai++){ae=af[ai];am[ae.name]=ap[ae.index]}var ac=$("<div/>").attr("id",ah).addClass("feature-popup"),aq=$("<table/>"),ao,an,ar;for(ao in am){an=am[ao];ar=$("<tr/>").appendTo(aq);$("<th/>").appendTo(ar).text(ao);$("<td/>").attr("align","left").appendTo(ar).text(typeof(an)==="number"?T(an,2):an)}ac.append($("<div class='feature-popup-inner'>").append(aq));Z[ah]=ac}ac.appendTo($(this).parents(".track-content").children(".overlay"));var ad=ak+parseInt(aa.html_elt.css("left"))-ac.width()/2,ab=aj+parseInt(aa.html_elt.css("top"))+7;ac.css("left",ad+"px").css("top",ab+"px")}else{if(!al.isPropagationStopped()){al.stopPropagation();$(this).siblings().each(function(){$(this).trigger(al)})}}}).mouseleave(function(){$(this).parents(".track-content").children(".overlay").children(".feature-popup").remove()})};var h=function(aa,Z,ac){o(ac,{drag_handle_class:"draghandle"});p.call(this,aa,Z,ac);this.data_url=("data_url" in ac?ac.data_url:default_data_url);this.data_url_extra_params={};this.data_query_wait=("data_query_wait" in ac?ac.data_query_wait:G);this.dataset_check_url=converted_datasets_state_url;var ab=new Dataset({id:ac.dataset_id,hda_ldda:ac.hda_ldda});this.data_manager=("data_manager" in ac?ac.data_manager:new GenomeDataManager({dataset:ab,data_url:default_data_url,data_mode_compatible:this.data_and_mode_compatible,can_subset:this.can_subset,}));this.min_height_px=16;this.max_height_px=800;this.visible_height_px=0;this.content_div=$("<div class='track-content'>").appendTo(this.container_div);if(this.container){this.container.content_div.append(this.container_div);if(!("resize" in ac)||ac.resize){this.add_resize_handle()}}};o(h.prototype,p.prototype,{action_icons_def:[{name:"mode_icon",title:"Set display mode",css_class:"chevron-expand",on_click_fn:function(){}},p.prototype.action_icons_def[0],{name:"overview_icon",title:"Set as overview",css_class:"overview-icon",on_click_fn:function(Z){Z.view.set_overview(Z)}},p.prototype.action_icons_def[1],{name:"filters_icon",title:"Filters",css_class:"filters-icon",on_click_fn:function(Z){if(Z.filters_manager.visible()){Z.filters_manager.clear_filters()}else{Z.filters_manager.init_filters()}Z.filters_manager.toggle()}},{name:"tools_icon",title:"Tool",css_class:"hammer",on_click_fn:function(Z){Z.dynamic_tool_div.toggle();if(Z.dynamic_tool_div.is(":visible")){Z.set_name(Z.name+Z.tool_region_and_parameters_str())}else{Z.revert_name()}$(".tipsy").remove()}},p.prototype.action_icons_def[2]],can_draw:function(){if(this.dataset_id&&p.prototype.can_draw.call(this)){return true}return false},build_container_div:function(){return $("<div/>").addClass("track").attr("id","track_"+this.id).css("position","relative")},build_header_div:function(){var Z=$("<div class='track-header'/>");if(this.view.editor){this.drag_div=$("<div/>").addClass(this.drag_handle_class).appendTo(Z)}this.name_div=$("<div/>").addClass("track-name").appendTo(Z).text(this.name).attr("id",this.name.replace(/\s+/g,"-").replace(/[^a-zA-Z0-9\-]/g,"").toLowerCase());return Z},on_resize:function(){},add_resize_handle:function(){var Z=this;var ac=false;var ab=false;var aa=$("<div class='track-resize'>");$(Z.container_div).hover(function(){if(Z.content_visible){ac=true;aa.show()}},function(){ac=false;if(!ab){aa.hide()}});aa.hide().bind("dragstart",function(ad,ae){ab=true;ae.original_height=$(Z.content_div).height()}).bind("drag",function(ae,af){var ad=Math.min(Math.max(af.original_height+af.deltaY,Z.min_height_px),Z.max_height_px);$(Z.tiles_div).css("height",ad);Z.visible_height_px=(Z.max_height_px===ad?0:ad);Z.on_resize()}).bind("dragend",function(ad,ae){Z.tile_cache.clear();ab=false;if(!ac){aa.hide()}Z.config.values.height=Z.visible_height_px;Z.changed()}).appendTo(Z.container_div)},set_display_modes:function(ac,af){this.display_modes=ac;this.mode=(af?af:(this.config&&this.config.values.mode?this.config.values.mode:this.display_modes[0]));this.action_icons.mode_icon.attr("title","Set display mode (now: "+this.mode+")");var aa=this,ad={};for(var ab=0,Z=aa.display_modes.length;ab<Z;ab++){var ae=aa.display_modes[ab];ad[ae]=function(ag){return function(){aa.change_mode(ag);aa.icons_div.show();aa.container_div.mouseleave(function(){aa.icons_div.hide()})}}(ae)}make_popupmenu(this.action_icons.mode_icon,ad)},build_action_icons:function(){p.prototype.build_action_icons.call(this,this.action_icons_def);if(this.display_modes!==undefined){this.set_display_modes(this.display_modes)}},hide_contents:function(){this.tiles_div.hide();this.container_div.find(".yaxislabel, .track-resize").hide()},show_contents:function(){this.tiles_div.show();this.container_div.find(".yaxislabel, .track-resize").show();this.request_draw()},get_type:function(){if(this instanceof V){return"LabelTrack"}else{if(this instanceof x){return"ReferenceTrack"}else{if(this instanceof i){return"LineTrack"}else{if(this instanceof Q){return"ReadTrack"}else{if(this instanceof N){return"VcfTrack"}else{if(this instanceof g){return"CompositeTrack"}else{if(this instanceof c){return"FeatureTrack"}}}}}}}return""},init:function(){var aa=this;aa.enabled=false;aa.tile_cache.clear();aa.data_manager.clear();aa.content_div.css("height","auto");aa.tiles_div.children().remove();aa.container_div.removeClass("nodata error pending");if(!aa.dataset_id){return}var Z=$.Deferred();$.getJSON(this.dataset_check_url,{hda_ldda:aa.hda_ldda,dataset_id:aa.dataset_id,chrom:aa.view.chrom},function(ab){if(!ab||ab==="error"||ab.kind==="error"){aa.container_div.addClass("error");aa.tiles_div.text(n);if(ab.message){var ac=$(" <a href='javascript:void(0);'></a>").text("View error").click(function(){show_modal("Trackster Error","<pre>"+ab.message+"</pre>",{Close:hide_modal})});aa.tiles_div.append(ac)}}else{if(ab==="no converter"){aa.container_div.addClass("error");aa.tiles_div.text(F)}else{if(ab==="no data"||(ab.data!==undefined&&(ab.data===null||ab.data.length===0))){aa.container_div.addClass("nodata");aa.tiles_div.text(A)}else{if(ab==="pending"){aa.container_div.addClass("pending");aa.tiles_div.html(s);setTimeout(function(){aa.init()},aa.data_query_wait)}else{if(ab==="data"||ab.status==="data"){if(ab.valid_chroms){aa.valid_chroms=ab.valid_chroms;aa.update_icons()}aa.tiles_div.text(S);if(aa.view.chrom){aa.tiles_div.text("");aa.tiles_div.css("height",aa.visible_height_px+"px");aa.enabled=true;$.when(aa.predraw_init()).done(function(){Z.resolve();aa.container_div.removeClass("nodata error pending");aa.request_draw()})}else{Z.resolve()}}}}}}});this.update_icons();return Z},predraw_init:function(){}});var I=function(ab,aa,ac){h.call(this,ab,aa,ac);var Z=this,ab=Z.view;l(Z.container_div,Z.drag_handle_class,".group",Z);this.filters_manager=new U(this,("filters" in ac?ac.filters:null));this.data_manager.set("filters_manager",this.filters_manager);this.filters_available=false;this.tool=("tool" in ac&&ac.tool?new q(this,ac.tool,ac.tool_state):null);this.tile_cache=new Cache(M);if(this.header_div){this.set_filters_manager(this.filters_manager);if(this.tool){this.dynamic_tool_div=this.tool.parent_div;this.header_div.after(this.dynamic_tool_div)}}this.tiles_div=$("<div/>").addClass("tiles").appendTo(this.content_div);this.overlay_div=$("<div/>").addClass("overlay").appendTo(this.content_div);if(ac.mode){this.change_mode(ac.mode)}};o(I.prototype,p.prototype,h.prototype,{action_icons_def:h.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(Z){$(".tipsy").remove();Z.slotters[Z.view.resolution_px_b].max_rows*=2;Z.request_draw(true)},hide:true}]),copy:function(Z){var aa=this.to_dict();o(aa,{data_manager:this.data_manager});var ab=new this.constructor(this.view,Z,aa);ab.change_mode(this.mode);ab.enabled=this.enabled;return ab},set_filters_manager:function(Z){this.filters_manager=Z;this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),name:this.name,hda_ldda:this.hda_ldda,dataset_id:this.dataset_id,prefs:this.prefs,mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:(this.tool?this.tool.state_dict():{})}},change_mode:function(aa){var Z=this;Z.mode=aa;Z.config.values.mode=aa;Z.tile_cache.clear();Z.request_draw();this.action_icons.mode_icon.attr("title","Set display mode (now: "+Z.mode+")");return Z},update_icons:function(){var Z=this;if(Z.filters_available){Z.action_icons.filters_icon.show()}else{Z.action_icons.filters_icon.hide()}if(Z.tool){Z.action_icons.tools_icon.show()}else{Z.action_icons.tools_icon.hide()}},_gen_tile_cache_key:function(aa,ab,Z){return aa+"_"+ab+"_"+Z},request_draw:function(aa,Z){this.view.request_redraw(false,aa,Z,this)},before_draw:function(){},_draw:function(aa,ak){if(!this.can_draw()){return}var ai=this.view.low,ae=this.view.high,ag=ae-ai,ab=this.view.container.width(),am=this.view.resolution_px_b,ad=this.view.resolution_b_px;if(this.is_overview){ai=this.view.max_low;ae=this.view.max_high;ad=Math.pow(RESOLUTION,Math.ceil(Math.log((view.max_high-view.max_low)/O)/Math.log(RESOLUTION)));am=ab/(view.max_high-view.max_low)}this.before_draw();this.tiles_div.children().addClass("remove");var Z=Math.floor(ai/(ad*O)),ah=true,al=[],af=function(an){return(an&&"track" in an)};while((Z*O*ad)<ae){var aj=this.draw_helper(aa,ab,Z,ad,this.tiles_div,am);if(af(aj)){al.push(aj)}else{ah=false}Z+=1}if(!ak){this.tiles_div.children(".remove").removeClass("remove").remove()}var ac=this;if(ah){this.tiles_div.children(".remove").remove();ac.postdraw_actions(al,ab,am,ak)}},postdraw_actions:function(ac,ad,af,Z){var ab=this;var ae=false;for(var aa=0;aa<ac.length;aa++){if(ac[aa].has_icons){ae=true;break}}if(ae){for(var aa=0;aa<ac.length;aa++){tile=ac[aa];if(!tile.has_icons){tile.html_elt.css("padding-top",z)}}}},draw_helper:function(Z,am,ar,ap,af,ag,an){var al=this,av=this._gen_tile_cache_key(am,ag,ar),ad=this._get_tile_bounds(ar,ap),ac=new GenomeRegion({chrom:this.view.chrom,start:ad[0],end:ad[1]});if(!an){an={}}var au=(Z?undefined:al.tile_cache.get_elt(av));if(au){al.show_tile(au,af,ag);return au}var aj=true;var aq=al.data_manager.get_data(ac,al.mode,ap,al.data_url_extra_params);if(is_deferred(aq)){aj=false}var ah;if(view.reference_track&&ag>view.canvas_manager.char_width_px){ah=view.reference_track.data_manager.get_data(ac,al.mode,ap,view.reference_track.data_url_extra_params);if(is_deferred(ah)){aj=false}}if(aj){o(aq,an.more_tile_data);var ai=al.mode;if(ai==="Auto"){ai=al.get_mode(aq);al.update_auto_mode(ai)}var ab=al.view.canvas_manager.new_canvas(),at=ac.get("start"),aa=ac.get("end"),am=Math.ceil((aa-at)*ag)+al.left_offset,ak=al.get_canvas_height(aq,ai,ag,am);ab.width=am;ab.height=ak;var ao=ab.getContext("2d");ao.translate(this.left_offset,0);var au=al.draw_tile(aq,ao,ai,ap,ar,ag,ah);if(au!==undefined){al.tile_cache.set_elt(av,au);al.show_tile(au,af,ag)}return au}var ae=$.Deferred();$.when(aq,ah).then(function(){view.request_redraw(false,false,false,al);ae.resolve()});return ae},get_canvas_height:function(Z,ab,ac,aa){return this.visible_height_px},draw_tile:function(Z,ab,ae,ad,aa,af,ac){console.log("Warning: TiledTrack.draw_tile() not implemented.")},show_tile:function(ab,ad,ae){var aa=this,Z=ab.html_elt;ab.predisplay_actions();var ac=(ab.low-(this.is_overview?this.view.max_low:this.view.low))*ae;if(this.left_offset){ac-=this.left_offset}Z.css({position:"absolute",top:0,left:ac});if(Z.hasClass("remove")){Z.removeClass("remove")}else{ad.append(Z)}aa.after_show_tile(ab)},after_show_tile:function(Z){},_get_tile_bounds:function(Z,aa){var ac=Math.floor(Z*O*aa),ad=Math.ceil(O*aa),ab=(ac+ad<=this.view.max_high?ac+ad:this.view.max_high);return[ac,ab]},tool_region_and_parameters_str:function(ab,Z,ac){var aa=this,ad=(ab!==undefined&&Z!==undefined&&ac!==undefined?ab+":"+Z+"-"+ac:"all");return" - region=["+ad+"], parameters=["+aa.tool.get_param_values().join(", ")+"]"},data_and_mode_compatible:function(Z,aa){return true},can_subset:function(Z){return false},init_for_tool_data:function(){this.data_url=raw_data_url;this.data_query_wait=1000;this.dataset_check_url=dataset_state_url;this.normal_postdraw_actions=this.postdraw_actions;this.postdraw_actions=function(ab,ac,ad,Z){var aa=this;aa.normal_postdraw_actions(ab,ac,ad,Z);aa.dataset_state_url=converted_datasets_state_url;aa.data_query_wait=G;$.when(server_state_deferred(aa.dataset_state_url,{dataset_id:aa.dataset_id,hda_ldda:aa.hda_ldda},aa.data_query_wait,function(ae){return ae!=="pending"})).then(function(){aa.data_url=default_data_url});aa.postdraw_actions=aa.normal_postdraw_actions}}});var V=function(aa,Z){var ab={resize:false};h.call(this,aa,Z,ab);this.container_div.addClass("label-track")};o(V.prototype,h.prototype,{build_header_div:function(){},init:function(){this.enabled=true},_draw:function(){var ab=this.view,ac=ab.high-ab.low,af=Math.floor(Math.pow(10,Math.floor(Math.log(ac)/Math.log(10)))),Z=Math.floor(ab.low/af)*af,ad=this.view.container.width(),aa=$("<div style='position: relative; height: 1.3em;'></div>");while(Z<ab.high){var ae=(Z-ab.low)/ac*ad;aa.append($("<div class='label'>"+commatize(Z)+"</div>").css({position:"absolute",left:ae-1}));Z+=af}this.content_div.children(":first").remove();this.content_div.append(aa)}});var g=function(aa,Z,ad){I.call(this,aa,Z,ad);this.drawables=[];this.left_offset=0;if("drawables" in ad){var ac;for(var ab=0;ab<ad.drawables.length;ab++){ac=ad.drawables[ab];this.drawables[ab]=object_from_template(ac);if(ac.left_offset>this.left_offset){this.left_offset=ac.left_offset}}this.enabled=true}if(this.drawables.length!==0){this.set_display_modes(this.drawables[0].display_modes,this.drawables[0].mode)}this.update_icons();this.obj_type="CompositeTrack"};o(g.prototype,I.prototype,{action_icons_def:[{name:"composite_icon",title:"Show individual tracks",css_class:"layers-stack",on_click_fn:function(Z){$(".tipsy").remove();Z.show_group()}}].concat(I.prototype.action_icons_def),to_dict:v.prototype.to_dict,add_drawable:v.prototype.add_drawable,unpack_drawables:v.prototype.unpack_drawables,change_mode:function(Z){I.prototype.change_mode.call(this,Z);for(var aa=0;aa<this.drawables.length;aa++){this.drawables[aa].change_mode(Z)}},init:function(){var ab=[];for(var aa=0;aa<this.drawables.length;aa++){ab.push(this.drawables[aa].init())}var Z=this;$.when.apply($,ab).then(function(){Z.enabled=true;Z.request_draw()})},update_icons:function(){this.action_icons.filters_icon.hide();this.action_icons.tools_icon.hide()},can_draw:p.prototype.can_draw,draw_helper:function(aa,ao,av,ar,ag,ai,ap){var an=this,az=this._gen_tile_cache_key(ao,ai,av),ae=this._get_tile_bounds(av,ar),aw=ae[0],ab=ae[1];if(!ap){ap={}}var ay=(aa?undefined:an.tile_cache.get_elt(az));if(ay){an.show_tile(ay,ag,ai);return ay}var ah=[],an,al=true,at,aj;for(var au=0;au<this.drawables.length;au++){an=this.drawables[au];at=an.data_manager.get_data(aw,ab,an.mode,ar,an.data_url_extra_params);if(is_deferred(at)){al=false}ah.push(at);aj=null;if(view.reference_track&&ai>view.canvas_manager.char_width_px){aj=view.reference_track.data_manager.get_data(aw,ab,an.mode,ar,view.reference_track.data_url_extra_params);if(is_deferred(aj)){al=false}}ah.push(aj)}if(al){o(at,ap.more_tile_data);this.tile_predraw_init();var ad=an.view.canvas_manager.new_canvas(),ae=an._get_tile_bounds(av,ar),aw=ae[0],ab=ae[1],ax=0,ao=Math.ceil((ab-aw)*ai)+this.left_offset,am=0,ac=[];var Z=0;for(var au=0;au<this.drawables.length;au++,ax+=2){an=this.drawables[au];at=ah[ax];var ak=an.mode;if(ak==="Auto"){ak=an.get_mode(at);an.update_auto_mode(ak)}ac.push(ak);Z=an.get_canvas_height(at,ak,ai,ao);if(Z>am){am=Z}}ad.width=ao;ad.height=(ap.height?ap.height:am);ax=0;var aq=ad.getContext("2d");aq.translate(this.left_offset,0);aq.globalAlpha=0.5;aq.globalCompositeOperation="source-over";for(var au=0;au<this.drawables.length;au++,ax+=2){an=this.drawables[au];at=ah[ax];aj=ah[ax+1];ay=an.draw_tile(at,aq,ac[au],ar,av,ai,aj)}this.tile_cache.set_elt(az,ay);this.show_tile(ay,ag,ai);return ay}var af=$.Deferred(),an=this;$.when.apply($,ah).then(function(){view.request_redraw(false,false,false,an);af.resolve()});return af},show_group:function(){var ac=new L(this.view,this.container,{name:this.name}),Z;for(var ab=0;ab<this.drawables.length;ab++){Z=this.drawables[ab];ac.add_drawable(Z);Z.container=ac;ac.content_div.append(Z.container_div)}var aa=this.container.replace_drawable(this,ac,true);ac.request_draw()},tile_predraw_init:function(){var ac=Number.MAX_VALUE,Z=-ac,aa;for(var ab=0;ab<this.drawables.length;ab++){aa=this.drawables[ab];if(aa instanceof i){if(aa.prefs.min_value<ac){ac=aa.prefs.min_value}if(aa.prefs.max_value>Z){Z=aa.prefs.max_value}}}for(var ab=0;ab<this.drawables.length;ab++){aa=this.drawables[ab];aa.prefs.min_value=ac;aa.prefs.max_value=Z}},postdraw_actions:function(ab,ae,ag,aa){I.prototype.postdraw_actions.call(this,ab,ae,ag,aa);var ad=-1;for(var ac=0;ac<ab.length;ac++){var Z=ab[ac].html_elt.find("canvas").height();if(Z>ad){ad=Z}}for(var ac=0;ac<ab.length;ac++){var af=ab[ac];if(af.html_elt.find("canvas").height()!==ad){this.draw_helper(true,ae,af.index,af.resolution,af.html_elt.parent(),ag,{height:ad});af.html_elt.remove()}}}});var x=function(Z){I.call(this,Z,{content_div:Z.top_labeltrack},{resize:false});Z.reference_track=this;this.left_offset=200;this.visible_height_px=12;this.container_div.addClass("reference-track");this.content_div.css("background","none");this.content_div.css("min-height","0px");this.content_div.css("border","none");this.data_url=reference_url;this.data_url_extra_params={dbkey:Z.dbkey};this.data_manager=new ReferenceTrackDataManager({data_url:reference_url});this.hide_contents()};o(x.prototype,p.prototype,I.prototype,{build_header_div:function(){},init:function(){this.data_manager.clear();this.enabled=true},can_draw:p.prototype.can_draw,draw_tile:function(ah,ai,ae,ad,aa,aj){var ac=this;if(aj>this.view.canvas_manager.char_width_px){if(ah.data===null){this.hide_contents();return}var ab=ai.canvas;ai.font=ai.canvas.manager.default_font;ai.textAlign="center";ah=ah.data;for(var af=0,ag=ah.length;af<ag;af++){var Z=Math.floor(af*aj);ai.fillText(ah[af],Z,10)}this.show_contents();return new b(ac,aa,ad,ab,ah)}this.hide_contents()}});var i=function(ab,aa,ac){var Z=this;this.display_modes=["Histogram","Line","Filled","Intensity"];this.mode="Histogram";I.call(this,ab,aa,ac);this.hda_ldda=ac.hda_ldda;this.dataset_id=ac.dataset_id;this.original_dataset_id=this.dataset_id;this.left_offset=0;this.config=new B({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"color",label:"Color",type:"color",default_value:get_random_color()},{key:"min_value",label:"Min Value",type:"float",default_value:undefined},{key:"max_value",label:"Max Value",type:"float",default_value:undefined},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:32,hidden:true}],saved_values:ac.prefs,onchange:function(){Z.set_name(Z.prefs.name);Z.vertical_range=Z.prefs.max_value-Z.prefs.min_value;Z.set_min_value(Z.prefs.min_value);Z.set_max_value(Z.prefs.max_value)}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.vertical_range=this.config.values.max_value-this.config.values.min_value};o(i.prototype,p.prototype,I.prototype,{on_resize:function(){this.request_draw(true)},set_min_value:function(Z){this.prefs.min_value=Z;$("#linetrack_"+this.dataset_id+"_minval").text(this.prefs.min_value);this.tile_cache.clear();this.request_draw()},set_max_value:function(Z){this.prefs.max_value=Z;$("#linetrack_"+this.dataset_id+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.request_draw()},predraw_init:function(){var Z=this;Z.vertical_range=undefined;return $.getJSON(Z.data_url,{stats:true,chrom:Z.view.chrom,low:null,high:null,hda_ldda:Z.hda_ldda,dataset_id:Z.dataset_id},function(aa){Z.container_div.addClass("line-track");var ad=aa.data;if(isNaN(parseFloat(Z.prefs.min_value))||isNaN(parseFloat(Z.prefs.max_value))){var ab=ad.min;var af=ad.max;ab=Math.floor(Math.min(0,Math.max(ab,ad.mean-2*ad.sd)));af=Math.ceil(Math.max(0,Math.min(af,ad.mean+2*ad.sd)));Z.prefs.min_value=ab;Z.prefs.max_value=af;$("#track_"+Z.dataset_id+"_minval").val(Z.prefs.min_value);$("#track_"+Z.dataset_id+"_maxval").val(Z.prefs.max_value)}Z.vertical_range=Z.prefs.max_value-Z.prefs.min_value;Z.total_frequency=ad.total_frequency;Z.container_div.find(".yaxislabel").remove();var ae=$("<div/>").text(T(Z.prefs.min_value,3)).make_text_editable({num_cols:6,on_finish:function(ag){$(".tipsy").remove();var ag=parseFloat(ag);if(!isNaN(ag)){Z.set_min_value(ag)}},help_text:"Set min value"}).addClass("yaxislabel bottom").attr("id","linetrack_"+Z.dataset_id+"_minval").prependTo(Z.container_div),ac=$("<div/>").text(T(Z.prefs.max_value,3)).make_text_editable({num_cols:6,on_finish:function(ag){$(".tipsy").remove();var ag=parseFloat(ag);if(!isNaN(ag)){Z.set_max_value(ag)}},help_text:"Set max value"}).addClass("yaxislabel top").attr("id","linetrack_"+Z.dataset_id+"_maxval").prependTo(Z.container_div)})},draw_tile:function(aj,ah,ae,ac,aa,ai){var ab=ah.canvas,Z=this._get_tile_bounds(aa,ac),ad=Z[0],ag=Z[1],af=new H.LinePainter(aj.data,ad,ag,this.prefs,ae);af.draw(ah,ab.width,ab.height,ai);return new b(this,aa,ac,ab,aj.data)},can_subset:function(Z){return false},});var c=function(ac,ab,ae){var aa=this;this.display_modes=["Auto","Histogram","Dense","Squish","Pack"];I.call(this,ac,ab,ae);var ad=get_random_color(),Z=get_random_color([ad,"#ffffff"]);this.config=new B({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:ad},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:Z},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true,help:"Show the number of items in each bin when drawing summary histogram"},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"clear value to set automatically"},{key:"connector_style",label:"Connector style",type:"select",default_value:"fishbones",options:[{label:"Line with arrows",value:"fishbone"},{label:"Arcs",value:"arcs"}]},{key:"mode",type:"string",default_value:this.mode,hidden:true},{key:"height",type:"int",default_value:this.visible_height_px,hidden:true}],saved_values:ae.prefs,onchange:function(){aa.set_name(aa.prefs.name);aa.tile_cache.clear();aa.set_painter_from_config();aa.request_draw()}});this.prefs=this.config.values;this.visible_height_px=this.config.values.height;this.container_div.addClass("feature-track");this.hda_ldda=ae.hda_ldda;this.dataset_id=ae.dataset_id;this.original_dataset_id=ae.dataset_id;this.show_labels_scale=0.001;this.showing_details=false;this.summary_draw_height=30;this.slotters={};this.start_end_dct={};this.left_offset=200;this.set_painter_from_config()};o(c.prototype,p.prototype,I.prototype,{set_painter_from_config:function(){if(this.config.values.connector_style==="arcs"){this.painter=H.ArcLinkedFeaturePainter}else{this.painter=H.LinkedFeaturePainter}},before_draw:function(){this.max_height_px=0},after_show_tile:function(Z){this.max_height_px=Math.max(this.max_height_px,Z.html_elt.height());Z.html_elt.parent().children().css("height",this.max_height_px+"px");var aa=this.max_height_px;if(this.visible_height_px!==0){aa=Math.min(this.max_height_px,this.visible_height_px)}this.tiles_div.css("height",aa+"px")},postdraw_actions:function(ao,aj,ae,ad){I.prototype.postdraw_actions.call(this,ao,ad);var ai=this;if(ai.mode==="Histogram"){var aa=-1;for(var al=0;al<ao.length;al++){var ak=ao[al].max_val;if(ak>aa){aa=ak}}for(var al=0;al<ao.length;al++){var aq=ao[al];if(aq.max_val!==aa){aq.html_elt.remove();ai.draw_helper(true,aj,aq.index,aq.resolution,aq.html_elt.parent(),ae,{more_tile_data:{max:aa}})}}}if(ai.filters_manager){var af=ai.filters_manager.filters;for(var an=0;an<af.length;an++){af[an].update_ui_elt()}var ap=false,Z,ag;for(var al=0;al<ao.length;al++){if(ao[al].data.length){Z=ao[al].data[0];for(var an=0;an<af.length;an++){ag=af[an];if(ag.applies_to(Z)&&ag.min!==ag.max){ap=true;break}}}}if(ai.filters_available!==ap){ai.filters_available=ap;if(!ai.filters_available){ai.filters_manager.hide()}ai.update_icons()}}this.container_div.find(".yaxislabel").remove();var ac=ao[0];if(ac instanceof j){var ah=(this.prefs.histogram_max?this.prefs.histogram_max:ac.max_val),ab=$("<div/>").text(ah).make_text_editable({num_cols:12,on_finish:function(ar){$(".tipsy").remove();var ar=parseFloat(ar);ai.prefs.histogram_max=(!isNaN(ar)?ar:null);ai.tile_cache.clear();ai.request_draw()},help_text:"Set max value; leave blank to use default"}).addClass("yaxislabel top").css("color",this.prefs.label_color);this.container_div.prepend(ab)}if(ac instanceof K){var am=true;for(var al=0;al<ao.length;al++){if(!ao[al].all_slotted){am=false;break}}if(!am){this.action_icons.show_more_rows_icon.show()}else{this.action_icons.show_more_rows_icon.hide()}}else{this.action_icons.show_more_rows_icon.hide()}},update_auto_mode:function(Z){var Z;if(this.mode==="Auto"){if(Z==="no_detail"){Z="feature spans"}else{if(Z==="summary_tree"){Z="coverage histogram"}}this.action_icons.mode_icon.attr("title","Set display mode (now: Auto/"+Z+")")}},incremental_slots:function(ad,Z,ac){var aa=this.view.canvas_manager.dummy_context,ab=this.slotters[ad];if(!ab||(ab.mode!==ac)){ab=new (r.FeatureSlotter)(ad,ac,w,function(ae){return aa.measureText(ae)});this.slotters[ad]=ab}return ab.slot_features(Z)},get_summary_tree_data:function(ad,ag,ab,ao){if(ao>ab-ag){ao=ab-ag}var ak=Math.floor((ab-ag)/ao),an=[],ac=0;var ae=0,af=0,aj,am=0,ah=[],al,ai;var aa=function(ar,aq,at,ap){ar[0]=aq+at*ap;ar[1]=aq+(at+1)*ap};while(am<ao&&ae!==ad.length){var Z=false;for(;am<ao&&!Z;am++){aa(ah,ag,am,ak);for(af=ae;af<ad.length;af++){aj=ad[af].slice(1,3);if(is_overlap(aj,ah)){Z=true;break}}if(Z){break}}data_start_index=af;an[an.length]=al=[ah[0],0];for(;af<ad.length;af++){aj=ad[af].slice(1,3);if(is_overlap(aj,ah)){al[1]++}else{break}}if(al[1]>ac){ac=al[1]}am++}return{max:ac,delta:ak,data:an}},get_mode:function(Z){if(Z.dataset_type==="summary_tree"){mode="summary_tree"}else{if(Z.extra_info==="no_detail"||this.is_overview){mode="no_detail"}else{if(this.view.high-this.view.low>E){mode="Squish"}else{mode="Pack"}}}return mode},get_canvas_height:function(Z,ad,ae,aa){if(ad==="summary_tree"||ad==="Histogram"){return this.summary_draw_height}else{var ac=this.incremental_slots(ae,Z.data,ad);var ab=new (this.painter)(null,null,null,this.prefs,ad);return Math.max(X,ab.get_required_height(ac,aa))}},draw_tile:function(ak,ao,am,ap,au,ag,ab){var an=this,aa=ao.canvas,ad=this._get_tile_bounds(au,ap),ax=ad[0],Z=ad[1],aC=25,ac=this.left_offset;if(am==="summary_tree"||am==="Histogram"){if(ak.dataset_type!=="summary_tree"){var ah=this.get_summary_tree_data(ak.data,ax,Z,200);if(ak.max){ah.max=ak.max}ak=ah}var az=new H.SummaryTreePainter(ak,ax,Z,this.prefs);az.draw(ao,aa.width,aa.height,ag);return new j(an,au,ap,aa,ak.data,ak.max)}var af=[],al=this.slotters[ag].slots;all_slotted=true;if(ak.data){var ai=this.filters_manager.filters;for(var aq=0,at=ak.data.length;aq<at;aq++){var ae=ak.data[aq];var ar=false;var aj;for(var aw=0,aB=ai.length;aw<aB;aw++){aj=ai[aw];aj.update_attrs(ae);if(!aj.keep(ae)){ar=true;break}}if(!ar){af.push(ae);if(!(ae[0] in al)){all_slotted=false}}}}var aA=(this.filters_manager.alpha_filter?new y(this.filters_manager.alpha_filter):null);var ay=(this.filters_manager.height_filter?new y(this.filters_manager.height_filter):null);var az=new (this.painter)(af,ax,Z,this.prefs,am,aA,ay,ab);var av=null;ao.fillStyle=this.prefs.block_color;ao.font=ao.canvas.manager.default_font;ao.textAlign="right";if(ak.data){av=az.draw(ao,aa.width,aa.height,ag,al);av.translation=-ac}return new K(an,au,ap,aa,ak.data,ag,am,ak.message,all_slotted,av)},data_and_mode_compatible:function(Z,aa){if(aa==="Auto"){return true}else{if(Z.extra_info==="no_detail"||Z.dataset_type==="summary_tree"){return false}else{return true}}},can_subset:function(Z){if(Z.dataset_type==="summary_tree"||Z.message||Z.extra_info==="no_detail"){return false}return true},});var N=function(aa,Z,ab){c.call(this,aa,Z,ab);this.config=new B({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block color",type:"color",default_value:get_random_color()},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"mode",type:"string",default_value:this.mode,hidden:true},],saved_values:ab.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=H.ReadPainter};o(N.prototype,p.prototype,I.prototype,c.prototype);var Q=function(ab,aa,ad){c.call(this,ab,aa,ad);var ac=get_random_color(),Z=get_random_color([ac,"#ffffff"]);this.config=new B({track:this,params:[{key:"name",label:"Name",type:"text",default_value:this.name},{key:"block_color",label:"Block and sense strand color",type:"color",default_value:ac},{key:"reverse_strand_color",label:"Antisense strand color",type:"color",default_value:Z},{key:"label_color",label:"Label color",type:"color",default_value:"black"},{key:"show_insertions",label:"Show insertions",type:"bool",default_value:false},{key:"show_differences",label:"Show differences only",type:"bool",default_value:true},{key:"show_counts",label:"Show summary counts",type:"bool",default_value:true},{key:"histogram_max",label:"Histogram maximum",type:"float",default_value:null,help:"Clear value to set automatically"},{key:"mode",type:"string",default_value:this.mode,hidden:true},],saved_values:ad.prefs,onchange:function(){this.track.set_name(this.track.prefs.name);this.track.tile_cache.clear();this.track.request_draw()}});this.prefs=this.config.values;this.painter=H.ReadPainter;this.update_icons()};o(Q.prototype,p.prototype,I.prototype,c.prototype);R.View=W;R.DrawableGroup=L;R.LineTrack=i;R.FeatureTrack=c;R.ReadTrack=Q;R.VcfTrack=N;R.CompositeTrack=g};var slotting_module=function(c,b){var e=c("class").extend;var d=2,a=5;b.FeatureSlotter=function(i,h,f,g){this.slots={};this.start_end_dct={};this.w_scale=i;this.mode=h;this.include_label=(h==="Pack");this.max_rows=f;this.measureText=g};e(b.FeatureSlotter.prototype,{slot_features:function(m){var p=this.w_scale,h=this.start_end_dct,x=[],z=[],n=0,y=this.max_rows;for(var v=0,w=m.length;v<w;v++){var k=m[v],o=k[0];if(this.slots[o]!==undefined){n=Math.max(n,this.slots[o]);z.push(this.slots[o])}else{x.push(v)}}var q=function(E,F){for(var D=0;D<=y;D++){var B=false,G=h[D];if(G!==undefined){for(var A=0,C=G.length;A<C;A++){var i=G[A];if(F>i[0]&&E<i[1]){B=true;break}}}if(!B){return D}}return -1};for(var v=0,w=x.length;v<w;v++){var k=m[x[v]],o=k[0],t=k[1],f=k[2],r=k[3],g=Math.floor(t*p),l=Math.ceil(f*p),u=this.measureText(r).width,j;if(r!==undefined&&this.include_label){u+=(d+a);if(g-u>=0){g-=u;j="left"}else{l+=u;j="right"}}var s=q(g,l);if(s>=0){if(h[s]===undefined){h[s]=[]}h[s].push([g,l]);this.slots[o]=s;n=Math.max(n,s)}}return n+1}})};var painters_module=function(i,x){var u=i("class").extend;var p=function(I,A,G,z,F,D){if(D===undefined){D=4}var C=z-A;var B=F-G;var E=Math.floor(Math.sqrt(C*C+B*B)/D);var J=C/E;var H=B/E;var y;for(y=0;y<E;y++,A+=J,G+=H){if(y%2!==0){continue}I.fillRect(A,G,D,1)}};var q=function(B,A,z,E){var D=A-E/2,C=A+E/2,F=z-Math.sqrt(E*3/2);B.beginPath();B.moveTo(D,F);B.lineTo(C,F);B.lineTo(A,z);B.lineTo(D,F);B.strokeStyle=this.fillStyle;B.fill();B.stroke();B.closePath()};var d=function(y){this.default_val=(y?y:1)};d.prototype.gen_val=function(y){return this.default_val};var l=function(A,C,y,z,B){this.data=A;this.view_start=C;this.view_end=y;this.prefs=u({},this.default_prefs,z);this.mode=B};l.prototype.default_prefs={};l.prototype.draw=function(z,A,y,B){};var v=function(A,C,y,z,B){l.call(this,A,C,y,z,B)};v.prototype.default_prefs={show_counts:false};v.prototype.draw=function(L,z,K,M){var E=this.view_start,N=this.view_end-this.view_start,I=this.data.data,G=(this.prefs.histogram_max?this.prefs.histogram_max:this.data.max),B=K;delta_x_px=Math.ceil(this.data.delta*M);L.save();for(var C=0,D=I.length;C<D;C++){var H=Math.floor((I[C][0]-E)*M);var F=I[C][1];if(!F){continue}var J=F/G*K;if(F!==0&&J<1){J=1}L.fillStyle=this.prefs.block_color;L.fillRect(H,B-J,delta_x_px,J);var A=4;if(this.prefs.show_counts&&(L.measureText(F).width+A)<delta_x_px){L.fillStyle=this.prefs.label_color;L.textAlign="center";L.fillText(F,H+(delta_x_px/2),10)}}L.restore()};var b=function(y,C,E,F,A){l.call(this,y,C,E,F,A);if(this.prefs.min_value===undefined){var G=Infinity;for(var z=0,B=this.data.length;z<B;z++){G=Math.min(G,this.data[z][1])}this.prefs.min_value=G}if(this.prefs.max_value===undefined){var D=-Infinity;for(var z=0,B=this.data.length;z<B;z++){D=Math.max(D,this.data[z][1])}this.prefs.max_value=D}};b.prototype.default_prefs={min_value:undefined,max_value:undefined,mode:"Histogram",color:"#000",overflow_color:"#F66"};b.prototype.draw=function(S,Q,N,D){var I=false,K=this.prefs.min_value,F=this.prefs.max_value,M=F-K,B=N,C=this.view_start,P=this.view_end-this.view_start,L=this.mode,aa=this.data;S.save();var ac=Math.round(N+K/M*N);if(L!=="Intensity"){S.fillStyle="#aaa";S.fillRect(0,ac,Q,1)}S.beginPath();var Y,G,E;if(aa.length>1){E=Math.ceil((aa[1][0]-aa[0][0])*D)}else{E=10}var A=parseInt(this.prefs.color.slice(1),16),H=(A&16711680)>>16,R=(A&65280)>>8,V=A&255;for(var T=0,U=aa.length;T<U;T++){S.fillStyle=S.strokeStyle=this.prefs.color;Y=Math.round((aa[T][0]-C)*D);G=aa[T][1];var W=false,J=false;if(G===null){if(I&&L==="Filled"){S.lineTo(Y,B)}I=false;continue}if(G<K){J=true;G=K}else{if(G>F){W=true;G=F}}if(L==="Histogram"){G=Math.round(G/M*B);S.fillRect(Y,ac,E,-G)}else{if(L==="Intensity"){var z=(G-K)/M,O=Math.round(H+(255-H)*(1-z)),X=Math.round(R+(255-R)*(1-z)),ab=Math.round(V+(255-V)*(1-z));S.fillStyle="rgb("+O+","+X+","+ab+")";S.fillRect(Y,0,E,B)}else{G=Math.round(B-(G-K)/M*B);if(I){S.lineTo(Y,G)}else{I=true;if(L==="Filled"){S.moveTo(Y,B);S.lineTo(Y,G)}else{S.moveTo(Y,G)}}}}S.fillStyle=this.prefs.overflow_color;if(W||J){var Z;if(L==="Histogram"||L==="Intensity"){Z=E}else{Y-=2;Z=4}if(W){S.fillRect(Y,0,Z,3)}if(J){S.fillRect(Y,B-3,Z,3)}}S.fillStyle=this.prefs.color}if(L==="Filled"){if(I){S.lineTo(Y,ac);S.lineTo(0,ac)}S.fill()}else{S.stroke()}S.restore()};var m=function(y){this.feature_positions={};this.slot_height=y;this.translation=0;this.y_translation=0};m.prototype.map_feature_data=function(z,B,y,A){if(!this.feature_positions[B]){this.feature_positions[B]=[]}this.feature_positions[B].push({data:z,x_start:y,x_end:A})};m.prototype.get_feature_data=function(z,D){var C=Math.floor((D-this.y_translation)/this.slot_height),B;if(!this.feature_positions[C]){return null}z+=this.translation;for(var A=0;A<this.feature_positions[C].length;A++){B=this.feature_positions[C][A];if(z>=B.x_start&&z<=B.x_end){return B.data}}};var o=function(A,D,y,z,C,E,B){l.call(this,A,D,y,z,C);this.alpha_scaler=(E?E:new d());this.height_scaler=(B?B:new d())};o.prototype.default_prefs={block_color:"#FFF",connector_color:"#FFF"};u(o.prototype,{get_required_height:function(A,z){var y=y_scale=this.get_row_height(),B=this.mode;if(B==="no_detail"||B==="Squish"||B==="Pack"){y=A*y_scale}return y+this.get_top_padding(z)+this.get_bottom_padding(z)},get_top_padding:function(y){return 0},get_bottom_padding:function(y){return Math.max(Math.round(this.get_row_height()/2),5)},draw:function(K,I,G,E,F){var Q=this.data,D=this.view_start,M=this.view_end;K.save();K.fillStyle=this.prefs.block_color;K.textAlign="right";var H=this.view_end-this.view_start,L=this.get_row_height(),P=new m(L),B;for(var N=0,O=Q.length;N<O;N++){var A=Q[N],C=A[0],J=A[1],y=A[2],z=(F&&F[C]!==undefined?F[C]:null);if((J<M&&y>D)&&(this.mode==="Dense"||z!==null)){B=this.draw_element(K,this.mode,A,z,D,M,E,L,I);P.map_feature_data(A,z,B[0],B[1])}}K.restore();P.y_translation=this.get_top_padding(I);return P},draw_element:function(E,A,G,C,B,D,F,z,y){console.log("WARNING: Unimplemented function.");return[0,0]}});var c=10,h=3,k=5,w=10,f=1,s=9,e=3,a=9,j=2,g="#ccc";var r=function(A,D,y,z,C,E,B){o.call(this,A,D,y,z,C,E,B);this.draw_background_connector=true;this.draw_individual_connectors=false};u(r.prototype,o.prototype,{get_row_height:function(){var z=this.mode,y;if(z==="Dense"){y=c}else{if(z==="no_detail"){y=h}else{if(z==="Squish"){y=k}else{y=w}}}return y},draw_element:function(M,D,X,H,O,ai,am,ao,y){var T=X[0],ak=X[1],ad=X[2]-1,Q=X[3],K=X[4],ae=Math.floor(Math.max(0,(ak-O)*am)),N=Math.ceil(Math.min(y,Math.max(0,(ad-O)*am))),ac=ae,an=N,aa=(D==="Dense"?0:(0+H))*ao+this.get_top_padding(y),L,ag,R=null,aq=null,B=B=(!K||K==="+"||K==="."?this.prefs.block_color:this.prefs.reverse_strand_color);label_color=this.prefs.label_color;M.globalAlpha=this.alpha_scaler.gen_val(X);if(D==="Dense"){H=1}if(D==="no_detail"){M.fillStyle=B;M.fillRect(ae,aa+5,N-ae,f)}else{var Z=X[5],af=X[6],C=X[7],V=true;if(Z&&af){R=Math.floor(Math.max(0,(Z-O)*am));aq=Math.ceil(Math.min(y,Math.max(0,(af-O)*am)))}var al,U;if(D==="Squish"){al=1;U=e;V=false}else{if(D==="Dense"){al=5;U=s}else{al=5;U=a}}if(!C){M.fillStyle=B;M.fillRect(ae,aa+1,N-ae,U);if(K&&V){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand_inv")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand_inv")}}M.fillRect(ae,aa+1,N-ae,U)}}else{var J,W;if(D==="Squish"||D==="Dense"){J=aa+Math.floor(e/2)+1;W=1}else{if(K){J=aa;W=U}else{J+=(e/2)+1;W=1}}if(this.draw_background_connector){if(D==="Squish"||D==="Dense"){M.fillStyle=g}else{if(K){if(K==="+"){M.fillStyle=M.canvas.manager.get_pattern("right_strand")}else{if(K==="-"){M.fillStyle=M.canvas.manager.get_pattern("left_strand")}}}else{M.fillStyle=g}}M.fillRect(ae,J,N-ae,W)}var E;for(var aj=0,A=C.length;aj<A;aj++){var F=C[aj],z=Math.floor(Math.max(0,(F[0]-O)*am)),Y=Math.ceil(Math.min(y,Math.max((F[1]-1-O)*am))),S,ab;if(z>Y){continue}M.fillStyle=B;M.fillRect(z,aa+(U-al)/2+1,Y-z,al);if(R!==undefined&&af>Z&&!(z>aq||Y<R)){var ah=Math.max(z,R),I=Math.min(Y,aq);M.fillRect(ah,aa+1,I-ah,U)}if(this.draw_individual_connectors&&S){this.draw_connector(M,S,ab,z,Y,aa)}S=z;ab=Y}if(D==="Pack"){M.globalAlpha=1;M.fillStyle="white";var G=this.height_scaler.gen_val(X),P=Math.ceil(U*G),ap=Math.round((U-P)/2);if(G!==1){M.fillRect(ae,J+1,N-ae,ap);M.fillRect(ae,J+U-ap+1,N-ae,ap)}}}M.globalAlpha=1;if(D==="Pack"&&ak>O){M.fillStyle=label_color;if(O===0&&ae-M.measureText(Q).width<0){M.textAlign="left";M.fillText(Q,N+j,aa+8);an+=M.measureText(Q).width+j}else{M.textAlign="right";M.fillText(Q,ae-j,aa+8);ac-=M.measureText(Q).width+j}}}M.globalAlpha=1;return[ac,an]}});var t=function(B,E,y,A,D,F,C,z){o.call(this,B,E,y,A,D,F,C);this.ref_seq=(z?z.data:null)};u(t.prototype,o.prototype,{get_row_height:function(){var y,z=this.mode;if(z==="Dense"){y=c}else{if(z==="Squish"){y=k}else{y=w;if(this.prefs.show_insertions){y*=2}}}return y},draw_read:function(K,A,ag,V,L,aa,ad,C,B,M){K.textAlign="center";var J=this,R=[L,aa],Z=0,W=0,D=0,F=K.canvas.manager.char_width_px,y=(B==="+"?this.prefs.block_color:this.prefs.reverse_strand_color);var O=[];if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){D=Math.round(ag/2)}if(!C){C=[[0,M.length]]}for(var G=0,I=C.length;G<I;G++){var z=C[G],E="MIDNSHP=X"[z[0]],S=z[1];if(E==="H"||E==="S"){Z-=S}var U=ad+Z,Y=Math.floor(Math.max(0,(U-L)*ag)),ab=Math.floor(Math.max(0,(U+S-L)*ag));if(Y===ab){ab+=1}switch(E){case"H":break;case"S":case"M":case"=":if(is_overlap([U,U+S],R)){var N=M.slice(W,W+S);if(D>0){K.fillStyle=y;K.fillRect(Y-D,V+1,ab-Y,9);K.fillStyle=g;for(var af=0,H=N.length;af<H;af++){if(this.prefs.show_differences){if(this.ref_seq){var P=this.ref_seq[U-L+af];if(!P||P.toLowerCase()===N[af].toLowerCase()){continue}}else{continue}}if(U+af>=L&&U+af<=aa){var X=Math.floor(Math.max(0,(U+af-L)*ag));K.fillText(N[af],X,V+9)}}}else{K.fillStyle=y;K.fillRect(Y,V+4,ab-Y,e)}}W+=S;Z+=S;break;case"N":K.fillStyle=g;K.fillRect(Y-D,V+5,ab-Y,1);Z+=S;break;case"D":K.fillStyle="red";K.fillRect(Y-D,V+4,ab-Y,3);Z+=S;break;case"P":break;case"I":var ah=Y-D;if(is_overlap([U,U+S],R)){var N=M.slice(W,W+S);if(this.prefs.show_insertions){var T=Y-(ab-Y)/2;if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){K.fillStyle="yellow";K.fillRect(T-D,V-9,ab-Y,9);O[O.length]={type:"triangle",data:[ah,V+4,5]};K.fillStyle=g;switch(compute_overlap([U,U+S],R)){case (OVERLAP_START):N=N.slice(L-U);break;case (OVERLAP_END):N=N.slice(0,U-aa);break;case (CONTAINED_BY):break;case (CONTAINS):N=N.slice(L-U,U-aa);break}for(var af=0,H=N.length;af<H;af++){var X=Math.floor(Math.max(0,(U+af-L)*ag));K.fillText(N[af],X-(ab-Y)/2,V)}}else{K.fillStyle="yellow";K.fillRect(T,V+(this.mode!=="Dense"?2:5),ab-Y,(A!=="Dense"?e:s))}}else{if((A==="Pack"||this.mode==="Auto")&&M!==undefined&&ag>F){O.push({type:"text",data:[N.length,ah,V+9]})}else{}}}W+=S;break;case"X":W+=S;break}}K.fillStyle="yellow";var Q,ai,ae;for(var ac=0;ac<O.length;ac++){Q=O[ac];ai=Q.type;ae=Q.data;if(ai==="text"){K.save();K.font="bold "+K.font;K.fillText(ae[0],ae[1],ae[2]);K.restore()}else{if(ai==="triangle"){q(K,ae[0],ae[1],ae[2])}}}},draw_element:function(R,M,E,B,U,z,I,S,P){var H=E[0],Q=E[1],A=E[2],J=E[3],D=Math.floor(Math.max(0,(Q-U)*I)),F=Math.ceil(Math.min(P,Math.max(0,(A-U)*I))),C=(M==="Dense"?0:(0+B))*S,G=this.prefs.label_color,O=0;if((M==="Pack"||this.mode==="Auto")&&I>R.canvas.manager.char_width_px){var O=Math.round(I/2)}if(E[5] instanceof Array){var N=Math.floor(Math.max(0,(E[4][0]-U)*I)),L=Math.ceil(Math.min(P,Math.max(0,(E[4][1]-U)*I))),K=Math.floor(Math.max(0,(E[5][0]-U)*I)),y=Math.ceil(Math.min(P,Math.max(0,(E[5][1]-U)*I)));if(E[4][1]>=U&&E[4][0]<=z&&E[4][2]){this.draw_read(R,M,I,C,U,z,E[4][0],E[4][2],E[4][3],E[4][4])}if(E[5][1]>=U&&E[5][0]<=z&&E[5][2]){this.draw_read(R,M,I,C,U,z,E[5][0],E[5][2],E[5][3],E[5][4])}if(K>L){R.fillStyle=g;p(R,L-O,C+5,K-O,C+5)}}else{this.draw_read(R,M,I,C,U,z,Q,E[4],E[5],E[6])}if(M==="Pack"&&Q>U&&J!=="."){R.fillStyle=this.prefs.label_color;var T=1;if(T===0&&D-R.measureText(J).width<0){R.textAlign="left";R.fillText(J,F+j-O,C+8)}else{R.textAlign="right";R.fillText(J,D-j-O,C+8)}}return[0,0]}});var n=function(A,D,y,z,C,E,B){r.call(this,A,D,y,z,C,E,B);this.longest_feature_length=this.calculate_longest_feature_length();this.draw_background_connector=false;this.draw_individual_connectors=true};u(n.prototype,o.prototype,r.prototype,{calculate_longest_feature_length:function(){var z=0;for(var C=0,y=this.data.length;C<y;C++){var B=this.data[C],A=B[1],D=B[2];z=Math.max(z,D-A)}return z},get_top_padding:function(z){var y=this.view_end-this.view_start,A=z/y;return Math.min(128,Math.ceil((this.longest_feature_length/2)*A))},draw_connector:function(G,B,F,H,E,D){var y=(F+H)/2,C=H-y;var A=Math.PI,z=0;if(C>0){G.beginPath();G.arc(y,D,H-y,Math.PI,0);G.stroke()}}});x.Scaler=d;x.SummaryTreePainter=v;x.LinePainter=b;x.LinkedFeaturePainter=r;x.ReadPainter=t;x.ArcLinkedFeaturePainter=n};(function(d){var c={};var b=function(e){return c[e]};var a=function(f,g){var e={};g(b,e);c[f]=e};a("class",class_module);a("slotting",slotting_module);a("painters",painters_module);a("trackster",trackster_module);for(key in c.trackster){d[key]=c.trackster[key]}})(window);
\ No newline at end of file
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd static/scripts/packed/viz/visualization.js
--- a/static/scripts/packed/viz/visualization.js
+++ b/static/scripts/packed/viz/visualization.js
@@ -1,1 +1,1 @@
-var Genome=Backbone.Model.extend({defaults:{name:null,key:null,chroms_info:null},get_chroms_info:function(){return this.attributes.chroms_info.chrom_info}});var BrowserBookmark=Backbone.Model.extend({defaults:{chrom:null,start:0,end:0,note:""}});var BrowserBookmarks=Backbone.Collection.extend({model:BrowserBookmark});var Visualization=Backbone.RelationalModel.extend({defaults:{id:"",title:"",type:"",dbkey:"",datasets:[]},url:function(){return galaxy_paths.get("visualization_url")},save:function(){return $.ajax({url:this.url(),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(this)}})}});var TracksterVisualization=Visualization.extend({defaults:{bookmarks:[],viewport:{}}});var CircsterVisualization=Visualization.extend({});var Dataset=Backbone.Model.extend({defaults:{id:"",type:"",name:"",hda_ldda:""}});var HistogramDataset=Backbone.Model.extend({initialize:function(a){this.attributes.data=a;this.attributes.max=_.max(a,function(b){if(!b||typeof b==="string"){return 0}return b[1]})[1]}});var TrackConfig=Backbone.Model.extend({});var CircsterHistogramDatasetLayout=Backbone.Model.extend({chroms_layout:function(){var b=this.attributes.genome.get_chroms_info(),d=d3.layout.pie().value(function(f){return f.len}).sort(null),e=d(b),a=this.attributes.total_gap/b.length,c=_.map(e,function(h,g){var f=h.endAngle-a;h.endAngle=(f>h.startAngle?f:h.startAngle);return h});return c},chrom_data_layout:function(j,b,g,f,h){if(!b||typeof b==="string"){return null}var d=b[0],i=b[3],c=d3.scale.linear().domain([0,h]).range([g,f]),e=d3.layout.pie().value(function(k){return i}).startAngle(j.startAngle).endAngle(j.endAngle),a=e(d);_.each(d,function(k,l){a[l].outerRadius=c(k[1])});return a}});var CircsterView=Backbone.View.extend({className:"circster",initialize:function(a){this.width=a.width;this.height=a.height;this.total_gap=a.total_gap;this.genome=a.genome;this.dataset=a.dataset;this.radius_start=a.radius_start;this.dataset_arc_height=a.dataset_arc_height},render:function(){var d=this.radius_start,e=this.dataset_arc_height,j=new CircsterHistogramDatasetLayout({genome:this.genome,total_gap:this.total_gap}),i=j.chroms_layout(),g=_.zip(i,this.dataset.attributes.data),h=this.dataset.attributes.max,b=_.map(g,function(m){var n=m[0],l=m[1];return j.chrom_data_layout(n,l,d,d+e,h)});var c=d3.select(this.$el[0]).append("svg").attr("width",this.width).attr("height",this.height).append("g").attr("transform","translate("+this.width/2+","+this.height/2+")");var k=c.append("g").attr("id","inner-arc"),f=d3.svg.arc().innerRadius(d).outerRadius(d+e),a=k.selectAll("#inner-arc>path").data(i).enter().append("path").attr("d",f).style("stroke","#ccc").style("fill","#ccc").append("title").text(function(l){return l.data.chrom});_.each(b,function(l){if(!l){return}var o=c.append("g"),n=d3.svg.arc().innerRadius(d),m=o.selectAll("path").data(l).enter().append("path").attr("d",n).style("stroke","red").style("fill","red")})}});var TrackBrowserRouter=Backbone.Router.extend({initialize:function(b){this.view=b.view;this.route(/([\w]+)$/,"change_location");this.route(/([\w]+\:[\d,]+-[\d,]+)$/,"change_location");var a=this;a.view.on("navigate",function(c){a.navigate(c)})},change_location:function(a){this.view.go_to(a)}});var add_datasets=function(a,c,b){$.ajax({url:a,data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(d){show_modal("Select datasets for new tracks",d,{Cancel:function(){hide_modal()},Add:function(){var e=[];$("input[name=id]:checked,input[name=ldda_ids]:checked").each(function(){var f,g=$(this).val();if($(this).attr("name")==="id"){f={hda_id:g}}else{f={ldda_id:g}}e[e.length]=$.ajax({url:c,data:f,dataType:"json",})});$.when.apply($,e).then(function(){var f=(arguments[0] instanceof Array?$.map(arguments,function(g){return g[0]}):[arguments[0]]);b(f)});hide_modal()}})}})};
\ No newline at end of file
+var Cache=Backbone.Model.extend({defaults:{num_elements:20,obj_cache:{},key_ary:[]},get_elt:function(b){var c=this.attributes.obj_cache,d=this.attributes.key_ary,a=d.indexOf(b);if(a!==-1){if(c[b].stale){d.splice(a,1);delete c[b]}else{this.move_key_to_end(b,a)}}return c[b]},set_elt:function(b,d){var e=this.attributes.obj_cache,f=this.attributes.key_ary,c=this.attributes.num_elements;if(!e[b]){if(f.length>=c){var a=f.shift();delete e[a]}f.push(b)}e[b]=d;return d},move_key_to_end:function(b,a){this.attributes.key_ary.splice(a,1);this.attributes.key_ary.push(b)},clear:function(){this.attributes.obj_cache={};this.attributes.key_ary=[]},size:function(){return this.attributes.key_ary.length}});var GenomeDataManager=Cache.extend({defaults:_.extend({},Cache.prototype.defaults,{dataset:null,filters_manager:null,data_url:null,data_mode_compatible:function(a,b){return true},can_subset:function(a){return false}}),load_data:function(h,g,b,f){var d={chrom:h.get("chrom"),low:h.get("start"),high:h.get("end"),mode:g,resolution:b};dataset=this.get("dataset");if(dataset){d.dataset_id=dataset.id;d.hda_ldda=dataset.get("hda_ldda")}$.extend(d,f);var j=this.get("filters_manager");if(j){var k=[];var a=j.filters;for(var e=0;e<a.length;e++){k.push(a[e].name)}d.filter_cols=JSON.stringify(k)}var c=this;return $.getJSON(this.get("data_url"),d,function(i){c.set_data(h,i)})},get_data:function(g,f,c,e){var h=this.get_elt(g);if(h&&(is_deferred(h)||this.get("data_mode_compatible")(h,f))){return h}var j=this.get("key_ary"),b=this.get("obj_cache"),k,g,a,f,h;for(var d=0;d<j.length;d++){a=new GenomeRegion(j[d]);if(a.contains(g)){var h=b[k];if(is_deferred(h)||(this.get("data_mode_compatible")(h,f)&&this.get("can_subset")(h))){this.move_key_to_end(k,d);return h}}}h=this.load_data(g,f,c,e);this.set_data(g,h);return h},set_data:function(b,a){this.set_elt(b,a)},DEEP_DATA_REQ:"deep",BROAD_DATA_REQ:"breadth",get_more_data:function(h,g,c,f,d){var j=this.get_elt(h);if(!(j&&this.get("data_mode_compatible")(j,g))){console.log("ERROR: no current data for: ",dataset,h.toString(),g,c,f);return}j.stale=true;var b=h.get("start");if(d===this.DEEP_DATA_REQ){$.extend(f,{start_val:j.data.length+1})}else{if(d===this.BROAD_DATA_REQ){b=(j.max_high?j.max_high:j.data[j.data.length-1][2])+1}}var i=h.copy().set("start",b);var a=this,e=this.load_data(i,g,c,f);new_data_available=$.Deferred();this.set_data(h,new_data_available);$.when(e).then(function(k){if(k.data){k.data=j.data.concat(k.data);if(k.max_low){k.max_low=j.max_low}if(k.message){k.message=k.message.replace(/[0-9]+/,k.data.length)}}a.set_data(h,k);new_data_available.resolve(k)});return new_data_available},get_elt:function(a){return Cache.prototype.get_elt.call(this,a.toString())},set_elt:function(b,a){return Cache.prototype.set_elt.call(this,b.toString(),a)}});var ReferenceTrackDataManager=GenomeDataManager.extend({load_data:function(a,d,e,b,c){if(b>1){return{data:null}}return GenomeDataManager.prototype.load_data.call(this,a,d,e,b,c)}});var Genome=Backbone.Model.extend({defaults:{name:null,key:null,chroms_info:null},get_chroms_info:function(){return this.attributes.chroms_info.chrom_info}});var GenomeRegion=Backbone.Model.extend({defaults:{chrom:null,start:0,end:0,DIF_CHROMS:1000,BEFORE:1001,CONTAINS:1002,OVERLAP_START:1003,OVERLAP_END:1004,CONTAINED_BY:1005,AFTER:1006},initialize:function(b){if(!this.get("chrom")&&!this.get("start")&&!this.get("end")&&"as_str" in b){var d=b.as_str.split(":"),c=d[0],d=d.split("-"),e=d[0],a=d[1];this.set("chrom",c);this.set("start",e);this.set("end",a)}},copy:function(){return new GenomeRegion({chrom:this.get("chrom"),start:this.get("start"),end:this.get("end")})},toString:function(){return this.get("chrom")+":"+this.get("start")+"-"+this.get("end")},compute_overlap:function(h){var b=this.get("chrom"),g=h.get("chrom"),f=this.get("start"),d=h.get("start"),e=this.get("end"),c=h.get("end"),a;if(b&&g&&b!==g){return this.get("DIF_CHROMS")}if(f<d){if(e<d){a=this.get("BEFORE")}else{if(e<=c){a=this.get("OVERLAP_START")}else{a=this.get("CONTAINS")}}}else{if(f>c){a=this.get("AFTER")}else{if(e<=c){a=this.get("CONTAINED_BY")}else{a=this.get("OVERLAP_END")}}}return a},contains:function(a){return this.compute_overlap(a)===this.get("CONTAINS")},overlaps:function(a){return _.intersection([this.compute_overlap(a)],[this.get("DIF_CHROMS"),this.get("BEFORE"),this.get("AFTER")]).length===0}});var BrowserBookmark=Backbone.Model.extend({defaults:{region:null,note:""}});var BrowserBookmarks=Backbone.Collection.extend({model:BrowserBookmark});var Visualization=Backbone.RelationalModel.extend({defaults:{id:"",title:"",type:"",dbkey:"",datasets:[]},url:function(){return galaxy_paths.get("visualization_url")},save:function(){return $.ajax({url:this.url(),type:"POST",dataType:"json",data:{vis_json:JSON.stringify(this)}})}});var TracksterVisualization=Visualization.extend({defaults:{bookmarks:[],viewport:{}}});var CircsterVisualization=Visualization.extend({});var Dataset=Backbone.Model.extend({defaults:{id:"",type:"",name:"",hda_ldda:""}});var HistogramDataset=Backbone.Model.extend({initialize:function(a){this.attributes.data=a;this.attributes.max=_.max(a,function(b){if(!b||typeof b==="string"){return 0}return b[1]})[1]}});var TrackConfig=Backbone.Model.extend({});var CircsterHistogramDatasetLayout=Backbone.Model.extend({chroms_layout:function(){var b=this.attributes.genome.get_chroms_info(),d=d3.layout.pie().value(function(f){return f.len}).sort(null),e=d(b),a=this.attributes.total_gap/b.length,c=_.map(e,function(h,g){var f=h.endAngle-a;h.endAngle=(f>h.startAngle?f:h.startAngle);return h});return c},chrom_data_layout:function(j,b,g,f,h){if(!b||typeof b==="string"){return null}var d=b[0],i=b[3],c=d3.scale.linear().domain([0,h]).range([g,f]),e=d3.layout.pie().value(function(k){return i}).startAngle(j.startAngle).endAngle(j.endAngle),a=e(d);_.each(d,function(k,l){a[l].outerRadius=c(k[1])});return a}});var CircsterView=Backbone.View.extend({className:"circster",initialize:function(a){this.width=a.width;this.height=a.height;this.total_gap=a.total_gap;this.genome=a.genome;this.dataset=a.dataset;this.radius_start=a.radius_start;this.dataset_arc_height=a.dataset_arc_height},render:function(){var d=this.radius_start,e=this.dataset_arc_height,j=new CircsterHistogramDatasetLayout({genome:this.genome,total_gap:this.total_gap}),i=j.chroms_layout(),g=_.zip(i,this.dataset.attributes.data),h=this.dataset.attributes.max,b=_.map(g,function(m){var n=m[0],l=m[1];return j.chrom_data_layout(n,l,d,d+e,h)});var c=d3.select(this.$el[0]).append("svg").attr("width",this.width).attr("height",this.height).append("g").attr("transform","translate("+this.width/2+","+this.height/2+")");var k=c.append("g").attr("id","inner-arc"),f=d3.svg.arc().innerRadius(d).outerRadius(d+e),a=k.selectAll("#inner-arc>path").data(i).enter().append("path").attr("d",f).style("stroke","#ccc").style("fill","#ccc").append("title").text(function(l){return l.data.chrom});_.each(b,function(l){if(!l){return}var o=c.append("g"),n=d3.svg.arc().innerRadius(d),m=o.selectAll("path").data(l).enter().append("path").attr("d",n).style("stroke","red").style("fill","red")})}});var TrackBrowserRouter=Backbone.Router.extend({initialize:function(b){this.view=b.view;this.route(/([\w]+)$/,"change_location");this.route(/([\w]+\:[\d,]+-[\d,]+)$/,"change_location");var a=this;a.view.on("navigate",function(c){a.navigate(c)})},change_location:function(a){this.view.go_to(a)}});var add_datasets=function(a,c,b){$.ajax({url:a,data:{"f-dbkey":view.dbkey},error:function(){alert("Grid failed")},success:function(d){show_modal("Select datasets for new tracks",d,{Cancel:function(){hide_modal()},Add:function(){var e=[];$("input[name=id]:checked,input[name=ldda_ids]:checked").each(function(){var f,g=$(this).val();if($(this).attr("name")==="id"){f={hda_id:g}}else{f={ldda_id:g}}e[e.length]=$.ajax({url:c,data:f,dataType:"json",})});$.when.apply($,e).then(function(){var f=(arguments[0] instanceof Array?$.map(arguments,function(g){return g[0]}):[arguments[0]]);b(f)});hide_modal()}})}})};
\ No newline at end of file
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd static/scripts/viz/paramamonster.js
--- a/static/scripts/viz/paramamonster.js
+++ b/static/scripts/viz/paramamonster.js
@@ -3,10 +3,15 @@
* genomic visualization.
*/
+/**
+ * --- Models ---
+ */
+
var ToolParameterTree = Backbone.Model.extend({
defaults: {
tool: null,
- samples: 4
+ params: null,
+ num_samples: 4
},
@@ -17,64 +22,189 @@
// Valid inputs for tree are number, select parameters.
var tool = this.get('tool'),
- samples = this.get('samples'),
- inputs = tool.get('inputs').filter(function(input) {
- return ( ['number', 'select'].indexOf(input.get('type')) !== -1 );
+ num_samples = this.get('num_samples'),
+ params_samples = tool.get('inputs').map(function(input) {
+ return input.get_samples(num_samples);
}),
- inputs_names = _.map(inputs, function(i) { return i.get('name')});
- // Sample from all valid inputs.
- sampling = _.map(inputs, function(input) {
- var type = input.get('type');
- if (type === 'number') {
- return d3.scale.linear().domain([input.get('min'), input.get('max')]).ticks(samples);
- }
- else if (type === 'select') {
- return _.map(input.get('options'), function(option) {
- return option[0];
- });
- }
+ filtered_params_samples = _.filter(params_samples, function(param_sample) {
+ return param_sample.get('samples').length !== 0;
});
/**
- * Returns tree data.
+ * Returns tree data. Params_sampling is an array of
+ *
*/
- var create_tree_data = function(param_names, param_settings, index) {
- // Terminate when last parameter setting is reached.
- if (param_settings.length - 1 === index) {
- return _.map(param_settings[index], function(setting) {
+ var create_tree_data = function(params_samples, index) {
+ var
+ param_samples = params_samples[index],
+ param = param_samples.get('param'),
+ param_name = param.get('name'),
+ settings = param_samples.get('samples');
+
+ // Create leaves when last parameter setting is reached.
+ if (params_samples.length - 1 === index) {
+ return _.map(settings, function(setting) {
return {
- name: setting
+ name: param_name + '=' + setting,
+ param: param
}
});
}
// Recurse to handle other parameters.
- return _.map(param_settings[index], function(setting) {
+ return _.map(settings, function(setting) {
return {
- name: param_names[index] + ':' + setting,
- children: create_tree_data(param_names, param_settings, index + 1)
+ name: param_name + '=' + setting,
+ param: param,
+ children: create_tree_data(filtered_params_samples, index + 1)
}
});
};
var tree_data = {
name: 'Parameter Tree for ' + tool.get('name'),
- children: create_tree_data(inputs_names, sampling, 0)
+ children: create_tree_data(filtered_params_samples, 0)
};
// Set valid inputs, tree data for later use.
- this.set('valid_inputs', inputs);
+ this.set('params', _.map(params_samples, function(s) { return s.get('param') }));
this.set('tree_data', tree_data);
}
});
+/**
+ * Tile of rendered genomic data.
+ */
+var Tile = Backbone.Model.extend({
+ defaults: {
+ track: null,
+ index: null,
+ region: null,
+ resolution: null,
+ data: null,
+ stale: null,
+ html_elt: null
+ },
+
+ initialize: function(options) {
+
+ }
+
+});
+
+/**
+ * A track in a genome browser.
+ */
+var Track = Backbone.Model.extend({
+ defaults: {
+ dataset: null
+ }
+});
+
+var FeatureTrack = Track.extend({
+ defaults: {
+ track: null
+ },
+
+ /**
+ * Draw FeatureTrack tile.
+ * @param result result from server
+ * @param cxt canvas context to draw on
+ * @param mode mode to draw in
+ * @param resolution view resolution
+ * @param region region to draw
+ * @param w_scale pixels per base
+ * @param ref_seq reference sequence data
+ */
+ draw_tile: function(result, ctx, mode, resolution, region, w_scale, ref_seq) {
+ var track = this,
+ canvas = ctx.canvas,
+ tile_low = region.get('start'),
+ tile_high = region.get('end'),
+ min_height = 25,
+ left_offset = this.left_offset;
+
+ // Drawing the summary tree (feature coverage histogram)
+ if (mode === "summary_tree" || mode === "Histogram") {
+ // Get summary tree data if necessary and set max if there is one.
+ if (result.dataset_type !== "summary_tree") {
+ var st_data = this.get_summary_tree_data(result.data, tile_low, tile_high, 200);
+ if (result.max) {
+ st_data.max = result.max;
+ }
+ result = st_data;
+ }
+ // Paint summary tree into canvas
+ var painter = new painters.SummaryTreePainter(result, tile_low, tile_high, this.prefs);
+ painter.draw(ctx, canvas.width, canvas.height, w_scale);
+ return new SummaryTreeTile(track, tile_index, resolution, canvas, result.data, result.max);
+ }
+
+ // Handle row-by-row tracks
+
+ // Preprocessing: filter features and determine whether all unfiltered features have been slotted.
+ var
+ filtered = [],
+ slots = this.slotters[w_scale].slots;
+ all_slotted = true;
+ if ( result.data ) {
+ var filters = this.filters_manager.filters;
+ for (var i = 0, len = result.data.length; i < len; i++) {
+ var feature = result.data[i];
+ var hide_feature = false;
+ var filter;
+ for (var f = 0, flen = filters.length; f < flen; f++) {
+ filter = filters[f];
+ filter.update_attrs(feature);
+ if (!filter.keep(feature)) {
+ hide_feature = true;
+ break;
+ }
+ }
+ if (!hide_feature) {
+ // Feature visible.
+ filtered.push(feature);
+ // Set flag if not slotted.
+ if ( !(feature[0] in slots) ) {
+ all_slotted = false;
+ }
+ }
+ }
+ }
+
+ // Create painter.
+ var filter_alpha_scaler = (this.filters_manager.alpha_filter ? new FilterScaler(this.filters_manager.alpha_filter) : null);
+ var filter_height_scaler = (this.filters_manager.height_filter ? new FilterScaler(this.filters_manager.height_filter) : null);
+ // HACK: ref_seq will only be defined for ReadTracks, and only the ReadPainter accepts that argument
+ var painter = new (this.painter)(filtered, tile_low, tile_high, this.prefs, mode, filter_alpha_scaler, filter_height_scaler, ref_seq);
+ var feature_mapper = null;
+
+ // console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale);
+ ctx.fillStyle = this.prefs.block_color;
+ ctx.font = ctx.canvas.manager.default_font;
+ ctx.textAlign = "right";
+
+ if (result.data) {
+ // Draw features.
+ feature_mapper = painter.draw(ctx, canvas.width, canvas.height, w_scale, slots);
+ feature_mapper.translation = -left_offset;
+ }
+
+ return new FeatureTrackTile(track, tile_index, resolution, canvas, result.data, w_scale, mode, result.message, all_slotted, feature_mapper);
+ }
+});
+
+/**
+ * --- Views ---
+ */
+
var TileView = Backbone.View.extend({
});
var ToolParameterTreeView = Backbone.View.extend({
- className: 'paramamonster',
+ className: 'tool-parameter-tree',
initialize: function(options) {
this.model = options.model;
@@ -84,12 +214,14 @@
var width = 960,
height = 2000;
+ // Layout tree.
var cluster = d3.layout.cluster()
.size([height, width - 160]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
+ // Set up vis element.
var vis = d3.select(this.$el[0])
.append("svg")
.attr("width", width)
@@ -97,27 +229,32 @@
.append("g")
.attr("transform", "translate(80, 0)");
- var nodes = cluster.nodes(this.model.get('tree_data'));
+ // Set up nodes, links.
+ var nodes = cluster.nodes(this.model.get('tree_data'));
- var link = vis.selectAll("path.link")
- .data(cluster.links(nodes))
- .enter().append("path")
- .attr("class", "link")
- .attr("d", diagonal);
+ var link = vis.selectAll("path.link")
+ .data(cluster.links(nodes))
+ .enter().append("path")
+ .attr("class", "link")
+ .attr("d", diagonal);
- var node = vis.selectAll("g.node")
- .data(nodes)
- .enter().append("g")
- .attr("class", "node")
- .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
+ var node = vis.selectAll("g.node")
+ .data(nodes)
+ .enter().append("g")
+ .attr("class", "node")
+ .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
+
+ node.on("click", function(d, i) {
+ console.log(d, i);
+ });
- node.append("circle")
- .attr("r", 4.5);
+ node.append("circle")
+ .attr("r", 4.5);
- node.append("text")
- .attr("dx", function(d) { return d.children ? -8 : 8; })
- .attr("dy", 3)
- .attr("text-anchor", function(d) { return d.children ? "end" : "start"; })
- .text(function(d) { return d.name; });
+ node.append("text")
+ .attr("dx", function(d) { return d.children ? -8 : 8; })
+ .attr("dy", 3)
+ .attr("text-anchor", function(d) { return d.children ? "end" : "start"; })
+ .text(function(d) { return d.name; });
}
});
\ No newline at end of file
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd static/scripts/viz/trackster.js
--- a/static/scripts/viz/trackster.js
+++ b/static/scripts/viz/trackster.js
@@ -437,249 +437,6 @@
}
/**
- * Generic cache that handles key/value pairs.
- */
-var Cache = function( num_elements ) {
- this.num_elements = num_elements;
- this.clear();
-};
-extend(Cache.prototype, {
- get: function(key) {
- var index = this.key_ary.indexOf(key);
- if (index !== -1) {
- if (this.obj_cache[key].stale) {
- // Object is stale, so remove key and object.
- this.key_ary.splice(index, 1);
- delete this.obj_cache[key];
- }
- else {
- this.move_key_to_end(key, index);
- }
- }
- return this.obj_cache[key];
- },
- set: function(key, value) {
- if (!this.obj_cache[key]) {
- if (this.key_ary.length >= this.num_elements) {
- // Remove first element
- var deleted_key = this.key_ary.shift();
- delete this.obj_cache[deleted_key];
- }
- this.key_ary.push(key);
- }
- this.obj_cache[key] = value;
- return value;
- },
- // Move key to end of cache. Keys are removed from the front, so moving a key to the end
- // delays the key's removal.
- move_key_to_end: function(key, index) {
- this.key_ary.splice(index, 1);
- this.key_ary.push(key);
- },
- clear: function() {
- this.obj_cache = {};
- this.key_ary = [];
- },
- // Returns the number of elements in the cache.
- size: function() {
- return this.key_ary.length;
- }
-});
-
-/**
- * Data manager for a track.
- */
-var DataManager = function(num_elements, track) {
- Cache.call(this, num_elements);
- this.track = track;
-};
-extend(DataManager.prototype, Cache.prototype, {
- /**
- * Load data from server; returns AJAX object so that use of Deferred is possible.
- */
- load_data: function(low, high, mode, resolution, extra_params) {
- // Setup data request params.
- var
- chrom = this.track.view.chrom,
- params = {"chrom": chrom, "low": low, "high": high, "mode": mode,
- "resolution": resolution, "dataset_id" : this.track.dataset_id,
- "hda_ldda": this.track.hda_ldda};
- $.extend(params, extra_params);
-
- // Add track filters to params.
- if (this.track.filters_manager) {
- var filter_names = [];
- var filters = this.track.filters_manager.filters;
- for (var i = 0; i < filters.length; i++) {
- filter_names.push(filters[i].name);
- }
- params.filter_cols = JSON.stringify(filter_names);
- }
-
- // Do request.
- var manager = this;
- return $.getJSON(this.track.data_url, params, function (result) {
- manager.set_data(low, high, result);
- });
- },
- /**
- * Get track data.
- */
- get_data: function(low, high, mode, resolution, extra_params) {
- // Debugging:
- //console.log("get_data", low, high, mode);
- /*
- console.log("cache contents:")
- for (var i = 0; i < this.key_ary.length; i++) {
- console.log("\t", this.key_ary[i], this.obj_cache[this.key_ary[i]]);
- }
- */
-
- // Look for entry and return if it's a deferred or if data available is compatible with mode.
- var entry = this.get(low, high);
- if ( entry &&
- ( is_deferred(entry) || this.track.data_and_mode_compatible(entry, mode) ) ) {
- return entry;
- }
-
- //
- // Look in cache for data that can be used. Data can be reused if it
- // has the requested data and is not summary tree and has details.
- // TODO: this logic could be improved if the visualization knew whether
- // the data was "index" or "data."
- //
- var key, split_key, entry_low, entry_high, mode, entry;
- for (var i = 0; i < this.key_ary.length; i++) {
- key = this.key_ary[i];
- split_key = this.split_key(key);
- entry_low = split_key[0];
- entry_high = split_key[1];
-
- if (low >= entry_low && high <= entry_high) {
- // This entry has data in the requested range. Return if data
- // is compatible and can be subsetted.
- var entry = this.obj_cache[key];
- if ( is_deferred(entry) ||
- ( this.track.data_and_mode_compatible(entry, mode) && this.track.can_subset(entry) ) ) {
- this.move_key_to_end(key, i);
- return entry;
- }
- }
- }
-
- // Load data from server. The deferred is immediately saved until the
- // data is ready, it then replaces itself with the actual data.
- entry = this.load_data(low, high, mode, resolution, extra_params);
- this.set_data(low, high, entry);
- return entry;
- },
- /** "Deep" data request; used as a parameter for DataManager.get_more_data() */
- DEEP_DATA_REQ: "deep",
- /** "Broad" data request; used as a parameter for DataManager.get_more_data() */
- BROAD_DATA_REQ: "breadth",
- /**
- * Gets more data for a region using either a depth-first or a breadth-first approach.
- */
- get_more_data: function(low, high, mode, resolution, extra_params, req_type) {
- //
- // Get current data from cache and mark as stale.
- //
- var cur_data = this.get(low, high);
- if ( !(cur_data && this.track.data_and_mode_compatible(cur_data, mode)) ) {
- console.log("ERROR: no current data for: ", this.track, low, high, mode, resolution, extra_params);
- return;
- }
- cur_data.stale = true;
-
- //
- // Set parameters based on request type.
- //
- var query_low = low;
- if (req_type === this.DEEP_DATA_REQ) {
- // Use same interval but set start_val to skip data that's already in cur_data.
- $.extend(extra_params, {start_val: cur_data.data.length + 1});
- }
- else if (req_type === this.BROAD_DATA_REQ) {
- // To get past an area of extreme feature depth, set query low to be after either
- // (a) the maximum high or HACK/FIXME (b) the end of the last feature returned.
- query_low = (cur_data.max_high ? cur_data.max_high : cur_data.data[cur_data.data.length - 1][2]) + 1;
- }
-
- //
- // Get additional data, append to current data, and set new data. Use a custom deferred object
- // to signal when new data is available.
- //
- var
- data_manager = this,
- new_data_request = this.load_data(query_low, high, mode, resolution, extra_params)
- new_data_available = $.Deferred();
- // load_data sets cache to new_data_request, but use custom deferred object so that signal and data
- // is all data, not just new data.
- this.set_data(low, high, new_data_available);
- $.when(new_data_request).then(function(result) {
- // Update data and message.
- if (result.data) {
- result.data = cur_data.data.concat(result.data);
- if (result.max_low) {
- result.max_low = cur_data.max_low;
- }
- if (result.message) {
- // HACK: replace number in message with current data length. Works but is ugly.
- result.message = result.message.replace(/[0-9]+/, result.data.length);
- }
- }
- data_manager.set_data(low, high, result);
- new_data_available.resolve(result);
- });
- return new_data_available;
- },
- /**
- * Get data from the cache.
- */
- get: function(low, high) {
- return Cache.prototype.get.call( this, this.gen_key(low, high) );
- },
- /**
- * Sets data in the cache.
- */
- set_data: function(low, high, result) {
- return this.set(this.gen_key(low, high), result);
- },
- /**
- * Generate key for cache.
- */
- // TODO: use chrom in key so that (a) data is not thrown away when changing chroms and (b)
- // manager does not need to be cleared when changing chroms.
- // TODO: use resolution in key b/c summary tree data is dependent on resolution -- is this
- // necessary, i.e. will resolution change but not low/high/mode?
- gen_key: function(low, high) {
- var key = low + "_" + high;
- return key;
- },
- /**
- * Split key from cache into array with format [low, high]
- */
- split_key: function(key) {
- return key.split("_");
- }
-});
-
-var ReferenceTrackDataManager = function(num_elements, track, subset) {
- DataManager.call(this, num_elements, track, subset);
-};
-extend(ReferenceTrackDataManager.prototype, DataManager.prototype, Cache.prototype, {
- get: DataManager.prototype.get,
- load_data: function(low, high, mode, resolution, extra_params) {
- if (resolution > 1) {
- // Now that data is pre-fetched before draw, we don't load reference tracks
- // unless it's at the bottom level.
- return { data: null };
- }
- return DataManager.prototype.load_data.call(this, low, high, mode, resolution, extra_params);
- }
-});
-
-/**
* Drawables hierarchy:
*
* Drawable
@@ -3082,7 +2839,11 @@
.css({'height': ERROR_PADDING-1, 'width': canvas.width}).prependTo(this.html_elt);
// Handle message; only message currently is that only the first N elements are displayed.
- var
+ var tile_region = new GenomeRegion({
+ chrom: track.view.chrom,
+ start: this.low,
+ end: this.high
+ }),
num_features = data.length,
more_down_icon = $("<a href='javascript:void(0);'/>").addClass("icon more-down")
.attr("title", "For speed, only the first " + num_features + " features in this region were obtained from server. Click to get more data including depth")
@@ -3095,7 +2856,7 @@
more_down_icon.click(function() {
// Mark tile as stale, request more data, and redraw track.
tile.stale = true;
- track.data_manager.get_more_data(tile.low, tile.high, track.mode, tile.resolution, {}, track.data_manager.DEEP_DATA_REQ);
+ track.data_manager.get_more_data(tile_region, track.mode, tile.resolution, {}, track.data_manager.DEEP_DATA_REQ);
$(".tipsy").hide();
track.request_draw(true);
}).dblclick(function(e) {
@@ -3106,7 +2867,7 @@
more_across_icon.click(function() {
// Mark tile as stale, request more data, and redraw track.
tile.stale = true;
- track.data_manager.get_more_data(tile.low, tile.high, track.mode, tile.resolution, {}, track.data_manager.BROAD_DATA_REQ);
+ track.data_manager.get_more_data(tile_region, track.mode, tile.resolution, {}, track.data_manager.BROAD_DATA_REQ);
$(".tipsy").hide();
track.request_draw(true);
}).dblclick(function(e) {
@@ -3245,7 +3006,20 @@
this.data_url_extra_params = {}
this.data_query_wait = ('data_query_wait' in obj_dict ? obj_dict.data_query_wait : DEFAULT_DATA_QUERY_WAIT);
this.dataset_check_url = converted_datasets_state_url;
- this.data_manager = ('data_manager' in obj_dict ? obj_dict.data_manager : new DataManager(DATA_CACHE_SIZE, this));
+
+ // A little ugly creating data manager right now due to transition to Backbone-based objects.
+ var dataset = new Dataset({
+ id: obj_dict.dataset_id,
+ hda_ldda: obj_dict.hda_ldda
+ });
+ this.data_manager = ('data_manager' in obj_dict ?
+ obj_dict.data_manager :
+ new GenomeDataManager({
+ dataset: dataset,
+ data_url: default_data_url,
+ data_mode_compatible: this.data_and_mode_compatible,
+ can_subset: this.can_subset,
+ }));
// Height attributes: min height, max height, and visible height.
this.min_height_px = 16;
@@ -3560,6 +3334,9 @@
// Attribute init.
this.filters_manager = new FiltersManager(this, ('filters' in obj_dict ? obj_dict.filters : null));
+ // HACK: set filters manager for data manager.
+ // FIXME: prolly need function to set filters and update data_manager reference.
+ this.data_manager.set('filters_manager', this.filters_manager);
this.filters_available = false;
this.tool = ('tool' in obj_dict && obj_dict.tool ? new Tool(this, obj_dict.tool, obj_dict.tool_state) : null);
this.tile_cache = new Cache(TILE_CACHE_SIZE);
@@ -3810,15 +3587,18 @@
var track = this,
key = this._gen_tile_cache_key(width, w_scale, tile_index),
tile_bounds = this._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1];
+ region = new GenomeRegion({
+ chrom: this.view.chrom,
+ start: tile_bounds[0],
+ end: tile_bounds[1]
+ });
// Init kwargs if necessary to avoid having to check if kwargs defined.
if (!kwargs) { kwargs = {}; }
// Check tile cache, if found show existing tile in correct position
- var tile = (force ? undefined : track.tile_cache.get(key));
- if (tile) {
+ var tile = (force ? undefined : track.tile_cache.get_elt(key));
+ if (tile) {
track.show_tile(tile, parent_element, w_scale);
return tile;
}
@@ -3827,7 +3607,7 @@
var can_draw_now = true
// Get the track data, maybe a deferred
- var tile_data = track.data_manager.get_data( tile_low, tile_high, track.mode, resolution, track.data_url_extra_params );
+ var tile_data = track.data_manager.get_data( region, track.mode, resolution, track.data_url_extra_params );
if ( is_deferred( tile_data ) ) {
can_draw_now = false;
}
@@ -3835,7 +3615,7 @@
// Get seq data if needed, maybe a deferred
var seq_data;
if ( view.reference_track && w_scale > view.canvas_manager.char_width_px ) {
- seq_data = view.reference_track.data_manager.get_data(tile_low, tile_high, track.mode, resolution, view.reference_track.data_url_extra_params)
+ seq_data = view.reference_track.data_manager.get_data(region, track.mode, resolution, view.reference_track.data_url_extra_params)
if ( is_deferred( seq_data ) ) {
can_draw_now = false;
}
@@ -3857,9 +3637,8 @@
// Draw canvas.
var
canvas = track.view.canvas_manager.new_canvas(),
- tile_bounds = track._get_tile_bounds(tile_index, resolution),
- tile_low = tile_bounds[0],
- tile_high = tile_bounds[1],
+ tile_low = region.get('start'),
+ tile_high = region.get('end'),
width = Math.ceil( (tile_high - tile_low) * w_scale ) + track.left_offset,
height = track.get_canvas_height(tile_data, mode, w_scale, width);
@@ -3871,7 +3650,7 @@
// Don't cache, show if no tile.
if (tile !== undefined) {
- track.tile_cache.set(key, tile);
+ track.tile_cache.set_elt(key, tile);
track.show_tile(tile, parent_element, w_scale);
}
return tile;
@@ -4158,7 +3937,7 @@
if (!kwargs) { kwargs = {}; }
// Check tile cache, if found show existing tile in correct position
- var tile = (force ? undefined : track.tile_cache.get(key));
+ var tile = (force ? undefined : track.tile_cache.get_elt(key));
if (tile) {
track.show_tile(tile, parent_element, w_scale);
return tile;
@@ -4246,7 +4025,7 @@
}
// Don't cache, show if no tile.
- this.tile_cache.set(key, tile);
+ this.tile_cache.set_elt(key, tile);
this.show_tile(tile, parent_element, w_scale);
return tile;
}
@@ -4353,7 +4132,9 @@
this.content_div.css("border", "none");
this.data_url = reference_url;
this.data_url_extra_params = {dbkey: view.dbkey};
- this.data_manager = new ReferenceTrackDataManager(DATA_CACHE_SIZE, this, false);
+ this.data_manager = new ReferenceTrackDataManager({
+ data_url: reference_url
+ });
this.hide_contents();
};
extend(ReferenceTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd static/scripts/viz/visualization.js
--- a/static/scripts/viz/visualization.js
+++ b/static/scripts/viz/visualization.js
@@ -6,9 +6,271 @@
* and models can be used without views.
*/
+// --------- Models ---------
+
/**
- * -- Models --
+ * Generic cache that handles key/value pairs.
+ */
+var Cache = Backbone.Model.extend({
+ defaults: {
+ num_elements: 20,
+ obj_cache: {},
+ key_ary: []
+ },
+
+ get_elt: function(key) {
+ var obj_cache = this.attributes.obj_cache,
+ key_ary = this.attributes.key_ary,
+ index = key_ary.indexOf(key);
+ if (index !== -1) {
+ if (obj_cache[key].stale) {
+ // Object is stale, so remove key and object.
+ key_ary.splice(index, 1);
+ delete obj_cache[key];
+ }
+ else {
+ this.move_key_to_end(key, index);
+ }
+ }
+ return obj_cache[key];
+ },
+
+ set_elt: function(key, value) {
+ var obj_cache = this.attributes.obj_cache,
+ key_ary = this.attributes.key_ary,
+ num_elements = this.attributes.num_elements;
+ if (!obj_cache[key]) {
+ if (key_ary.length >= num_elements) {
+ // Remove first element
+ var deleted_key = key_ary.shift();
+ delete obj_cache[deleted_key];
+ }
+ key_ary.push(key);
+ }
+ obj_cache[key] = value;
+ return value;
+ },
+
+ // Move key to end of cache. Keys are removed from the front, so moving a key to the end
+ // delays the key's removal.
+ move_key_to_end: function(key, index) {
+ this.attributes.key_ary.splice(index, 1);
+ this.attributes.key_ary.push(key);
+ },
+
+ clear: function() {
+ this.attributes.obj_cache = {};
+ this.attributes.key_ary = [];
+ },
+
+ // Returns the number of elements in the cache.
+ size: function() {
+ return this.attributes.key_ary.length;
+ }
+});
+
+/**
+ * Data manager for genomic data. Data is connected to and queryable by genomic regions.
*/
+var GenomeDataManager = Cache.extend({
+ defaults: _.extend({}, Cache.prototype.defaults, {
+ dataset: null,
+ filters_manager: null,
+ data_url: null,
+ data_mode_compatible: function(entry, mode) { return true; },
+ can_subset: function(entry) { return false; }
+ }),
+
+ /**
+ * Load data from server; returns AJAX object so that use of Deferred is possible.
+ */
+ load_data: function(region, mode, resolution, extra_params) {
+ // Setup data request params.
+ var params = {
+ "chrom": region.get('chrom'),
+ "low": region.get('start'),
+ "high": region.get('end'),
+ "mode": mode,
+ "resolution": resolution
+ };
+ dataset = this.get('dataset');
+
+ // ReferenceDataManager does not have dataset.
+ if (dataset) {
+ params['dataset_id'] = dataset.id;
+ params['hda_ldda'] = dataset.get('hda_ldda');
+ }
+
+ $.extend(params, extra_params);
+
+ // Add track filters to params.
+ var filters_manager = this.get('filters_manager');
+ if (filters_manager) {
+ var filter_names = [];
+ var filters = filters_manager.filters;
+ for (var i = 0; i < filters.length; i++) {
+ filter_names.push(filters[i].name);
+ }
+ params.filter_cols = JSON.stringify(filter_names);
+ }
+
+ // Do request.
+ var manager = this;
+ return $.getJSON(this.get('data_url'), params, function (result) {
+ manager.set_data(region, result);
+ });
+ },
+
+ /**
+ * Get data from dataset.
+ */
+ get_data: function(region, mode, resolution, extra_params) {
+ // Debugging:
+ //console.log("get_data", low, high, mode);
+ /*
+ console.log("cache contents:")
+ for (var i = 0; i < this.key_ary.length; i++) {
+ console.log("\t", this.key_ary[i], this.obj_cache[this.key_ary[i]]);
+ }
+ */
+
+ // Look for entry and return if it's a deferred or if data available is compatible with mode.
+ var entry = this.get_elt(region);
+ if ( entry &&
+ ( is_deferred(entry) || this.get('data_mode_compatible')(entry, mode) ) ) {
+ return entry;
+ }
+
+ //
+ // Look in cache for data that can be used. Data can be reused if it
+ // has the requested data and is not summary tree and has details.
+ // TODO: this logic could be improved if the visualization knew whether
+ // the data was "index" or "data."
+ //
+ var
+ key_ary = this.get('key_ary'),
+ obj_cache = this.get('obj_cache'),
+ key, region, entry_region, mode, entry;
+ for (var i = 0; i < key_ary.length; i++) {
+ entry_region = new GenomeRegion(key_ary[i]);
+
+ if (entry_region.contains(region)) {
+ // This entry has data in the requested range. Return if data
+ // is compatible and can be subsetted.
+ var entry = obj_cache[key];
+ if ( is_deferred(entry) ||
+ ( this.get('data_mode_compatible')(entry, mode) && this.get('can_subset')(entry) ) ) {
+ this.move_key_to_end(key, i);
+ return entry;
+ }
+ }
+ }
+
+ // Load data from server. The deferred is immediately saved until the
+ // data is ready, it then replaces itself with the actual data.
+ entry = this.load_data(region, mode, resolution, extra_params);
+ this.set_data(region, entry);
+ return entry;
+ },
+
+ /**
+ * Alias for set_elt for readbility.
+ */
+ set_data: function(region, entry) {
+ this.set_elt(region, entry);
+ },
+
+ /**
+
+ /** "Deep" data request; used as a parameter for DataManager.get_more_data() */
+ DEEP_DATA_REQ: "deep",
+
+ /** "Broad" data request; used as a parameter for DataManager.get_more_data() */
+ BROAD_DATA_REQ: "breadth",
+
+ /**
+ * Gets more data for a region using either a depth-first or a breadth-first approach.
+ */
+ get_more_data: function(region, mode, resolution, extra_params, req_type) {
+ //
+ // Get current data from cache and mark as stale.
+ //
+ var cur_data = this.get_elt(region);
+ if ( !(cur_data && this.get('data_mode_compatible')(cur_data, mode)) ) {
+ console.log("ERROR: no current data for: ", dataset, region.toString(), mode, resolution, extra_params);
+ return;
+ }
+ cur_data.stale = true;
+
+ //
+ // Set parameters based on request type.
+ //
+ var query_low = region.get('start');
+ if (req_type === this.DEEP_DATA_REQ) {
+ // Use same interval but set start_val to skip data that's already in cur_data.
+ $.extend(extra_params, {start_val: cur_data.data.length + 1});
+ }
+ else if (req_type === this.BROAD_DATA_REQ) {
+ // To get past an area of extreme feature depth, set query low to be after either
+ // (a) the maximum high or HACK/FIXME (b) the end of the last feature returned.
+ query_low = (cur_data.max_high ? cur_data.max_high : cur_data.data[cur_data.data.length - 1][2]) + 1;
+ }
+ var query_region = region.copy().set('start', query_low);
+
+ //
+ // Get additional data, append to current data, and set new data. Use a custom deferred object
+ // to signal when new data is available.
+ //
+ var
+ data_manager = this,
+ new_data_request = this.load_data(query_region, mode, resolution, extra_params)
+ new_data_available = $.Deferred();
+ // load_data sets cache to new_data_request, but use custom deferred object so that signal and data
+ // is all data, not just new data.
+ this.set_data(region, new_data_available);
+ $.when(new_data_request).then(function(result) {
+ // Update data and message.
+ if (result.data) {
+ result.data = cur_data.data.concat(result.data);
+ if (result.max_low) {
+ result.max_low = cur_data.max_low;
+ }
+ if (result.message) {
+ // HACK: replace number in message with current data length. Works but is ugly.
+ result.message = result.message.replace(/[0-9]+/, result.data.length);
+ }
+ }
+ data_manager.set_data(region, result);
+ new_data_available.resolve(result);
+ });
+ return new_data_available;
+ },
+
+ /**
+ * Get data from the cache.
+ */
+ get_elt: function(region) {
+ return Cache.prototype.get_elt.call(this, region.toString());
+ },
+
+ /**
+ * Sets data in the cache.
+ */
+ set_elt: function(region, result) {
+ return Cache.prototype.set_elt.call(this, region.toString(), result);
+ }
+});
+
+var ReferenceTrackDataManager = GenomeDataManager.extend({
+ load_data: function(low, high, mode, resolution, extra_params) {
+ if (resolution > 1) {
+ // Now that data is pre-fetched before draw, we don't load reference tracks
+ // unless it's at the bottom level.
+ return { data: null };
+ }
+ return GenomeDataManager.prototype.load_data.call(this, low, high, mode, resolution, extra_params);
+ }
+});
/**
* A genome build.
@@ -26,13 +288,117 @@
});
/**
+ * A genomic region.
+ */
+var GenomeRegion = Backbone.Model.extend({
+ defaults: {
+ chrom: null,
+ start: 0,
+ end: 0,
+ DIF_CHROMS: 1000,
+ BEFORE: 1001,
+ CONTAINS: 1002,
+ OVERLAP_START: 1003,
+ OVERLAP_END: 1004,
+ CONTAINED_BY: 1005,
+ AFTER: 1006
+ },
+
+ /**
+ * as_str attribute using the format chrom:start-end can be
+ * used to set object's attributes.
+ */
+ initialize: function(options) {
+ if (!this.get('chrom') && !this.get('start') &&
+ !this.get('end') && 'as_str' in options) {
+ var pieces = options.as_str.split(':'),
+ chrom = pieces[0],
+ pieces = pieces.split('-'),
+ start = pieces[0],
+ end = pieces[1];
+ this.set('chrom', chrom);
+ this.set('start', start);
+ this.set('end', end);
+ }
+ },
+
+ copy: function() {
+ return new GenomeRegion({
+ chrom: this.get('chrom'),
+ start: this.get('start'),
+ end: this.get('end')
+ });
+ },
+
+ /** Returns region in canonical form chrom:start-end */
+ toString: function() {
+ return this.get('chrom') + ":" + this.get('start') + "-" + this.get('end');
+ },
+
+ /**
+ * Compute the type of overlap between this region and another region. The overlap is computed relative to the given/second region;
+ * hence, OVERLAP_START indicates that the first region overlaps the start (but not the end) of the second region.
+ */
+ compute_overlap: function(a_region) {
+ var first_chrom = this.get('chrom'), second_chrom = a_region.get('chrom'),
+ first_start = this.get('start'), second_start = a_region.get('start'),
+ first_end = this.get('end'), second_end = a_region.get('end'),
+ overlap;
+
+ // Look at chroms.
+ if (first_chrom && second_chrom && first_chrom !== second_chrom) {
+ return this.get('DIF_CHROMS');
+ }
+
+ // Look at regions.
+ if (first_start < second_start) {
+ if (first_end < second_start) {
+ overlap = this.get('BEFORE');
+ }
+ else if (first_end <= second_end) {
+ overlap = this.get('OVERLAP_START');
+ }
+ else { // first_end > second_end
+ overlap = this.get('CONTAINS');
+ }
+ }
+ else { // first_start >= second_start
+ if (first_start > second_end) {
+ overlap = this.get('AFTER');
+ }
+ else if (first_end <= second_end) {
+ overlap = this.get('CONTAINED_BY');
+ }
+ else {
+ overlap = this.get('OVERLAP_END');
+ }
+ }
+
+ return overlap;
+ },
+
+ /**
+ * Returns true if this region contains a given region.
+ */
+ contains: function(a_region) {
+ return this.compute_overlap(a_region) === this.get('CONTAINS');
+ },
+
+ /**
+ * Returns true if regions overlap.
+ */
+ overlaps: function(a_region) {
+ return _.intersection( [this.compute_overlap(a_region)],
+ [this.get('DIF_CHROMS'), this.get('BEFORE'), this.get('AFTER')] ).length === 0;
+ }
+});
+
+/**
* A genome browser bookmark.
*/
var BrowserBookmark = Backbone.Model.extend({
defaults: {
- chrom: null,
- start: 0,
- end: 0,
+ region: null,
note: ""
}
});
@@ -100,7 +466,7 @@
id: "",
type: "",
name: "",
- hda_ldda: ""
+ hda_ldda: ""
}
});
@@ -232,7 +598,7 @@
var chrom_arc = chrom_info[0],
chrom_data = chrom_info[1];
return arcs_layout.chrom_data_layout(chrom_arc, chrom_data, radius_start, radius_start + dataset_arc_height, dataset_max);
- });
+ });
// -- Render viz. --
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd templates/tracks/browser.mako
--- a/templates/tracks/browser.mako
+++ b/templates/tracks/browser.mako
@@ -42,7 +42,7 @@
<script type='text/javascript' src="${h.url_for('/static/scripts/excanvas.js')}"></script><![endif]-->
-${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.event.hover","jquery.mousewheel", "jquery.autocomplete", "viz/trackster", "viz/trackster_ui", "jquery.ui.sortable.slider", "farbtastic", "viz/visualization" )}
+${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.event.hover","jquery.mousewheel", "jquery.autocomplete", "viz/visualization", "viz/trackster", "viz/trackster_ui", "jquery.ui.sortable.slider", "farbtastic" )}
<script type="text/javascript">
//
diff -r 8872e75d8d4321c491ee43aca91d149362585ca6 -r 37352829fedd8ef5df912281b91471302cd6e5dd templates/visualization/paramamonster.mako
--- a/templates/visualization/paramamonster.mako
+++ b/templates/visualization/paramamonster.mako
@@ -46,7 +46,7 @@
tool_param_tree_view = new ToolParameterTreeView({ model: tool_param_tree });
tool_param_tree_view.render();
- $('#vis').append(tool_param_tree_view.$el);
+ $('#param-tree').append(tool_param_tree_view.$el);
});
</script></%def>
@@ -58,5 +58,8 @@
</div><div style="clear: both"></div></div>
- <div id="vis" class="unified-panel-body"></div>
+ <div class="unified-panel-body">
+ <div id="param-tree"></div>
+ <div id="tile-view"></div>
+ </div></%def>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

07 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8872e75d8d43/
changeset: 8872e75d8d43
user: jgoecks
date: 2012-06-07 20:16:26
summary: Fix bugs in fetching reference genome data.
affected #: 1 file
diff -r d238d7aabc016c01a4320908e67c561472713e73 -r 8872e75d8d4321c491ee43aca91d149362585ca6 lib/galaxy/visualization/genomes.py
--- a/lib/galaxy/visualization/genomes.py
+++ b/lib/galaxy/visualization/genomes.py
@@ -190,6 +190,7 @@
# Get/create genome object.
#
genome = None
+ twobit_file = None
# Look first in user's custom builds.
if dbkey_user and 'dbkeys' in dbkey_user.preferences:
@@ -199,11 +200,14 @@
if 'fasta' in dbkey_attributes:
build_fasta = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] )
len_file = build_fasta.get_converted_dataset( trans, 'len' ).file_name
+ converted_dataset = build_fasta.get_converted_dataset( trans, 'twobit' )
+ if converted_dataset:
+ twobit_file = converted_dataset.file_name
# Backwards compatibility: look for len file directly.
elif 'len' in dbkey_attributes:
len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name
if len_file:
- genome = Genome( dbkey, len_file=len_file )
+ genome = Genome( dbkey, len_file=len_file, twobit_file=twobit_file )
# Look in system builds.
@@ -256,9 +260,9 @@
# Get twobit file with reference data.
#
twobit_file_name = None
- if dbkey in self.available_genomes:
+ if dbkey in self.genomes:
# Built-in twobit.
- twobit_file_name = self.available_genomes[dbkey]
+ twobit_file_name = self.genomes[dbkey].twobit_file
else:
user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
dbkey_attributes = user_keys[ dbkey ]
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: Scott McManus: XML parsing for applying regular expressions and exit code checking to tools' output
by Bitbucket 07 Jun '12
by Bitbucket 07 Jun '12
07 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/d238d7aabc01/
changeset: d238d7aabc01
user: Scott McManus
date: 2012-06-07 16:45:01
summary: XML parsing for applying regular expressions and exit code checking to tools' output
affected #: 1 file
diff -r e6bbd3928bae27966792f604d410b4c8655c2a36 -r d238d7aabc016c01a4320908e67c561472713e73 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -5,7 +5,7 @@
pkg_resources.require( "simplejson" )
-import logging, os, string, sys, tempfile, glob, shutil, types, urllib, subprocess, random
+import logging, os, string, sys, tempfile, glob, shutil, types, urllib, subprocess, random, math, traceback
import simplejson
import binascii
from UserDict import DictMixin
@@ -738,6 +738,8 @@
self.app = app
#setup initial attribute values
self.inputs = odict()
+ self.stdio_exit_codes = list()
+ self.stdio_regexes = list()
self.inputs_by_page = list()
self.display_by_page = list()
self.action = '/tool_runner/index'
@@ -932,6 +934,8 @@
self.parse_help( root )
# Description of outputs produced by an invocation of the tool
self.parse_outputs( root )
+ # Parse result handling for tool exit codes and stdout/stderr messages:
+ self.parse_stdio( root )
# Any extra generated config files for the tool
self.config_files = []
conf_parent_elem = root.find("configfiles")
@@ -1089,6 +1093,192 @@
output.tool = self
output.actions = ToolOutputActionGroup( output, data_elem.find( 'actions' ) )
self.outputs[ output.name ] = output
+
+ # TODO: Include the tool's name in any parsing warnings.
+ def parse_stdio( self, root ):
+ """
+ Parse <stdio> element(s) and fill in self.return_codes,
+ self.stderr_rules, and self.stdout_rules. Return codes have a range
+ and an error type (fault or warning). Stderr and stdout rules have
+ a regular expression and an error level (fault or warning).
+ """
+ try:
+ self.stdio_exit_codes = list()
+ self.stdio_regexes = list()
+
+ # We should have a single <stdio> element, but handle the case for
+ # multiples.
+ # For every stdio element, add all of the exit_code and regex
+ # subelements that we find:
+ for stdio_elem in ( root.findall( 'stdio' ) ):
+ self.parse_stdio_exit_codes( stdio_elem )
+ self.parse_stdio_regexes( stdio_elem )
+ except Exception as e:
+ log.error( "Exception in parse_stdio! " + str(sys.exc_info()) )
+
+ def parse_stdio_exit_codes( self, stdio_elem ):
+ """
+ Parse the tool's <stdio> element's <exit_code> subelements.
+ This will add all of those elements, if any, to self.stdio_exit_codes.
+ """
+ try:
+ # Look for all <exit_code> elements. Each exit_code element must
+ # have a range/value.
+ # Exit-code ranges have precedence over a single exit code.
+ # So if there are value and range attributes, we use the range
+ # attribute. If there is neither a range nor a value, then print
+ # a warning and skip to the next.
+ for exit_code_elem in ( stdio_elem.findall( "exit_code" ) ):
+ exit_code = ToolStdioExitCode()
+ exit_code.error_level = (
+ self.parse_error_level( exit_code_elem.get( "level" )))
+ code_range = exit_code_elem.get( "range", "" )
+ if None == code_range:
+ code_range = code_elem.get( "value", "" )
+ if None == code_range:
+ log.warning( "Tool stdio exit codes must have "
+ + "a range or value" )
+ continue
+ # Parse the range. We look for:
+ # :Y
+ # X:
+ # X:Y - Split on the colon. We do not allow a colon
+ # without a beginning or end, though we could.
+ # Also note that whitespace is eliminated.
+ # TODO: Turn this into a single match - it will be
+ # more efficient
+ string.strip( code_range )
+ code_range = re.sub( "\s", "", code_range )
+ log.debug( "Code range after sub: %s" % code_range )
+ code_ranges = re.split( ":", code_range )
+ if ( len( code_ranges ) == 2 ):
+ if ( None == code_ranges[0] or '' == code_ranges[0] ):
+ exit_code.range_start = float( "-inf" )
+ else:
+ exit_code.range_start = int( code_ranges[0] )
+ if ( None == code_ranges[1] or '' == code_ranges[1] ):
+ exit_code.range_end = float( "inf" )
+ else:
+ exit_code.range_end = int( code_ranges[1] )
+ # If we got more than one colon, then ignore the exit code.
+ elif ( len( code_ranges ) > 2 ):
+ log.warning( "Invalid tool exit_code range %s - ignored"
+ % code_range )
+ continue
+ # Else we have a singular value. If it's not an integer, then
+ # we'll just write a log message and skip this exit_code.
+ else:
+ try:
+ exit_code.range_start = int( code_range )
+ except:
+ log.error( code_range )
+ log.warning( "Invalid range start for tool's exit_code %s: exit_code ignored" % code_range )
+ continue
+ exit_code.range_end = exit_code.range_start
+ # TODO: Check if we got ">", ">=", "<", or "<=":
+ # Check that the range, regardless of how we got it,
+ # isn't bogus. If we have two infinite values, then
+ # the start must be -inf and the end must be +inf.
+ # So at least warn about this situation:
+ if ( math.isinf( exit_code.range_start ) and
+ math.isinf( exit_code.range_end ) ):
+ log.warning( "Tool exit_code range %s will match on "
+ + "all exit codes" % code_range )
+ self.stdio_exit_codes.append( exit_code )
+ except Exception as e:
+ log.error( "Exception in parse_stdio_exit_codes! "
+ + str(sys.exc_info()) )
+ trace = sys.exc_info()[2]
+ if ( None != trace ):
+ trace_msg = repr( traceback.format_tb( trace ) )
+ log.error( "Traceback: %s" % trace_msg )
+
+ def parse_stdio_regexes( self, stdio_elem ):
+ """
+ Look in the tool's <stdio> elem for all <regex> subelements
+ that define how to look for warnings and fatal errors in
+ stdout and stderr. This will add all such regex elements
+ to the Tols's stdio_regexes list.
+ """
+ try:
+ # Look for every <regex> subelement. The regular expression
+ # will have "match" and "source" (or "src") attributes.
+ for regex_elem in ( stdio_elem.findall( "regex" ) ):
+ # TODO: Fill in ToolStdioRegex
+ regex = ToolStdioRegex()
+ regex.error_level = (
+ self.parse_error_level( regex_elem.get( "level" ) ) )
+ regex.match = regex_elem.get( "match", "" )
+ if None == regex.match:
+ # TODO: Convert the offending XML element to a string
+ log.warning( "Ignoring tool's stdio regex element %s - "
+ "the 'match' attribute must exist" )
+ continue
+ # Parse the output sources. We look for the "src", "source",
+ # and "sources" attributes, in that order. If there is no
+ # such source, then the source defaults to stderr & stdout.
+ # Look for a comma and then look for "err", "error", "out",
+ # and "output":
+ output_srcs = regex_elem.get( "src" )
+ if None == output_srcs:
+ output_srcs = regex_elem.get( "source" )
+ if None == output_srcs:
+ output_srcs = regex_elem.get( "sources" )
+ if None == output_srcs:
+ output_srcs = "output,error"
+ output_srcs = re.sub( "\s", "", output_srcs )
+ src_list = re.split( ",", output_srcs )
+ # Just put together anything to do with "out", including
+ # "stdout", "output", etc. Repeat for "stderr", "error",
+ # and anything to do with "err". If neither stdout nor
+ # stderr were specified, then raise a warning and scan both.
+ for src in src_list:
+ if re.match( "out", src, re.IGNORECASE ):
+ regex.stdout_match = True
+ if re.match( "err", src, re.IGNORECASE ):
+ regex.stderr_match = True
+ if (not regex.stdout_match and not regex.stderr_match):
+ log.warning( "Unable to determine if tool stream "
+ + "source scanning is output, error, "
+ + "or both. Defaulting to use both." )
+ regex.stdout_match = True
+ regex.stderr_match = True
+ self.stdio_regexes.append( regex )
+ except Exception as e:
+ log.error( "Exception in parse_stdio_exit_codes! "
+ + str(sys.exc_info()) )
+ trace = sys.exc_info()[2]
+ if ( None != trace ):
+ trace_msg = repr( traceback.format_tb( trace ) )
+ log.error( "Traceback: %s" % trace_msg )
+
+ def parse_error_level( self, err_level ):
+ """
+ Return fatal or warning depending on what's in the error level.
+ This will assume that the error level fatal is returned if it's
+ unparsable. (This doesn't have to be part of the Tool class.)
+ """
+ # What should the default be? I'm claiming it should be fatal:
+ # if you went to the trouble to write the rule, then it's
+ # probably a problem. I think there are easily three substantial
+ # camps: make it fatal, make it a warning, or, if it's missing,
+ # just throw an exception and ignore it.
+ return_level = "fatal"
+ try:
+ if ( None != err_level ):
+ if ( re.search( "warning", err_level, re.IGNORECASE ) ):
+ return_level = "warning"
+ elif ( re.search( "fatal", err_level, re.IGNORECASE ) ):
+ return_level = "fatal"
+ except Exception as e:
+ log.error( "Exception in parse_error_level "
+ + str(sys.exc_info() ) )
+ trace = sys.exc_info()[2]
+ if ( None != trace ):
+ trace_msg = repr( traceback.format_tb( trace ) )
+ log.error( "Traceback: %s" % trace_msg )
+ return return_level
+
def parse_tests( self, tests_elem ):
"""
Parse any "<test>" elements, create a `ToolTestBuilder` for each and
@@ -2577,6 +2767,31 @@
def __init__( self, value ):
self.value = value
+class ToolStdioRegex( object ):
+ """
+ This is a container for the <stdio> element's regex subelement.
+ The regex subelement has a "match" attribute, a "sources"
+ attribute that contains "output" and/or "error", and a "level"
+ attribute that contains "warning" or "fatal".
+ """
+ def __init__( self ):
+ self.match = ""
+ self.stdout_match = False
+ self.stderr_match = False
+ # TODO: Define a common class or constant for error level:
+ self.error_level = "fatal"
+
+class ToolStdioExitCode( object ):
+ """
+ This is a container for the <stdio> element's <exit_code> subelement.
+ The exit_code element has a range of exit codes and the error level.
+ """
+ def __init__( self ):
+ self.range_start = float( "-inf" )
+ self.range_end = float( "inf" )
+ # TODO: Define a common class or constant for error level:
+ self.error_level = "fatal"
+
class ToolParameterValueWrapper( object ):
"""
Base class for object that Wraps a Tool Parameter and Value.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

07 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/e6bbd3928bae/
changeset: e6bbd3928bae
user: dan
date: 2012-06-07 16:06:43
summary: Add BAM slicing tool to tool_conf.xml.main.
affected #: 1 file
diff -r 8cbdb2d339feb017e72ca73915e090c0aa7d07e1 -r e6bbd3928bae27966792f604d410b4c8655c2a36 tool_conf.xml.main
--- a/tool_conf.xml.main
+++ b/tool_conf.xml.main
@@ -279,6 +279,7 @@
<tool file="samtools/pileup_interval.xml" /><tool file="samtools/samtools_flagstat.xml" /><tool file="samtools/samtools_rmdup.xml" />
+ <tool file="samtools/samtools_slice_bam.xml" /></section><section name="NGS: GATK Tools (beta)" id="gatk"><label text="Alignment Utilities" id="gatk_bam_utilities"/>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: jgoecks: Add preset alignment options to Bowtie2 wrapper and fix default in Tophat2 wrapper.
by Bitbucket 06 Jun '12
by Bitbucket 06 Jun '12
06 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8cbdb2d339fe/
changeset: 8cbdb2d339fe
user: jgoecks
date: 2012-06-06 21:27:59
summary: Add preset alignment options to Bowtie2 wrapper and fix default in Tophat2 wrapper.
affected #: 3 files
diff -r 3db661b25774e2cb3eba6c156220f181eb28cb95 -r 8cbdb2d339feb017e72ca73915e090c0aa7d07e1 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -281,8 +281,8 @@
<when value="Yes"><param name="b2_preset_select" type="select" label="Preset option"><option value="very-fast">Very fast</option>
- <option selected="true" value="fast">Fast</option>
- <option value="sensitive">Sensitive</option>
+ <option value="fast">Fast</option>
+ <option selected="true" value="sensitive">Sensitive</option><option value="very-sensitive">Very sensitive</option></param></when>
diff -r 3db661b25774e2cb3eba6c156220f181eb28cb95 -r 8cbdb2d339feb017e72ca73915e090c0aa7d07e1 tools/sr_mapping/bowtie2_wrapper.py
--- a/tools/sr_mapping/bowtie2_wrapper.py
+++ b/tools/sr_mapping/bowtie2_wrapper.py
@@ -19,9 +19,12 @@
parser.add_option( '-2', '--input2', dest='input2', help='The reverse reads file in Sanger FASTQ format' )
parser.add_option( '', '--single-paired', dest='single_paired', help='' )
parser.add_option( '', '--settings', dest='settings', help='' )
+ parser.add_option( '', '--end-to-end', dest='end_to_end', action="store_true" )
+ parser.add_option( '', '--local', dest='local', action="store_true" )
+ parser.add_option( '', '--preset-alignment', dest='preset_alignment')
(options, args) = parser.parse_args()
-
+
# Creat bowtie index if necessary.
tmp_index_dir = tempfile.mkdtemp()
if options.own_file:
@@ -73,7 +76,10 @@
if options.settings == 'preSet':
pass
else:
- pass
+ if options.local:
+ opts += ' --local'
+ if options.preset_alignment:
+ opts += " --" + options.preset_alignment
# Final command:
cmd = cmd % ( opts, index_path, reads, options.output )
diff -r 3db661b25774e2cb3eba6c156220f181eb28cb95 -r 8cbdb2d339feb017e72ca73915e090c0aa7d07e1 tools/sr_mapping/bowtie2_wrapper.xml
--- a/tools/sr_mapping/bowtie2_wrapper.xml
+++ b/tools/sr_mapping/bowtie2_wrapper.xml
@@ -34,6 +34,12 @@
## Set params.
--settings=$params.settingsType
+
+ #if str($params.align_type) == "end_to_end":
+ --end-to-end --preset-alignment=$params.preset.align_preset_select
+ #else:
+ --local --preset-alignment=$params.preset.align_preset_select-local
+ #end if
</command><inputs><conditional name="singlePaired">
@@ -75,6 +81,26 @@
<when value="preSet" /><!-- Full/advanced params. --><when value="full">
+ <param name="align_type" type="select" label="Type of alignment">
+ <option selected="true" value="end_to_end">End to end</option>
+ <option value="local">Local</option>
+ </param>
+ <conditional name="preset">
+ <param name="b2_preset" type="select" label="Use Preset options">
+ <option selected="true" value="Yes">Yes</option>
+ <option value="No">No</option>
+ </param>
+ <when value="Yes">
+ <param name="align_preset_select" type="select" label="Preset option">
+ <option value="very-fast">Very fast</option>
+ <option value="fast">Fast</option>
+ <option selected="true" value="sensitive">Sensitive</option>
+ <option value="very-sensitive">Very sensitive</option>
+ </param>
+ </when>
+ <!-- TODO: -->
+ <when value="No" />
+ </conditional></when><!-- full --></conditional><!-- params --></inputs>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: greg: Refinements for setting metadata on the entire change log of tool shed repositories.
by Bitbucket 06 Jun '12
by Bitbucket 06 Jun '12
06 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/3db661b25774/
changeset: 3db661b25774
user: greg
date: 2012-06-06 21:15:15
summary: Refinements for setting metadata on the entire change log of tool shed repositories.
affected #: 1 file
diff -r 8a06e3e264ec912b67fb8388cf3874b32399ac07 -r 3db661b25774e2cb3eba6c156220f181eb28cb95 lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -962,6 +962,9 @@
# The list of changeset_revisions refers to repository_metadata records that have been created or updated. When the following loop
# completes, we'll delete all repository_metadata records for this repository that do not have a changeset_revision value in this list.
changeset_revisions = []
+ # When a new repository_metadata record is created, it always uses the values of metadata_changeset_revision and metadata_dict.
+ metadata_changeset_revision = None
+ metadata_dict = None
ancestor_changeset_revision = None
ancestor_metadata_dict = None
for changeset in repo.changelog:
@@ -978,13 +981,16 @@
if deleted_sample_file not in missing_sample_files:
missing_sample_files.append( deleted_sample_file )
if current_metadata_dict:
+ if not metadata_changeset_revision and not metadata_dict:
+ # We're at the first change set in the change log.
+ metadata_changeset_revision = current_changeset_revision
+ metadata_dict = current_metadata_dict
if ancestor_changeset_revision:
# Compare metadata from ancestor and current. The value of comparsion will be one of:
# 'no metadata' - no metadata for either ancestor or current, so continue from current
# 'equal' - ancestor metadata is equivalent to current metadata, so continue from current
# 'subset' - ancestor metadata is a subset of current metadata, so continue from current
- # 'not equal and not subset' - ancestor metadata is neither equal to nor a subset of current
- # metadata, so persist ancestor metadata.
+ # 'not equal and not subset' - ancestor metadata is neither equal to nor a subset of current metadata, so persist ancestor metadata.
comparison = compare_changeset_revisions( ancestor_changeset_revision,
ancestor_metadata_dict,
current_changeset_revision,
@@ -993,30 +999,38 @@
ancestor_changeset_revision = current_changeset_revision
ancestor_metadata_dict = current_metadata_dict
elif comparison == 'not equal and not subset':
- create_or_update_repository_metadata( trans, id, repository, ancestor_changeset_revision, ancestor_metadata_dict )
+ metadata_changeset_revision = ancestor_changeset_revision
+ metadata_dict = ancestor_metadata_dict
+ create_or_update_repository_metadata( trans, id, repository, metadata_changeset_revision, metadata_dict )
# Keep track of the changeset_revisions that we've persisted.
- changeset_revisions.append( ancestor_changeset_revision )
+ changeset_revisions.append( metadata_changeset_revision )
ancestor_changeset_revision = current_changeset_revision
ancestor_metadata_dict = current_metadata_dict
else:
- # We're either at the first change set in the change log or we have just created or updated
- # a repository_metadata record. At this point we set the ancestor changeset to the current
- # changeset for comparison in the next iteration.
+ # We're either at the first change set in the change log or we have just created or updated a repository_metadata record. At
+ # this point we set the ancestor changeset to the current changeset for comparison in the next iteration.
ancestor_changeset_revision = current_changeset_revision
ancestor_metadata_dict = current_metadata_dict
if not ctx.children():
+ metadata_changeset_revision = current_changeset_revision
+ metadata_dict = current_metadata_dict
# We're at the end of the change log.
- create_or_update_repository_metadata( trans, id, repository, current_changeset_revision, current_metadata_dict )
- changeset_revisions.append( current_changeset_revision )
+ create_or_update_repository_metadata( trans, id, repository, metadata_changeset_revision, metadata_dict )
+ changeset_revisions.append( metadata_changeset_revision )
ancestor_changeset_revision = None
ancestor_metadata_dict = None
elif ancestor_metadata_dict:
+ # We reach here only if current_metadata_dict is empty and ancestor_metadata_dict is not.
+ ancestor_changeset_revision = current_changeset_revision
+ metadata_changeset_revision = current_changeset_revision
+ metadata_dict = ancestor_metadata_dict
if not ctx.children():
# We're at the end of the change log.
- create_or_update_repository_metadata( trans, id, repository, current_changeset_revision, ancestor_metadata_dict )
- changeset_revisions.append( current_changeset_revision )
+ create_or_update_repository_metadata( trans, id, repository, metadata_changeset_revision, metadata_dict )
+ changeset_revisions.append( metadata_changeset_revision )
ancestor_changeset_revision = None
ancestor_metadata_dict = None
+ # Delete all repository_metadata records for this repository that do not have a changeset_revision value in changeset_revisions.
clean_repository_metadata( trans, id, changeset_revisions )
add_repository_metadata_tool_versions( trans, id, changeset_revisions )
if missing_sample_files:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: jgoecks: Update default and help for Tophat2 coverage search parameter.
by Bitbucket 06 Jun '12
by Bitbucket 06 Jun '12
06 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/8a06e3e264ec/
changeset: 8a06e3e264ec
user: jgoecks
date: 2012-06-06 20:42:21
summary: Update default and help for Tophat2 coverage search parameter.
affected #: 1 file
diff -r 41f72445078999fd2a3f6471ee319a0be977097c -r 8a06e3e264ec912b67fb8388cf3874b32399ac07 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -231,9 +231,9 @@
<!-- Coverage search. --><conditional name="coverage_search">
- <param name="use_search" type="select" label="Use Coverage Search">
- <option selected="true" value="Yes">Yes</option>
- <option value="No">No</option>
+ <param name="use_search" type="select" label="Use Coverage Search" help="Enables the coverage based search for junctions. Use when coverage search is disabled by default (such as for reads 75bp or longer), for maximum sensitivity.">
+ <option selected="true" value="No">No</option>
+ <option value="Yes">Yes</option></param><when value="Yes"><param name="min_coverage_intron" type="integer" value="50" label="Minimum intron length that may be found during coverage search" />
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: greg: Fixes for getting updates to installed tool shed repositories.
by Bitbucket 06 Jun '12
by Bitbucket 06 Jun '12
06 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/41f724450789/
changeset: 41f724450789
user: greg
date: 2012-06-06 19:26:15
summary: Fixes for getting updates to installed tool shed repositories.
affected #: 2 files
diff -r 5343150e167ea8682f25849e7b01e1f40c4b440d -r 41f72445078999fd2a3f6471ee319a0be977097c lib/galaxy/web/controllers/admin_toolshed.py
--- a/lib/galaxy/web/controllers/admin_toolshed.py
+++ b/lib/galaxy/web/controllers/admin_toolshed.py
@@ -174,6 +174,40 @@
return trans.response.send_redirect( url )
@web.expose
@web.require_admin
+ def check_installed_tool_dependencies( self, trans, repository_id, relative_install_dir ):
+ """See if any tool dependencies need to be installed."""
+ tool_dependencies_missing = False
+ repository = get_repository( trans, repository_id )
+ if repository.includes_tool_dependencies:
+ # Get the tool_dependencies.xml file from the repository.
+ work_dir = make_tmp_directory()
+ tool_dependencies_config = get_config_from_repository( trans.app,
+ 'tool_dependencies.xml',
+ repository,
+ repository.changeset_revision,
+ work_dir,
+ install_dir=relative_install_dir )
+ # Parse the tool_dependencies.xml config.
+ tree = ElementTree.parse( tool_dependencies_config )
+ root = tree.getroot()
+ ElementInclude.include( root )
+ fabric_version_checked = False
+ for elem in root:
+ if elem.tag == 'package':
+ package_name = elem.get( 'name', None )
+ package_version = elem.get( 'version', None )
+ if package_name and package_version:
+ install_dir = get_install_dir( trans.app, repository, repository.installed_changeset_revision, package_name, package_version )
+ if not_installed( install_dir ):
+ tool_dependencies_missing = True
+ break
+ try:
+ shutil.rmtree( work_dir )
+ except:
+ pass
+ return tool_dependencies_missing
+ @web.expose
+ @web.require_admin
def deactivate_or_uninstall_repository( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
@@ -540,7 +574,8 @@
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- repository = get_repository( trans, kwd[ 'id' ] )
+ repository_id = kwd[ 'id' ]
+ repository = get_repository( trans, repository_id )
description = util.restore_text( params.get( 'description', repository.description ) )
shed_tool_conf, tool_path, relative_install_dir = get_tool_panel_config_tool_path_install_dir( trans.app, repository )
repo_files_dir = os.path.abspath( os.path.join( relative_install_dir, repository.name ) )
@@ -558,34 +593,7 @@
trans.sa_session.add( repository )
trans.sa_session.flush()
message = "Repository metadata has been reset."
- tool_dependencies_missing = False
- if repository.includes_tool_dependencies:
- # See if any tool dependencies need to be installed, get the tool_dependencies.xml file from the repository.
- work_dir = make_tmp_directory()
- tool_dependencies_config = get_config_from_repository( trans.app,
- 'tool_dependencies.xml',
- repository,
- repository.changeset_revision,
- work_dir,
- install_dir=relative_install_dir )
- # Parse the tool_dependencies.xml config.
- tree = ElementTree.parse( tool_dependencies_config )
- root = tree.getroot()
- ElementInclude.include( root )
- fabric_version_checked = False
- for elem in root:
- if elem.tag == 'package':
- package_name = elem.get( 'name', None )
- package_version = elem.get( 'version', None )
- if package_name and package_version:
- install_dir = get_install_dir( trans.app, repository, repository.installed_changeset_revision, package_name, package_version )
- if not_installed( install_dir ):
- tool_dependencies_missing = True
- break
- try:
- shutil.rmtree( work_dir )
- except:
- pass
+ tool_dependencies_missing = self.check_installed_tool_dependencies( trans, repository_id, relative_install_dir )
return trans.fill_template( '/admin/tool_shed_repository/manage_repository.mako',
repository=repository,
description=description,
@@ -782,7 +790,7 @@
repository = get_repository_by_shed_name_owner_changeset_revision( trans.app, tool_shed_url, name, owner, changeset_revision )
if changeset_revision and latest_changeset_revision and latest_ctx_rev:
if changeset_revision == latest_changeset_revision:
- message = "The cloned tool shed repository named '%s' is current (there are no updates available)." % name
+ message = "The installed repository named '%s' is current, there are no updates available. " % name
else:
shed_tool_conf, tool_path, relative_install_dir = get_tool_panel_config_tool_path_install_dir( trans.app, repository )
if relative_install_dir:
@@ -801,13 +809,17 @@
repository.update_available = False
trans.sa_session.add( repository )
trans.sa_session.flush()
- message = "The cloned repository named '%s' has been updated to change set revision '%s'." % \
- ( name, latest_changeset_revision )
+ message = "The installed repository named '%s' has been updated to change set revision '%s'. " % ( name, latest_changeset_revision )
+ # See if any tool dependencies can be installed.
+ shed_tool_conf, tool_path, relative_install_dir = get_tool_panel_config_tool_path_install_dir( trans.app, repository )
+ tool_dependencies_missing = self.check_installed_tool_dependencies( trans, trans.security.encode_id( repository.id ), relative_install_dir )
+ if tool_dependencies_missing:
+ message += "Select <b>Install tool dependencies</b> from the repository's pop-up menu to install tool dependencies."
else:
- message = "The directory containing the cloned repository named '%s' cannot be found." % name
+ message = "The directory containing the installed repository named '%s' cannot be found. " % name
status = 'error'
else:
- message = "The latest changeset revision could not be retrieved for the repository named '%s'." % name
+ message = "The latest changeset revision could not be retrieved for the installed repository named '%s'. " % name
status = 'error'
return trans.response.send_redirect( web.url_for( controller='admin_toolshed',
action='manage_repository',
diff -r 5343150e167ea8682f25849e7b01e1f40c4b440d -r 41f72445078999fd2a3f6471ee319a0be977097c lib/galaxy/webapps/community/controllers/repository.py
--- a/lib/galaxy/webapps/community/controllers/repository.py
+++ b/lib/galaxy/webapps/community/controllers/repository.py
@@ -599,6 +599,9 @@
repository = get_repository_by_name_and_owner( trans, name, owner )
repo_dir = repository.repo_path
repo = hg.repository( get_configured_ui(), repo_dir )
+ # Default to the current changeset revision.
+ update_to_ctx = get_changectx_for_changeset( repo, changeset_revision )
+ latest_changeset_revision = changeset_revision
from_update_manager = webapp == 'update_manager'
if from_update_manager:
update = 'true'
@@ -608,80 +611,45 @@
url = '%sadmin_toolshed/update_to_changeset_revision?tool_shed_url=%s' % ( galaxy_url, url_for( '/', qualified=True ) )
url += '&name=%s&owner=%s&changeset_revision=%s&latest_changeset_revision=' % ( repository.name, repository.user.username, changeset_revision )
if changeset_revision == repository.tip:
- # If changeset_revision is the repository tip, we know there are no additional updates for the tools.
+ # If changeset_revision is the repository tip, there are no additional updates.
if from_update_manager:
return no_update
# Return the same value for changeset_revision and latest_changeset_revision.
- url += repository.tip
+ url += latest_changeset_revision
else:
repository_metadata = get_repository_metadata_by_changeset_revision( trans,
trans.security.encode_id( repository.id ),
changeset_revision )
if repository_metadata:
- # If changeset_revision is in the repository_metadata table for this repository, then we know there are no additional updates
- # for the tools.
+ # If changeset_revision is in the repository_metadata table for this repository, there are no additional updates.
if from_update_manager:
return no_update
else:
# Return the same value for changeset_revision and latest_changeset_revision.
- url += changeset_revision
+ url += latest_changeset_revision
else:
- # TODO: Re-engineer this to define the change set for update to be the one just before the next change set in the repository_metadata
- # table for this repository.
# The changeset_revision column in the repository_metadata table has been updated with a new changeset_revision value since the
- # repository was cloned. Load each tool in the repository's changeset_revision to generate a list of tool guids, since guids
- # differentiate tools by id and version.
- ctx = get_changectx_for_changeset( repo, changeset_revision )
- if ctx is not None:
- work_dir = make_tmp_directory()
- tool_guids = []
- for filename in ctx:
- # Find all tool configs in this repository changeset_revision.
- if filename not in NOT_TOOL_CONFIGS and filename.endswith( '.xml' ):
- is_tool_config, valid, tool, error_message = load_tool_from_tmp_directory( trans,
- repo,
- repo_dir,
- ctx,
- filename,
- work_dir )
- if valid and tool is not None:
- tool_guids.append( generate_tool_guid( trans, repository, tool ) )
- tool_guids.sort()
- if tool_guids:
- # Compare our list of tool guids against those in each repository_metadata record for the repository to find the
- # repository_metadata record with the changeset_revision value we want to pass back to the caller.
- found = False
- for repository_metadata in get_repository_metadata_by_repository_id( trans, trans.security.encode_id( repository.id ) ):
- metadata = repository_metadata.metadata
- metadata_tool_guids = []
- for tool_dict in metadata[ 'tools' ]:
- metadata_tool_guids.append( tool_dict[ 'guid' ] )
- metadata_tool_guids.sort()
- if tool_guids == metadata_tool_guids:
- # We've found the repository_metadata record whose changeset_revision value has been updated.
- if from_update_manager:
- return update
- url += repository_metadata.changeset_revision
- # Get the ctx_rev for the discovered changeset_revision.
- latest_ctx = get_changectx_for_changeset( repo, repository_metadata.changeset_revision )
- found = True
- break
- if not found:
- # There must be a problem in the data, so we'll just send back the received changeset_revision.
- log.debug( "Possible data corruption - updated repository_metadata cannot be found for repository id %d." % repository.id )
- if from_update_manager:
- return no_update
- url += changeset_revision
- else:
- # There are no tools in the changeset_revision, so no tool updates are possible.
- if from_update_manager:
- return no_update
- url += changeset_revision
- try:
- shutil.rmtree( work_dir )
- except:
- pass
- url += '&latest_ctx_rev=%s' % str( latest_ctx.rev() )
+ # repository was installed. We need to find the changeset_revision to which we need to update.
+ update_to_changeset_hash = None
+ for changeset in repo.changelog:
+ changeset_hash = str( repo.changectx( changeset ) )
+ ctx = get_changectx_for_changeset( repo, changeset_hash )
+ if update_to_changeset_hash:
+ if get_repository_metadata_by_changeset_revision( trans, trans.security.encode_id( repository.id ), changeset_hash ):
+ # We found a RepositoryMetadata record.
+ if changeset_hash == repository.tip:
+ # The current ctx is the repository tip, so use it.
+ update_to_ctx = get_changectx_for_changeset( repo, changeset_hash )
+ latest_changeset_revision = changeset_hash
+ else:
+ update_to_ctx = get_changectx_for_changeset( repo, update_to_changeset_hash )
+ latest_changeset_revision = update_to_changeset_hash
+ break
+ elif not update_to_changeset_hash and changeset_hash == changeset_revision:
+ # We've found the changeset in the changelog for which we need to get the next update.
+ update_to_changeset_hash = changeset_hash
+ url += str( latest_changeset_revision )
+ url += '&latest_ctx_rev=%s' % str( update_to_ctx.rev() )
return trans.response.send_redirect( url )
@web.expose
def contact_owner( self, trans, id, **kwd ):
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: greg: Fix for rendering the url for cloning a tool shed repository.
by Bitbucket 06 Jun '12
by Bitbucket 06 Jun '12
06 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/5343150e167e/
changeset: 5343150e167e
user: greg
date: 2012-06-06 15:48:15
summary: Fix for rendering the url for cloning a tool shed repository.
affected #: 1 file
diff -r da5b91a86b2f739eb9deb8dc51fe16446e5cda35 -r 5343150e167ea8682f25849e7b01e1f40c4b440d lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -407,12 +407,13 @@
def generate_clone_url( trans, repository_id ):
"""Generate the URL for cloning a repository."""
repository = get_repository( trans, repository_id )
- protocol, base = trans.request.base.split( '://' )
+ base_url = url_for( '/', qualified=True ).rstrip( '/' )
if trans.user:
+ protocol, base = base_url.split( '://' )
username = '%s@' % trans.user.username
+ return '%s://%s%s/repos/%s/%s' % ( protocol, username, base, repository.user.username, repository.name )
else:
- username = ''
- return '%s://%s%s/repos/%s/%s' % ( protocol, username, base, repository.user.username, repository.name )
+ return '%s/repos/%s/%s' % ( base_url, repository.user.username, repository.name )
def generate_metadata_for_changeset_revision( trans, repo, id, ctx, changeset_revision, repo_dir, updating_tip=False ):
if updating_tip:
# If a push from the command line is occurring, update the repository files on disk before setting metadata.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: fubar: Added __admin_users__ as a parameter so tools can check __user_email__ and refuse to run unless
by Bitbucket 06 Jun '12
by Bitbucket 06 Jun '12
06 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/da5b91a86b2f/
changeset: da5b91a86b2f
user: fubar
date: 2012-06-06 06:02:41
summary: Added __admin_users__ as a parameter so tools can check __user_email__ and refuse to run unless
being invoked by a local admin - useful for any potentially dangerous tools.
affected #: 1 file
diff -r a4196ffaf11e22b9b0a76fe6aa1cbbcccd2fb219 -r da5b91a86b2f739eb9deb8dc51fe16446e5cda35 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -2056,6 +2056,7 @@
# datatypes conf path, so we can load the datatypes registry
param_dict['__root_dir__'] = param_dict['GALAXY_ROOT_DIR'] = os.path.abspath( self.app.config.root )
param_dict['__datatypes_config__'] = param_dict['GALAXY_DATATYPES_CONF_FILE'] = self.app.datatypes_registry.integrated_datatypes_configs
+ param_dict['__admin_users__'] = self.app.config.admin_users
# Return the dictionary of parameters
return param_dict
def build_param_file( self, param_dict, directory=None ):
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: fubar: Adding a new datatype to datatypes_conf.xml.sample - toolshed compatible gzips created by a new automated
by Bitbucket 05 Jun '12
by Bitbucket 05 Jun '12
05 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/a4196ffaf11e/
changeset: a4196ffaf11e
user: fubar
date: 2012-06-06 04:08:55
summary: Adding a new datatype to datatypes_conf.xml.sample - toolshed compatible gzips created by a new automated
script wrapper.
affected #: 1 file
diff -r 0b2d939a7870074977a30d7e9b57cd0c434fe637 -r a4196ffaf11e22b9b0a76fe6aa1cbbcccd2fb219 datatypes_conf.xml.sample
--- a/datatypes_conf.xml.sample
+++ b/datatypes_conf.xml.sample
@@ -89,6 +89,7 @@
<converter file="gff_to_interval_index_converter.xml" target_datatype="interval_index"/><converter file="gff_to_summary_tree_converter.xml" target_datatype="summary_tree"/></datatype>
+ <datatype extension="toolshed.gz" type="galaxy.datatypes.binary:Binary" mimetype="multipart/x-gzip" subclass="True" /><datatype extension="h5" type="galaxy.datatypes.binary:Binary" mimetype="application/octet-stream" subclass="True" /><datatype extension="html" type="galaxy.datatypes.images:Html" mimetype="text/html"/><datatype extension="interval" type="galaxy.datatypes.interval:Interval" display_in_upload="true">
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: jgoecks: Foundation for parameter sweeping visualization.
by Bitbucket 05 Jun '12
by Bitbucket 05 Jun '12
05 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/0b2d939a7870/
changeset: 0b2d939a7870
user: jgoecks
date: 2012-06-05 23:10:23
summary: Foundation for parameter sweeping visualization.
affected #: 6 files
diff -r 91217d6ead5a58609e0cc0ac839d2c2a6bc94691 -r 0b2d939a7870074977a30d7e9b57cd0c434fe637 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -2407,10 +2407,19 @@
if isinstance( input, DataToolParameter ):
param_dict.update( { 'type' : 'data', 'html' : urllib.quote( input.get_html( trans ) ) } )
elif isinstance( input, SelectToolParameter ):
- param_dict.update( { 'type' : 'select', 'html' : urllib.quote( input.get_html( trans ) ) } )
+ param_dict.update( { 'type' : 'select',
+ 'html' : urllib.quote( input.get_html( trans ) ),
+ 'options': input.static_options
+ } )
elif isinstance( input, Conditional ):
# TODO.
pass
+ elif isinstance( input, ( IntegerToolParameter, FloatToolParameter ) ):
+ param_dict.update( { 'type' : 'number', 'init_value' : input.value,
+ 'html' : urllib.quote( input.get_html( trans ) ),
+ 'min': input.min,
+ 'max': input.max
+ } )
else:
param_dict.update( { 'type' : '??', 'init_value' : input.value, \
'html' : urllib.quote( input.get_html( trans ) ) } )
diff -r 91217d6ead5a58609e0cc0ac839d2c2a6bc94691 -r 0b2d939a7870074977a30d7e9b57cd0c434fe637 lib/galaxy/web/controllers/tracks.py
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -762,6 +762,15 @@
output_dataset = joda.dataset
return self.add_track_async( trans, output_dataset.id )
+
+ @web.expose
+ @web.require_login( "use Galaxy visualizations", use_panels=True )
+ def paramamonster( self, trans, hda_ldda, dataset_id ):
+ # Get dataset.
+ dataset = self._get_dataset( trans, hda_ldda, dataset_id )
+
+ return trans.fill_template_mako( "visualization/paramamonster.mako", dataset=dataset,
+ tool=self.app.toolbox.tools_by_id[ 'cufflinks' ].to_dict( trans, for_display=True ) )
@web.expose
@web.require_login( "use Galaxy visualizations", use_panels=True )
diff -r 91217d6ead5a58609e0cc0ac839d2c2a6bc94691 -r 0b2d939a7870074977a30d7e9b57cd0c434fe637 static/scripts/packed/viz/paramamonster.js
--- /dev/null
+++ b/static/scripts/packed/viz/paramamonster.js
@@ -0,0 +1,1 @@
+var ToolParameterTree=Backbone.Model.extend({defaults:{tool:null,samples:4},initialize:function(d){var c=this.get("tool"),b=this.get("samples"),a=c.get("inputs").filter(function(h){return(["number","select"].indexOf(h.get("type"))!==-1)}),f=_.map(a,function(h){return h.get("name")});sampling=_.map(a,function(h){var i=h.get("type");if(i==="number"){return d3.scale.linear().domain([h.get("min"),h.get("max")]).ticks(b)}else{if(i==="select"){return _.map(h.get("options"),function(j){return j[0]})}}});var g=function(j,h,i){if(h.length-1===i){return _.map(h[i],function(k){return{name:k}})}return _.map(h[i],function(k){return{name:j[i]+":"+k,children:g(j,h,i+1)}})};var e={name:"Parameter Tree for "+c.get("name"),children:g(f,sampling,0)};this.set("valid_inputs",a);this.set("tree_data",e)}});var TileView=Backbone.View.extend({});var ToolParameterTreeView=Backbone.View.extend({className:"paramamonster",initialize:function(a){this.model=a.model},render:function(){var e=960,b=2000;var a=d3.layout.cluster().size([b,e-160]);var d=d3.svg.diagonal().projection(function(i){return[i.y,i.x]});var h=d3.select(this.$el[0]).append("svg").attr("width",e).attr("height",b).append("g").attr("transform","translate(80, 0)");var c=a.nodes(this.model.get("tree_data"));var g=h.selectAll("path.link").data(a.links(c)).enter().append("path").attr("class","link").attr("d",d);var f=h.selectAll("g.node").data(c).enter().append("g").attr("class","node").attr("transform",function(i){return"translate("+i.y+","+i.x+")"});f.append("circle").attr("r",4.5);f.append("text").attr("dx",function(i){return i.children?-8:8}).attr("dy",3).attr("text-anchor",function(i){return i.children?"end":"start"}).text(function(i){return i.name})}});
\ No newline at end of file
diff -r 91217d6ead5a58609e0cc0ac839d2c2a6bc94691 -r 0b2d939a7870074977a30d7e9b57cd0c434fe637 static/scripts/viz/paramamonster.js
--- /dev/null
+++ b/static/scripts/viz/paramamonster.js
@@ -0,0 +1,123 @@
+/**
+ * Visualization and components for ParamaMonster, a visualization for exploring a tool's parameter space via
+ * genomic visualization.
+ */
+
+var ToolParameterTree = Backbone.Model.extend({
+ defaults: {
+ tool: null,
+ samples: 4
+ },
+
+
+ initialize: function(options) {
+ //
+ // -- Create tree data from tool. --
+ //
+
+ // Valid inputs for tree are number, select parameters.
+ var tool = this.get('tool'),
+ samples = this.get('samples'),
+ inputs = tool.get('inputs').filter(function(input) {
+ return ( ['number', 'select'].indexOf(input.get('type')) !== -1 );
+ }),
+ inputs_names = _.map(inputs, function(i) { return i.get('name')});
+ // Sample from all valid inputs.
+ sampling = _.map(inputs, function(input) {
+ var type = input.get('type');
+ if (type === 'number') {
+ return d3.scale.linear().domain([input.get('min'), input.get('max')]).ticks(samples);
+ }
+ else if (type === 'select') {
+ return _.map(input.get('options'), function(option) {
+ return option[0];
+ });
+ }
+ });
+
+ /**
+ * Returns tree data.
+ */
+ var create_tree_data = function(param_names, param_settings, index) {
+ // Terminate when last parameter setting is reached.
+ if (param_settings.length - 1 === index) {
+ return _.map(param_settings[index], function(setting) {
+ return {
+ name: setting
+ }
+ });
+ }
+
+ // Recurse to handle other parameters.
+ return _.map(param_settings[index], function(setting) {
+ return {
+ name: param_names[index] + ':' + setting,
+ children: create_tree_data(param_names, param_settings, index + 1)
+ }
+ });
+ };
+
+ var tree_data = {
+ name: 'Parameter Tree for ' + tool.get('name'),
+ children: create_tree_data(inputs_names, sampling, 0)
+ };
+
+ // Set valid inputs, tree data for later use.
+ this.set('valid_inputs', inputs);
+ this.set('tree_data', tree_data);
+ }
+
+});
+
+var TileView = Backbone.View.extend({
+
+});
+
+var ToolParameterTreeView = Backbone.View.extend({
+ className: 'paramamonster',
+
+ initialize: function(options) {
+ this.model = options.model;
+ },
+
+ render: function() {
+ var width = 960,
+ height = 2000;
+
+ var cluster = d3.layout.cluster()
+ .size([height, width - 160]);
+
+ var diagonal = d3.svg.diagonal()
+ .projection(function(d) { return [d.y, d.x]; });
+
+ var vis = d3.select(this.$el[0])
+ .append("svg")
+ .attr("width", width)
+ .attr("height", height)
+ .append("g")
+ .attr("transform", "translate(80, 0)");
+
+ var nodes = cluster.nodes(this.model.get('tree_data'));
+
+ var link = vis.selectAll("path.link")
+ .data(cluster.links(nodes))
+ .enter().append("path")
+ .attr("class", "link")
+ .attr("d", diagonal);
+
+ var node = vis.selectAll("g.node")
+ .data(nodes)
+ .enter().append("g")
+ .attr("class", "node")
+ .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
+
+ node.append("circle")
+ .attr("r", 4.5);
+
+ node.append("text")
+ .attr("dx", function(d) { return d.children ? -8 : 8; })
+ .attr("dy", 3)
+ .attr("text-anchor", function(d) { return d.children ? "end" : "start"; })
+ .text(function(d) { return d.name; });
+ }
+});
\ No newline at end of file
diff -r 91217d6ead5a58609e0cc0ac839d2c2a6bc94691 -r 0b2d939a7870074977a30d7e9b57cd0c434fe637 templates/visualization/circster.mako
--- a/templates/visualization/circster.mako
+++ b/templates/visualization/circster.mako
@@ -16,7 +16,7 @@
<%def name="javascripts()">
${parent.javascripts()}
- ${h.js( "libs/d3", "mvc/visualization" )}
+ ${h.js( "libs/d3", "viz/visualization" )}
<script type="text/javascript">
$(function() {
@@ -81,7 +81,7 @@
// -- Render viz. --
circster.render();
- $('#vis').append(circster.$el);
+ $('#vis').append(circster.$el);
});
</script></%def>
diff -r 91217d6ead5a58609e0cc0ac839d2c2a6bc94691 -r 0b2d939a7870074977a30d7e9b57cd0c434fe637 templates/visualization/paramamonster.mako
--- /dev/null
+++ b/templates/visualization/paramamonster.mako
@@ -0,0 +1,62 @@
+<%inherit file="/webapps/galaxy/base_panels.mako"/>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.active_view="visualization"
+ self.message_box_visible=False
+%>
+</%def>
+
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ <style>
+ .unified-panel-body {
+ overflow: auto;
+ }
+ .link {
+ fill: none;
+ stroke: #ccc;
+ stroke-width: 1.5px;
+ }
+ .node {
+ font: 10px sans-serif;
+ }
+ .node circle {
+ fill: #fff;
+ stroke: steelblue;
+ stroke-width: 1.5px;
+ }
+ </style>
+</%def>
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+
+ ${h.templates( "tool_link", "panel_section", "tool_search" )}
+ ${h.js( "libs/d3", "viz/visualization", "viz/paramamonster", "mvc/tools" )}
+
+ <script type="text/javascript">
+ $(function() {
+ // -- Viz set up. --
+
+ var tool = new Tool(JSON.parse('${ h.to_json_string( tool ) }')),
+ tool_param_tree = new ToolParameterTree({ tool: tool }),
+ tool_param_tree_view = new ToolParameterTreeView({ model: tool_param_tree });
+
+ tool_param_tree_view.render();
+ $('#vis').append(tool_param_tree_view.$el);
+ });
+ </script>
+</%def>
+
+<%def name="center_panel()">
+ <div class="unified-panel-header" unselectable="on">
+ <div class="unified-panel-header-inner">
+ <div style="float:left;" id="title"></div>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div id="vis" class="unified-panel-body"></div>
+</%def>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: greg: Fixes for setting metadata on tool shed repoistories.
by Bitbucket 05 Jun '12
by Bitbucket 05 Jun '12
05 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/91217d6ead5a/
changeset: 91217d6ead5a
user: greg
date: 2012-06-05 20:59:42
summary: Fixes for setting metadata on tool shed repoistories.
affected #: 2 files
diff -r d7580315fa206bcc64bc7607ee52bccfbf95e42f -r 91217d6ead5a58609e0cc0ac839d2c2a6bc94691 lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -1010,12 +1010,12 @@
ancestor_changeset_revision = None
ancestor_metadata_dict = None
elif ancestor_metadata_dict:
- # Our current change set has no metadata, but our ancestor change set has metadata, so save it.
- create_or_update_repository_metadata( trans, id, repository, ancestor_changeset_revision, ancestor_metadata_dict )
- # Keep track of the changeset_revisions that we've persisted.
- changeset_revisions.append( ancestor_changeset_revision )
- ancestor_changeset_revision = None
- ancestor_metadata_dict = None
+ if not ctx.children():
+ # We're at the end of the change log.
+ create_or_update_repository_metadata( trans, id, repository, current_changeset_revision, ancestor_metadata_dict )
+ changeset_revisions.append( current_changeset_revision )
+ ancestor_changeset_revision = None
+ ancestor_metadata_dict = None
clean_repository_metadata( trans, id, changeset_revisions )
add_repository_metadata_tool_versions( trans, id, changeset_revisions )
if missing_sample_files:
@@ -1083,7 +1083,7 @@
repository_metadata.metadata = metadata_dict
trans.sa_session.add( repository_metadata )
trans.sa_session.flush()
- elif not invalid_files:
+ elif updating_tip and len( repo ) == 1 and not invalid_files:
message = "Revision '%s' includes no tools, datatypes or exported workflows for which metadata can " % str( changeset_revision )
message += "be defined so this revision cannot be automatically installed into a local Galaxy instance."
status = "error"
@@ -1120,7 +1120,6 @@
status = 'error'
return message, status
def set_repository_metadata_due_to_new_tip( trans, id, repository, content_alert_str=None, **kwd ):
- message = util.restore_text( kwd.get( 'message', '' ) )
# Set metadata on the repository tip.
error_message, status = set_repository_metadata( trans, id, repository.tip, content_alert_str=content_alert_str, **kwd )
if not error_message:
diff -r d7580315fa206bcc64bc7607ee52bccfbf95e42f -r 91217d6ead5a58609e0cc0ac839d2c2a6bc94691 lib/galaxy/webapps/community/controllers/upload.py
--- a/lib/galaxy/webapps/community/controllers/upload.py
+++ b/lib/galaxy/webapps/community/controllers/upload.py
@@ -158,6 +158,8 @@
message += " %d files were removed from the repository root. " % len( files_to_remove )
kwd[ 'message' ] = message
set_repository_metadata_due_to_new_tip( trans, repository_id, repository, content_alert_str=content_alert_str, **kwd )
+ # Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
+ reset_tool_data_tables( trans.app )
trans.response.send_redirect( web.url_for( controller='repository',
action='browse_repository',
id=repository_id,
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: greg: Fixes for handling tool shed repository files that have been renamed or deleted.
by Bitbucket 05 Jun '12
by Bitbucket 05 Jun '12
05 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/d7580315fa20/
changeset: d7580315fa20
user: greg
date: 2012-06-05 18:51:54
summary: Fixes for handling tool shed repository files that have been renamed or deleted.
affected #: 3 files
diff -r f551610b687003fc09cd6ff7bbc506d237c67a75 -r d7580315fa206bcc64bc7607ee52bccfbf95e42f lib/galaxy/util/shed_util.py
--- a/lib/galaxy/util/shed_util.py
+++ b/lib/galaxy/util/shed_util.py
@@ -856,14 +856,26 @@
response.close()
return ctx_rev
def get_named_tmpfile_from_ctx( ctx, filename, dir ):
- fctx = ctx[ filename ]
- fh = tempfile.NamedTemporaryFile( 'wb', dir=dir )
- tmp_filename = fh.name
- fh.close()
- fh = open( tmp_filename, 'wb' )
- fh.write( fctx.data() )
- fh.close()
- return tmp_filename
+ filename = strip_path( filename )
+ for ctx_file in ctx.files():
+ ctx_file_name = strip_path( ctx_file )
+ if filename == ctx_file_name:
+ try:
+ # If the file was moved, its destination file contents will be returned here.
+ fctx = ctx[ ctx_file ]
+ except LookupError, e:
+ # Continue looking in case the file was moved.
+ fctx = None
+ continue
+ if fctx:
+ fh = tempfile.NamedTemporaryFile( 'wb', dir=dir )
+ tmp_filename = fh.name
+ fh.close()
+ fh = open( tmp_filename, 'wb' )
+ fh.write( fctx.data() )
+ fh.close()
+ return tmp_filename
+ return None
def get_repository_by_shed_name_owner_changeset_revision( app, tool_shed, name, owner, changeset_revision ):
sa_session = app.model.context.current
if tool_shed.find( '//' ) > 0:
diff -r f551610b687003fc09cd6ff7bbc506d237c67a75 -r d7580315fa206bcc64bc7607ee52bccfbf95e42f lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -529,17 +529,22 @@
def get_file_context_from_ctx( ctx, filename ):
# We have to be careful in determining if we found the correct file because multiple files with the same name may be in different directories
# within ctx if the files were moved within the change set. For example, in the following ctx.files() list, the former may have been moved to
- # the latter:
- # ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample']
+ # the latter: ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample']. Another scenario
+ # is that the file has been deleted.
+ deleted = False
filename = strip_path( filename )
for ctx_file in ctx.files():
ctx_file_name = strip_path( ctx_file )
if filename == ctx_file_name:
try:
+ # If the file was moved, its destination will be returned here.
fctx = ctx[ ctx_file ]
return fctx
except LookupError, e:
- return 'DELETED'
+ # Set deleted for now, and continue looking in case the file was moved instead of deleted.
+ deleted = True
+ if deleted:
+ return 'DELETED'
return None
def get_latest_repository_metadata( trans, id ):
"""Get last metadata defined for a specified repository from the database"""
@@ -772,37 +777,37 @@
def load_tool_from_changeset_revision( trans, repository_id, changeset_revision, tool_config_filename ):
"""
Return a loaded tool whose tool config file name (e.g., filtering.xml) is the value of tool_config_filename. The value of changeset_revision
- is a valid (downloadable) changset revision. If changeset_revision is the repository tip, then the tool will be loaded from it's file on disk.
- Otherwise, the tool config will be located in the repository manifest between the received valid changeset revision and the previous valid
- changeset revision (if one exists) or the first changeset revision in the repository (if one doesn't).
+ is a valid (downloadable) changset revision. The tool config will be located in the repository manifest between the received valid changeset
+ revision and the first changeset revision in the repository, searching backwards.
"""
def load_from_tmp_config( ctx, ctx_file, work_dir ):
tool = None
message = ''
tmp_tool_config = get_named_tmpfile_from_ctx( ctx, ctx_file, work_dir )
- element_tree = util.parse_xml( tmp_tool_config )
- element_tree_root = element_tree.getroot()
- # Look for code files required by the tool config.
- tmp_code_files = []
- for code_elem in element_tree_root.findall( 'code' ):
- code_file_name = code_elem.get( 'file' )
- tmp_code_file_name = copy_file_from_manifest( repo, ctx, code_file_name, work_dir )
- if tmp_code_file_name:
- tmp_code_files.append( tmp_code_file_name )
- try:
- tool = load_tool( trans, tmp_tool_config )
- except Exception, e:
- tool = None
- message = "Error loading tool: %s. " % str( e )
- for tmp_code_file in tmp_code_files:
+ if tmp_tool_config:
+ element_tree = util.parse_xml( tmp_tool_config )
+ element_tree_root = element_tree.getroot()
+ # Look for code files required by the tool config.
+ tmp_code_files = []
+ for code_elem in element_tree_root.findall( 'code' ):
+ code_file_name = code_elem.get( 'file' )
+ tmp_code_file_name = copy_file_from_manifest( repo, ctx, code_file_name, work_dir )
+ if tmp_code_file_name:
+ tmp_code_files.append( tmp_code_file_name )
try:
- os.unlink( tmp_code_file )
+ tool = load_tool( trans, tmp_tool_config )
+ except Exception, e:
+ tool = None
+ message = "Error loading tool: %s. " % str( e )
+ for tmp_code_file in tmp_code_files:
+ try:
+ os.unlink( tmp_code_file )
+ except:
+ pass
+ try:
+ os.unlink( tmp_tool_config )
except:
pass
- try:
- os.unlink( tmp_tool_config )
- except:
- pass
return tool, message
tool_config_filename = strip_path( tool_config_filename )
repository = get_repository( trans, repository_id )
@@ -842,41 +847,42 @@
valid = False
error_message = ''
tmp_config = get_named_tmpfile_from_ctx( ctx, filename, dir )
- if not ( check_binary( tmp_config ) or check_image( tmp_config ) or check_gzip( tmp_config )[ 0 ]
- or check_bz2( tmp_config )[ 0 ] or check_zip( tmp_config ) ):
- try:
- # Make sure we're looking at a tool config and not a display application config or something else.
- element_tree = util.parse_xml( tmp_config )
- element_tree_root = element_tree.getroot()
- is_tool_config = element_tree_root.tag == 'tool'
- except Exception, e:
- log.debug( "Error parsing %s, exception: %s" % ( tmp_config, str( e ) ) )
- is_tool_config = False
- if is_tool_config:
- # Load entries into the tool_data_tables if the tool requires them.
- tool_data_table_config = copy_file_from_manifest( repo, ctx, 'tool_data_table_conf.xml.sample', dir )
- if tool_data_table_config:
- error, correction_msg = handle_sample_tool_data_table_conf_file( trans.app, tool_data_table_config )
- # Look for code files required by the tool config. The directory to which dir refers should be removed by the caller.
- for code_elem in element_tree_root.findall( 'code' ):
- code_file_name = code_elem.get( 'file' )
- if not os.path.exists( os.path.join( dir, code_file_name ) ):
- tmp_code_file_name = copy_file_from_disk( code_file_name, repo_dir, dir )
- if tmp_code_file_name is None:
- tmp_code_file_name = copy_file_from_manifest( repo, ctx, code_file_name, dir )
+ if tmp_config:
+ if not ( check_binary( tmp_config ) or check_image( tmp_config ) or check_gzip( tmp_config )[ 0 ]
+ or check_bz2( tmp_config )[ 0 ] or check_zip( tmp_config ) ):
try:
- tool = load_tool( trans, tmp_config )
- valid = True
- except KeyError, e:
- valid = False
- error_message = 'This file requires an entry for "%s" in the tool_data_table_conf.xml file. Upload a file ' % str( e )
- error_message += 'named tool_data_table_conf.xml.sample to the repository that includes the required entry to correct '
- error_message += 'this error. '
+ # Make sure we're looking at a tool config and not a display application config or something else.
+ element_tree = util.parse_xml( tmp_config )
+ element_tree_root = element_tree.getroot()
+ is_tool_config = element_tree_root.tag == 'tool'
except Exception, e:
- valid = False
- error_message = str( e )
- # Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
- reset_tool_data_tables( trans.app )
+ log.debug( "Error parsing %s, exception: %s" % ( tmp_config, str( e ) ) )
+ is_tool_config = False
+ if is_tool_config:
+ # Load entries into the tool_data_tables if the tool requires them.
+ tool_data_table_config = copy_file_from_manifest( repo, ctx, 'tool_data_table_conf.xml.sample', dir )
+ if tool_data_table_config:
+ error, correction_msg = handle_sample_tool_data_table_conf_file( trans.app, tool_data_table_config )
+ # Look for code files required by the tool config. The directory to which dir refers should be removed by the caller.
+ for code_elem in element_tree_root.findall( 'code' ):
+ code_file_name = code_elem.get( 'file' )
+ if not os.path.exists( os.path.join( dir, code_file_name ) ):
+ tmp_code_file_name = copy_file_from_disk( code_file_name, repo_dir, dir )
+ if tmp_code_file_name is None:
+ tmp_code_file_name = copy_file_from_manifest( repo, ctx, code_file_name, dir )
+ try:
+ tool = load_tool( trans, tmp_config )
+ valid = True
+ except KeyError, e:
+ valid = False
+ error_message = 'This file requires an entry for "%s" in the tool_data_table_conf.xml file. Upload a file ' % str( e )
+ error_message += 'named tool_data_table_conf.xml.sample to the repository that includes the required entry to correct '
+ error_message += 'this error. '
+ except Exception, e:
+ valid = False
+ error_message = str( e )
+ # Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
+ reset_tool_data_tables( trans.app )
return is_tool_config, valid, tool, error_message
def new_tool_metadata_required( trans, id, metadata_dict ):
"""
diff -r f551610b687003fc09cd6ff7bbc506d237c67a75 -r d7580315fa206bcc64bc7607ee52bccfbf95e42f lib/galaxy/webapps/community/controllers/repository.py
--- a/lib/galaxy/webapps/community/controllers/repository.py
+++ b/lib/galaxy/webapps/community/controllers/repository.py
@@ -10,7 +10,7 @@
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.util.json import from_json_string, to_json_string
from galaxy.model.orm import *
-from galaxy.util.shed_util import get_changectx_for_changeset, get_configured_ui, get_named_tmpfile_from_ctx, make_tmp_directory, NOT_TOOL_CONFIGS, strip_path
+from galaxy.util.shed_util import get_changectx_for_changeset, get_configured_ui, make_tmp_directory, NOT_TOOL_CONFIGS, strip_path
from galaxy.tool_shed.encoding_util import *
from common import *
@@ -1257,12 +1257,6 @@
repo = hg.repository( get_configured_ui(), repo_dir )
ctx = get_changectx_for_changeset( repo, changeset_revision )
invalid_message = ''
- work_dir = make_tmp_directory()
- for filename in ctx:
- ctx_file_name = strip_path( filename )
- if ctx_file_name == tool_config:
- tool_config_path = get_named_tmpfile_from_ctx( ctx, filename, work_dir )
- break
metadata_dict, invalid_files, deleted_sample_files = generate_metadata_for_changeset_revision( trans,
repo,
repository_id,
@@ -1275,17 +1269,11 @@
invalid_tool_config_name = strip_path( invalid_tool_config )
if tool_config == invalid_tool_config_name:
invalid_message = invalid_msg
- break
+ break
tool, error_message = load_tool_from_changeset_revision( trans, repository_id, changeset_revision, tool_config )
- #if error_message:
- # message += error_message
tool_state = self.__new_state( trans )
is_malicious = changeset_is_malicious( trans, repository_id, repository.tip )
try:
- shutil.rmtree( work_dir )
- except:
- pass
- try:
if invalid_message:
message = invalid_message
return trans.fill_template( "/webapps/community/repository/tool_form.mako",
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: greg: Refinements for locating desired files within a tool shed repository manifest and setting metadata on tool shed repositories.
by Bitbucket 05 Jun '12
by Bitbucket 05 Jun '12
05 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/f551610b6870/
changeset: f551610b6870
user: greg
date: 2012-06-05 17:15:14
summary: Refinements for locating desired files within a tool shed repository manifest and setting metadata on tool shed repositories.
affected #: 5 files
diff -r 29b8e39db1094cb5a5586c4f7b404428641d518c -r f551610b687003fc09cd6ff7bbc506d237c67a75 lib/galaxy/util/shed_util.py
--- a/lib/galaxy/util/shed_util.py
+++ b/lib/galaxy/util/shed_util.py
@@ -21,6 +21,7 @@
log = logging.getLogger( __name__ )
+INITIAL_CHANGELOG_HASH = '000000000000'
# Characters that must be html escaped
MAPPED_CHARS = { '>' :'>',
'<' :'<',
@@ -771,28 +772,15 @@
if str( ctx ) == changeset_revision:
return ctx
return None
-def get_config( config_file, repo, repo_dir, ctx, dir ):
- """Return config_filename if it exists in some changeset of the repository."""
- # First look on disk.
- for root, dirs, files in os.walk( repo_dir ):
- if root.find( '.hg' ) < 0:
- for name in files:
- if name == config_file:
- dest_file_name = os.path.join( dir, name )
- shutil.copy( os.path.abspath( os.path.join( root, name ) ), dest_file_name )
- return os.path.abspath( dest_file_name )
- # Next look in the current change set.
- for filename in ctx:
- ctx_file_name = strip_path( filename )
- if ctx_file_name == config_file:
- return get_named_tmpfile_from_ctx( ctx, filename, dir )
- # Finally look in the repository manifest.
- for changeset in repo.changelog:
- prev_ctx = repo.changectx( changeset )
- for ctx_file in prev_ctx.files():
+def get_config( config_file, repo, ctx, dir ):
+ """Return the latest version of config_filename from the repository manifest."""
+ config_file = strip_path( config_file )
+ for changeset in reversed_upper_bounded_changelog( repo, ctx ):
+ changeset_ctx = repo.changectx( changeset )
+ for ctx_file in changeset_ctx.files():
ctx_file_name = strip_path( ctx_file )
if ctx_file_name == config_file:
- return get_named_tmpfile_from_ctx( prev_ctx, filename, dir )
+ return get_named_tmpfile_from_ctx( changeset_ctx, ctx_file, dir )
return None
def get_config_from_disk( config_file, relative_install_dir ):
for root, dirs, files in os.walk( relative_install_dir ):
@@ -808,7 +796,7 @@
repo_files_dir = os.path.join( install_dir, repository.name )
repo = hg.repository( get_configured_ui(), repo_files_dir )
ctx = get_changectx_for_changeset( repo, changeset_revision )
- config = get_config( config_file, repo, repo_files_dir, ctx, dir )
+ config = get_config( config_file, repo, ctx, dir )
return config
def get_configured_ui():
# Configure any desired ui settings.
@@ -867,6 +855,15 @@
ctx_rev = response.read()
response.close()
return ctx_rev
+def get_named_tmpfile_from_ctx( ctx, filename, dir ):
+ fctx = ctx[ filename ]
+ fh = tempfile.NamedTemporaryFile( 'wb', dir=dir )
+ tmp_filename = fh.name
+ fh.close()
+ fh = open( tmp_filename, 'wb' )
+ fh.write( fctx.data() )
+ fh.close()
+ return tmp_filename
def get_repository_by_shed_name_owner_changeset_revision( app, tool_shed, name, owner, changeset_revision ):
sa_session = app.model.context.current
if tool_shed.find( '//' ) > 0:
@@ -1383,6 +1380,31 @@
def reset_tool_data_tables( app ):
# Reset the tool_data_tables to an empty dictionary.
app.tool_data_tables.data_tables = {}
+def reversed_lower_upper_bounded_changelog( repo, excluded_lower_bounds_changeset_revision, included_upper_bounds_changeset_revision ):
+ """
+ Return a reversed list of changesets in the repository changelog after the excluded_lower_bounds_changeset_revision, but up to and
+ including the included_upper_bounds_changeset_revision. The value of excluded_lower_bounds_changeset_revision will be the value of
+ INITIAL_CHANGELOG_HASH if no valid changesets exist before included_upper_bounds_changeset_revision.
+ """
+ # To set excluded_lower_bounds_changeset_revision, calling methods should do the following, where the value of changeset_revision
+ # is a downloadable changeset_revision.
+ # excluded_lower_bounds_changeset_revision = get_previous_valid_changset_revision( repository, repo, changeset_revision )
+ if excluded_lower_bounds_changeset_revision == INITIAL_CHANGELOG_HASH:
+ appending_started = True
+ else:
+ appending_started = False
+ reversed_changelog = []
+ for changeset in repo.changelog:
+ changeset_hash = str( repo.changectx( changeset ) )
+ if appending_started:
+ reversed_changelog.insert( 0, changeset )
+ if changeset_hash == excluded_lower_bounds_changeset_revision and not appending_started:
+ appending_started = True
+ if changeset_hash == included_upper_bounds_changeset_revision:
+ break
+ return reversed_changelog
+def reversed_upper_bounded_changelog( repo, included_upper_bounds_changeset_revision ):
+ return reversed_lower_upper_bounded_changelog( repo, INITIAL_CHANGELOG_HASH, included_upper_bounds_changeset_revision )
def strip_path( fpath ):
file_path, file_name = os.path.split( fpath )
return file_name
diff -r 29b8e39db1094cb5a5586c4f7b404428641d518c -r f551610b687003fc09cd6ff7bbc506d237c67a75 lib/galaxy/webapps/community/controllers/admin.py
--- a/lib/galaxy/webapps/community/controllers/admin.py
+++ b/lib/galaxy/webapps/community/controllers/admin.py
@@ -473,9 +473,12 @@
.filter( trans.model.Repository.table.c.deleted == False ):
try:
error_message, status = reset_all_metadata_on_repository( trans, trans.security.encode_id( repository.id ) )
- if error_message:
+ if status not in [ 'ok' ] and error_message:
log.debug( "Error attempting to reset metadata on repository '%s': %s" % ( repository.name, error_message ) )
unsuccessful_count += 1
+ elif status in [ 'ok' ] and error_message:
+ log.debug( "Successfully reset metadata on repository %s, but encountered this problem: %s" % ( repository.name, error_message ) )
+ successful_count += 1
else:
log.debug( "Successfully reset metadata on repository %s" % repository.name )
successful_count += 1
diff -r 29b8e39db1094cb5a5586c4f7b404428641d518c -r f551610b687003fc09cd6ff7bbc506d237c67a75 lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -6,8 +6,9 @@
from galaxy.util.json import from_json_string, to_json_string
from galaxy.util.hash_util import *
from galaxy.util.shed_util import copy_sample_file, generate_datatypes_metadata, generate_tool_dependency_metadata, generate_tool_metadata
-from galaxy.util.shed_util import generate_workflow_metadata, get_changectx_for_changeset, get_config, get_configured_ui, handle_sample_tool_data_table_conf_file
-from galaxy.util.shed_util import make_tmp_directory, NOT_TOOL_CONFIGS, reset_tool_data_tables, strip_path, to_html_escaped, to_html_str, update_repository
+from galaxy.util.shed_util import generate_workflow_metadata, get_changectx_for_changeset, get_config, get_configured_ui, get_named_tmpfile_from_ctx
+from galaxy.util.shed_util import handle_sample_tool_data_table_conf_file, INITIAL_CHANGELOG_HASH, make_tmp_directory, NOT_TOOL_CONFIGS, reset_tool_data_tables
+from galaxy.util.shed_util import reversed_upper_bounded_changelog, strip_path, to_html_escaped, to_html_str, update_repository
from galaxy.web.base.controller import *
from galaxy.webapps.community import model
from galaxy.model.orm import *
@@ -201,7 +202,7 @@
if options:
if options.tool_data_table or options.missing_tool_data_table_name:
# Make sure the repository contains a tool_data_table_conf.xml.sample file.
- sample_tool_data_table_conf = get_config( 'tool_data_table_conf.xml.sample', repo, repo_dir, ctx, dir )
+ sample_tool_data_table_conf = get_config( 'tool_data_table_conf.xml.sample', repo, ctx, dir )
if sample_tool_data_table_conf:
error, correction_msg = handle_sample_tool_data_table_conf_file( trans.app, sample_tool_data_table_conf )
if error:
@@ -254,9 +255,9 @@
# The metadata associated with ancestor_changeset_revision is ancestor_metadata_dict. This changeset_revision is an ancestor of
# current_changeset_revision which is associated with current_metadata_dict.
#
- # TODO: a new repository_metadata record will be created only when this method returns the string 'not equal and not subset'. However,
- # we're currently also returning the strings 'no metadata', 'equal' and 'subset', depending upon how the 2 change sets compare. We'll
- # leave things this way for the current time in case we discover a use for these additional result strings.
+ # A new repository_metadata record will be created only when this method returns the string 'not equal and not subset'. However, we're
+ # currently also returning the strings 'no metadata', 'equal' and 'subset', depending upon how the 2 change sets compare. We'll leave
+ # things this way for the current time in case we discover a use for these additional result strings.
ancestor_datatypes = ancestor_metadata_dict.get( 'datatypes', [] )
ancestor_tools = ancestor_metadata_dict.get( 'tools', [] )
ancestor_guids = [ tool_dict[ 'guid' ] for tool_dict in ancestor_tools ]
@@ -357,41 +358,18 @@
tmp_filename = None
return tmp_filename
def copy_file_from_manifest( repo, ctx, filename, dir ):
- """Copy a file named filename from somewhere in the repository manifest to the directory to which dir refers."""
- filename = strip_path( filename )
- fctx = None
- found = False
- # First see if the file is in ctx. We have to be careful in determining if we found the correct file because multiple files
- # with the same name may be in different directories within ctx if the repository owner moved the files as part of the change set.
- # For example, in the following ctx.files() list, the former may have been moved to the latter:
- # ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample']
- for ctx_file in ctx.files():
- ctx_file_name = strip_path( ctx_file )
- if filename == ctx_file_name:
- try:
- fctx = ctx[ ctx_file ]
- found = True
- break
- except:
- continue
- if not found:
- # Find the file in the repository manifest.
- for changeset in repo.changelog:
- prev_ctx = repo.changectx( changeset )
- for ctx_file in prev_ctx.files():
- ctx_file_name = strip_path( ctx_file )
- if filename == ctx_file_name:
- try:
- fctx = prev_ctx[ ctx_file ]
- break
- except:
- continue
- if fctx:
- file_path = os.path.join( dir, filename )
- fh = open( file_path, 'wb' )
- fh.write( fctx.data() )
- fh.close()
- return file_path
+ """
+ Copy the latest version of the file named filename from the repository manifest to the directory to which dir refers.
+ """
+ for changeset in reversed_upper_bounded_changelog( repo, ctx ):
+ changeset_ctx = repo.changectx( changeset )
+ fctx = get_file_context_from_ctx( changeset_ctx, filename )
+ if fctx and fctx not in [ 'DELETED' ]:
+ file_path = os.path.join( dir, filename )
+ fh = open( file_path, 'wb' )
+ fh.write( fctx.data() )
+ fh.close()
+ return file_path
return None
def create_or_update_repository_metadata( trans, id, repository, changeset_revision, metadata_dict ):
repository_metadata = get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
@@ -444,10 +422,10 @@
invalid_tool_configs = []
original_tool_data_path = trans.app.config.tool_data_path
work_dir = make_tmp_directory()
- datatypes_config = get_config( 'datatypes_conf.xml', repo, repo_dir, ctx, work_dir )
+ datatypes_config = get_config( 'datatypes_conf.xml', repo, ctx, work_dir )
if datatypes_config:
metadata_dict = generate_datatypes_metadata( datatypes_config, metadata_dict )
- sample_files = get_sample_files( repo, repo_dir, dir=work_dir )
+ sample_files, deleted_sample_files = get_list_of_copied_sample_files( repo, ctx, dir=work_dir )
# Handle the tool_data_table_conf.xml.sample file if it is included in the repository.
if 'tool_data_table_conf.xml.sample' in sample_files:
tool_data_table_config = copy_file_from_manifest( repo, ctx, 'tool_data_table_conf.xml.sample', work_dir )
@@ -515,7 +493,7 @@
invalid_files.append( ( ctx_file_name, str( e ) ) )
if 'tools' in metadata_dict:
# Find tool_dependencies.xml if it exists. This step must be done after metadata for tools has been defined.
- tool_dependencies_config = get_config( 'tool_dependencies.xml', repo, repo_dir, ctx, work_dir )
+ tool_dependencies_config = get_config( 'tool_dependencies.xml', repo, ctx, work_dir )
if tool_dependencies_config:
metadata_dict = generate_tool_dependency_metadata( tool_dependencies_config, metadata_dict )
if invalid_tool_configs:
@@ -529,7 +507,7 @@
shutil.rmtree( work_dir )
except:
pass
- return metadata_dict, invalid_files
+ return metadata_dict, invalid_files, deleted_sample_files
def generate_tool_guid( trans, repository, tool ):
"""
Generate a guid for the received tool. The form of the guid is
@@ -548,21 +526,59 @@
return trans.sa_session.query( trans.model.Category ) \
.filter( trans.model.Category.table.c.deleted==False ) \
.order_by( trans.model.Category.table.c.name ).all()
+def get_file_context_from_ctx( ctx, filename ):
+ # We have to be careful in determining if we found the correct file because multiple files with the same name may be in different directories
+ # within ctx if the files were moved within the change set. For example, in the following ctx.files() list, the former may have been moved to
+ # the latter:
+ # ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample']
+ filename = strip_path( filename )
+ for ctx_file in ctx.files():
+ ctx_file_name = strip_path( ctx_file )
+ if filename == ctx_file_name:
+ try:
+ fctx = ctx[ ctx_file ]
+ return fctx
+ except LookupError, e:
+ return 'DELETED'
+ return None
def get_latest_repository_metadata( trans, id ):
"""Get last metadata defined for a specified repository from the database"""
return trans.sa_session.query( trans.model.RepositoryMetadata ) \
.filter( trans.model.RepositoryMetadata.table.c.repository_id == trans.security.decode_id( id ) ) \
.order_by( trans.model.RepositoryMetadata.table.c.id.desc() ) \
.first()
-def get_named_tmpfile_from_ctx( ctx, filename, dir ):
- fctx = ctx[ filename ]
- fh = tempfile.NamedTemporaryFile( 'wb', dir=dir )
- tmp_filename = fh.name
- fh.close()
- fh = open( tmp_filename, 'wb' )
- fh.write( fctx.data() )
- fh.close()
- return tmp_filename
+def get_list_of_copied_sample_files( repo, ctx, dir ):
+ """
+ Find all sample files (files in the repository with the special .sample extension) in the reversed repository manifest up to ctx. Copy
+ each discovered file to dir and return the list of filenames. If a .sample file was added in a changeset and then deleted in a later
+ changeset, it will be returned in the deleted_sample_files list. The caller will set the value of app.config.tool_data_path to dir in
+ order to load the tools and generate metadata for them.
+ """
+ deleted_sample_files = []
+ sample_files = []
+ for changeset in reversed_upper_bounded_changelog( repo, ctx ):
+ changeset_ctx = repo.changectx( changeset )
+ for ctx_file in changeset_ctx.files():
+ ctx_file_name = strip_path( ctx_file )
+ # If we decide in the future that files deleted later in the changelog should not be used, we can use the following if statement.
+ # if ctx_file_name.endswith( '.sample' ) and ctx_file_name not in sample_files and ctx_file_name not in deleted_sample_files:
+ if ctx_file_name.endswith( '.sample' ) and ctx_file_name not in sample_files:
+ fctx = get_file_context_from_ctx( changeset_ctx, ctx_file )
+ if fctx in [ 'DELETED' ]:
+ # Since the possibly future used if statement above is commented out, the same file that was initially added will be
+ # discovered in an earlier changeset in the change log and fall through to the else block below. In other words, if
+ # a file named blast2go.loc.sample was added in change set 0 and then deleted in changeset 3, the deleted file in changeset
+ # 3 will be handled here, but the later discovered file in changeset 0 will be handled in the else block below. In this
+ # way, the file contents will always be found for future tools even though the file was deleted.
+ if ctx_file_name not in deleted_sample_files:
+ deleted_sample_files.append( ctx_file_name )
+ else:
+ sample_files.append( ctx_file_name )
+ tmp_ctx_file_name = os.path.join( dir, ctx_file_name.replace( '.sample', '' ) )
+ fh = open( tmp_ctx_file_name, 'wb' )
+ fh.write( fctx.data() )
+ fh.close()
+ return sample_files, deleted_sample_files
def get_parent_id( trans, id, old_id, version, guid, changeset_revisions ):
parent_id = None
# Compare from most recent to oldest.
@@ -582,6 +598,10 @@
# The tool did not change through all of the changeset revisions.
return old_id
def get_previous_valid_changset_revision( repository, repo, before_changeset_revision ):
+ """
+ Return the downloadable changeset_revision in the repository changelog just prior to the changeset to which before_changeset_revision
+ refers. If there isn't one, return the hash value of an empty repository changlog, INITIAL_CHANGELOG_HASH.
+ """
changeset_tups = []
for repository_metadata in repository.downloadable_revisions:
changeset_revision = repository_metadata.changeset_revision
@@ -600,7 +620,7 @@
return previous_changeset_revision
else:
# Return the hash value of an empty repository changlog - note that this will not be a valid changset revision.
- return '000000000000'
+ return INITIAL_CHANGELOG_HASH
else:
previous_changeset_revision = current_changeset_revision
def get_repository( trans, id ):
@@ -640,29 +660,6 @@
return "%s:%s" % ( str( ctx.rev() ), changeset_revision )
else:
return "-1:%s" % changeset_revision
-def get_sample_files( repo, repo_dir, dir ):
- """Return a list of all files in the repository with the special .sample extension"""
- sample_files = []
- # Copy all discovered sample files to dir, and the caller will set the value of app.config.tool_data_path to dir
- # in order to load the tools and generate metadata for them. First look on disk.
- for root, dirs, files in os.walk( repo_dir ):
- if root.find( '.hg' ) < 0:
- for name in files:
- if name.endswith( '.sample' ) and name not in sample_files:
- new_name = name.replace( '.sample', '' )
- file_path = os.path.join( dir, new_name )
- shutil.copy( os.path.abspath( os.path.join( root, name ) ), file_path )
- sample_files.append( name )
- # Next look in the repository manifest.
- for changeset in repo.changelog:
- ctx = repo.changectx( changeset )
- for ctx_file in ctx.files():
- ctx_file_name = strip_path( ctx_file )
- if ctx_file_name.endswith( '.sample' ) and ctx_file_name not in sample_files:
- new_ctx_file_name = ctx_file_name.replace( '.sample', '' )
- copy_file_from_manifest( repo, ctx, ctx_file, dir )
- sample_files.append( ctx_file_name )
- return sample_files
def get_user( trans, id ):
"""Get a user from the database by id"""
return trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( id ) )
@@ -796,7 +793,7 @@
tool = load_tool( trans, tmp_tool_config )
except Exception, e:
tool = None
- message = "Error loading tool: %s. Clicking <b>Reset metadata</b> may correct this error." % str( e )
+ message = "Error loading tool: %s. " % str( e )
for tmp_code_file in tmp_code_files:
try:
os.unlink( tmp_code_file )
@@ -819,40 +816,19 @@
tool_data_table_config = copy_file_from_manifest( repo, ctx, 'tool_data_table_conf.xml.sample', work_dir )
if tool_data_table_config:
error, correction_msg = handle_sample_tool_data_table_conf_file( trans.app, tool_data_table_config )
- if changeset_revision == repository.tip:
- # Load the tool fron it's config file on disk.
- try:
- copied_tool_config = copy_file_from_disk( tool_config_filename, repo_files_dir, work_dir )
- tool = load_tool( trans, copied_tool_config )
- except Exception, e:
- tool = None
- message = "Error loading tool from config '%s': %s." % ( tool_config_filename, str( e ) )
- else:
- found = False
- tool = None
- # Get the tool config from ctx if present.
- for ctx_file in ctx.files():
+ found = False
+ # Get the latest revision of the tool config from the repository manifest up to the value of changeset_revision.
+ for changeset in reversed_upper_bounded_changelog( repo, changeset_revision ):
+ manifest_changeset_revision = str( repo.changectx( changeset ) )
+ manifest_ctx = repo.changectx( changeset )
+ for ctx_file in manifest_ctx.files():
ctx_file_name = strip_path( ctx_file )
if ctx_file_name == tool_config_filename:
found = True
break
if found:
- if found:
- tool, message = load_from_tmp_config( ctx, ctx_file, work_dir )
- else:
- # Get the tool config from the repository manifest between valid changeset revisions.
- previous_valid_changset_revision = get_previous_valid_changset_revision( repository, repo, changeset_revision )
- for changeset in reversed_filtered_changelog( repo, previous_valid_changset_revision, changeset_revision ):
- manifest_changeset_revision = str( repo.changectx( changeset ) )
- manifest_ctx = repo.changectx( changeset )
- for ctx_file in manifest_ctx.files():
- ctx_file_name = strip_path( ctx_file )
- if ctx_file_name == tool_config_filename:
- found = True
- break
- if found:
- tool, message = load_from_tmp_config( manifest_ctx, ctx_file, work_dir )
- break
+ tool, message = load_from_tmp_config( manifest_ctx, ctx_file, work_dir )
+ break
# Reset the tool_data_tables by loading the empty tool_data_table_conf.xml file.
reset_tool_data_tables( trans.app )
try:
@@ -968,6 +944,7 @@
log.debug( "Resetting all metadata on repository: %s" % repository.name )
repo_dir = repository.repo_path
repo = hg.repository( get_configured_ui(), repo_dir )
+ missing_sample_files = []
if len( repo ) == 1:
error_message, status = set_repository_metadata( trans, id, repository.tip, **kwd )
if error_message:
@@ -983,13 +960,16 @@
for changeset in repo.changelog:
current_changeset_revision = str( repo.changectx( changeset ) )
ctx = get_changectx_for_changeset( repo, current_changeset_revision )
- current_metadata_dict, invalid_files = generate_metadata_for_changeset_revision( trans,
- repo,
- id,
- ctx,
- current_changeset_revision,
- repo_dir,
- updating_tip=current_changeset_revision==repository.tip )
+ current_metadata_dict, invalid_files, deleted_sample_files = generate_metadata_for_changeset_revision( trans,
+ repo,
+ id,
+ ctx,
+ current_changeset_revision,
+ repo_dir,
+ updating_tip=current_changeset_revision==repository.tip )
+ for deleted_sample_file in deleted_sample_files:
+ if deleted_sample_file not in missing_sample_files:
+ missing_sample_files.append( deleted_sample_file )
if current_metadata_dict:
if ancestor_changeset_revision:
# Compare metadata from ancestor and current. The value of comparsion will be one of:
@@ -1032,27 +1012,12 @@
ancestor_metadata_dict = None
clean_repository_metadata( trans, id, changeset_revisions )
add_repository_metadata_tool_versions( trans, id, changeset_revisions )
+ if missing_sample_files:
+ message += "Metadata was successfully reset, but the following required sample files have been deleted from the repository so the version "
+ message += "of each file just prior to its deletion is being used. These files should be re-added to the repository as soon as possible: "
+ message += "<b>%s</b><br/>" % ', '.join( missing_sample_files )
+ return message, 'ok'
return '', 'ok'
-def reversed_filtered_changelog( repo, excluded_lower_bounds_changeset_revision, included_upper_bounds_changeset_revision ):
- """
- Return a reversed list of changesets in the repository changelog after the excluded_lower_bounds_changeset_revision, but up to and
- including the included_upper_bounds_changeset_revision. The value of excluded_lower_bounds_changeset_revision will be '000000000000'
- if no valid changesets exist before included_upper_bounds_changeset_revision.
- """
- if excluded_lower_bounds_changeset_revision == '000000000000':
- appending_started = True
- else:
- appending_started = False
- reversed_changelog = []
- for changeset in repo.changelog:
- changeset_hash = str( repo.changectx( changeset ) )
- if appending_started:
- reversed_changelog.insert( 0, changeset )
- if changeset_hash == excluded_lower_bounds_changeset_revision and not appending_started:
- appending_started = True
- if changeset_hash == included_upper_bounds_changeset_revision:
- break
- return reversed_changelog
def set_repository_metadata( trans, id, changeset_revision, content_alert_str='', **kwd ):
"""
Set repository metadata on the repository tip, returning specific error messages (if any) to alert the repository owner that the changeset
@@ -1068,13 +1033,13 @@
invalid_files = []
updating_tip = changeset_revision == repository.tip
if ctx is not None:
- metadata_dict, invalid_files = generate_metadata_for_changeset_revision( trans,
- repo,
- id,
- ctx,
- changeset_revision,
- repo_dir,
- updating_tip=updating_tip )
+ metadata_dict, invalid_files, deleted_sample_files = generate_metadata_for_changeset_revision( trans,
+ repo,
+ id,
+ ctx,
+ changeset_revision,
+ repo_dir,
+ updating_tip=updating_tip )
if metadata_dict:
if updating_tip:
if new_tool_metadata_required( trans, id, metadata_dict ) or new_workflow_metadata_required( trans, id, metadata_dict ):
@@ -1120,6 +1085,10 @@
# Here ctx is None.
message = "This repository does not include revision '%s'." % str( changeset_revision )
status = 'error'
+ if deleted_sample_files:
+ message += "Metadata was successfully reset, but the following required sample files have been deleted from the repository so the version "
+ message += "of each file just prior to its deletion is being used. These files should be re-added to the repository as soon as possible: "
+ message += "<b>%s</b><br/>" % ', '.join( deleted_sample_files )
if invalid_files:
if metadata_dict:
message = "Metadata was defined for some items in revision '%s'. " % str( changeset_revision )
diff -r 29b8e39db1094cb5a5586c4f7b404428641d518c -r f551610b687003fc09cd6ff7bbc506d237c67a75 lib/galaxy/webapps/community/controllers/hg.py
--- a/lib/galaxy/webapps/community/controllers/hg.py
+++ b/lib/galaxy/webapps/community/controllers/hg.py
@@ -27,8 +27,10 @@
repository = get_repository_by_name_and_owner( trans, name, owner )
if repository:
error_message, status = reset_all_metadata_on_repository( trans, trans.security.encode_id( repository.id ) )
- if error_message:
- log.debug( "Error resetting all metadata on repository '%s': %s" % ( str( repository.name ), str( error_message ) ) )
+ if status not in [ 'ok' ] and error_message:
+ log.debug( "Error resetting metadata on repository '%s': %s" % ( str( repository.name ), str( error_message ) ) )
+ elif status in [ 'ok' ] and error_message:
+ log.debug( "Successfully reset metadata on repository %s, but encountered problem: %s" % ( str( repository.name ), str( error_message ) ) )
return wsgi_app
def make_web_app():
diff -r 29b8e39db1094cb5a5586c4f7b404428641d518c -r f551610b687003fc09cd6ff7bbc506d237c67a75 lib/galaxy/webapps/community/controllers/repository.py
--- a/lib/galaxy/webapps/community/controllers/repository.py
+++ b/lib/galaxy/webapps/community/controllers/repository.py
@@ -10,7 +10,7 @@
from galaxy.web.framework.helpers import time_ago, iff, grids
from galaxy.util.json import from_json_string, to_json_string
from galaxy.model.orm import *
-from galaxy.util.shed_util import get_changectx_for_changeset, get_configured_ui, make_tmp_directory, NOT_TOOL_CONFIGS, strip_path
+from galaxy.util.shed_util import get_changectx_for_changeset, get_configured_ui, get_named_tmpfile_from_ctx, make_tmp_directory, NOT_TOOL_CONFIGS, strip_path
from galaxy.tool_shed.encoding_util import *
from common import *
@@ -323,17 +323,26 @@
email_alerts_repository_list_grid = EmailAlertsRepositoryListGrid()
category_list_grid = CategoryListGrid()
- @web.expose
- def index( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- # See if there are any RepositoryMetadata records since menu items require them.
- repository_metadata = trans.sa_session.query( model.RepositoryMetadata ).first()
- return trans.fill_template( '/webapps/community/index.mako',
- repository_metadata=repository_metadata,
- message=message,
- status=status )
+ def __add_hgweb_config_entry( self, trans, repository, repository_path ):
+ # Add an entry in the hgweb.config file for a new repository. An entry looks something like:
+ # repos/test/mira_assembler = database/community_files/000/repo_123.
+ hgweb_config = "%s/hgweb.config" % trans.app.config.root
+ if repository_path.startswith( './' ):
+ repository_path = repository_path.replace( './', '', 1 )
+ entry = "repos/%s/%s = %s" % ( repository.user.username, repository.name, repository_path )
+ tmp_fd, tmp_fname = tempfile.mkstemp()
+ if os.path.exists( hgweb_config ):
+ # Make a backup of the hgweb.config file since we're going to be changing it.
+ self.__make_hgweb_config_copy( trans, hgweb_config )
+ new_hgweb_config = open( tmp_fname, 'wb' )
+ for i, line in enumerate( open( hgweb_config ) ):
+ new_hgweb_config.write( line )
+ else:
+ new_hgweb_config = open( tmp_fname, 'wb' )
+ new_hgweb_config.write( '[paths]\n' )
+ new_hgweb_config.write( "%s\n" % entry )
+ new_hgweb_config.flush()
+ shutil.move( tmp_fname, os.path.abspath( hgweb_config ) )
@web.expose
def browse_categories( self, trans, **kwd ):
if 'f-free-text-search' in kwd:
@@ -360,9 +369,146 @@
return trans.response.send_redirect( web.url_for( controller='repository',
action='browse_repositories',
**kwd ) )
- # Render the list view
return self.category_list_grid( trans, **kwd )
@web.expose
+ def browse_invalid_tools( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ webapp = params.get( 'webapp', 'community' )
+ cntrller = params.get( 'cntrller', 'repository' )
+ is_admin = trans.user_is_admin()
+ invalid_tools_dict = odict()
+ if is_admin and cntrller == 'admin':
+ for repository in trans.sa_session.query( trans.model.Repository ) \
+ .filter( trans.model.Repository.table.c.deleted == False ) \
+ .order_by( trans.model.Repository.table.c.name ):
+ for downloadable_revision in repository.downloadable_revisions:
+ metadata = downloadable_revision.metadata
+ invalid_tools = metadata.get( 'invalid_tools', [] )
+ for invalid_tool_config in invalid_tools:
+ invalid_tools_dict[ invalid_tool_config ] = ( repository.id, repository.name, downloadable_revision.changeset_revision )
+ else:
+ for repository in trans.sa_session.query( trans.model.Repository ) \
+ .filter( and_( trans.model.Repository.table.c.deleted == False,
+ trans.model.Repository.table.c.user_id == trans.user.id ) ) \
+ .order_by( trans.model.Repository.table.c.name ):
+ for downloadable_revision in repository.downloadable_revisions:
+ metadata = downloadable_revision.metadata
+ invalid_tools = metadata.get( 'invalid_tools', [] )
+ for invalid_tool_config in invalid_tools:
+ invalid_tools_dict[ invalid_tool_config ] = ( repository.id, repository.name, downloadable_revision.changeset_revision )
+ return trans.fill_template( '/webapps/community/repository/browse_invalid_tools.mako',
+ cntrller=cntrller,
+ invalid_tools_dict=invalid_tools_dict,
+ webapp=webapp,
+ message=message,
+ status=status )
+ @web.expose
+ def browse_repositories( self, trans, **kwd ):
+ # We add params to the keyword dict in this method in order to rename the param
+ # with an "f-" prefix, simulating filtering by clicking a search link. We have
+ # to take this approach because the "-" character is illegal in HTTP requests.
+ if 'webapp' not in kwd:
+ kwd[ 'webapp' ] = 'community'
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "view_or_manage_repository":
+ repository_id = kwd[ 'id' ]
+ repository = get_repository( trans, repository_id )
+ is_admin = trans.user_is_admin()
+ if is_admin or repository.user == trans.user:
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='manage_repository',
+ **kwd ) )
+ else:
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='view_repository',
+ **kwd ) )
+ elif operation == "edit_repository":
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='edit_repository',
+ **kwd ) )
+ elif operation == "repositories_by_user":
+ # Eliminate the current filters if any exist.
+ for k, v in kwd.items():
+ if k.startswith( 'f-' ):
+ del kwd[ k ]
+ if 'user_id' in kwd:
+ user = get_user( trans, kwd[ 'user_id' ] )
+ kwd[ 'f-email' ] = user.email
+ del kwd[ 'user_id' ]
+ else:
+ # The received id is the repository id, so we need to get the id of the user
+ # that uploaded the repository.
+ repository_id = kwd.get( 'id', None )
+ repository = get_repository( trans, repository_id )
+ kwd[ 'f-email' ] = repository.user.email
+ elif operation == "my_repositories":
+ # Eliminate the current filters if any exist.
+ for k, v in kwd.items():
+ if k.startswith( 'f-' ):
+ del kwd[ k ]
+ kwd[ 'f-email' ] = trans.user.email
+ elif operation == "repositories_by_category":
+ # Eliminate the current filters if any exist.
+ for k, v in kwd.items():
+ if k.startswith( 'f-' ):
+ del kwd[ k ]
+ category_id = kwd.get( 'id', None )
+ category = get_category( trans, category_id )
+ kwd[ 'f-Category.name' ] = category.name
+ elif operation == "receive email alerts":
+ if trans.user:
+ if kwd[ 'id' ]:
+ kwd[ 'caller' ] = 'browse_repositories'
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='set_email_alerts',
+ **kwd ) )
+ else:
+ kwd[ 'message' ] = 'You must be logged in to set email alerts.'
+ kwd[ 'status' ] = 'error'
+ del kwd[ 'operation' ]
+ # The changeset_revision_select_field in the RepositoryListGrid performs a refresh_on_change
+ # which sends in request parameters like changeset_revison_1, changeset_revision_2, etc. One
+ # of the many select fields on the grid performed the refresh_on_change, so we loop through
+ # all of the received values to see which value is not the repository tip. If we find it, we
+ # know the refresh_on_change occurred, and we have the necessary repository id and change set
+ # revision to pass on.
+ for k, v in kwd.items():
+ changset_revision_str = 'changeset_revision_'
+ if k.startswith( changset_revision_str ):
+ repository_id = trans.security.encode_id( int( k.lstrip( changset_revision_str ) ) )
+ repository = get_repository( trans, repository_id )
+ if repository.tip != v:
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='browse_repositories',
+ operation='view_or_manage_repository',
+ id=trans.security.encode_id( repository.id ),
+ changeset_revision=v ) )
+ return self.repository_list_grid( trans, **kwd )
+ @web.expose
+ def browse_repository( self, trans, id, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ webapp = params.get( 'webapp', 'community' )
+ commit_message = util.restore_text( params.get( 'commit_message', 'Deleted selected files' ) )
+ repository = get_repository( trans, id )
+ repo = hg.repository( get_configured_ui(), repository.repo_path )
+ current_working_dir = os.getcwd()
+ # Update repository files for browsing.
+ update_repository( repo )
+ is_malicious = changeset_is_malicious( trans, id, repository.tip )
+ return trans.fill_template( '/webapps/community/repository/browse_repository.mako',
+ repo=repo,
+ repository=repository,
+ commit_message=commit_message,
+ is_malicious=is_malicious,
+ webapp=webapp,
+ message=message,
+ status=status )
+ @web.expose
def browse_valid_repositories( self, trans, **kwd ):
webapp = kwd.get( 'webapp', 'community' )
galaxy_url = kwd.get( 'galaxy_url', None )
@@ -404,40 +550,400 @@
allow_multiple=False,
async_compatible=False ) ]
return self.valid_repository_list_grid( trans, **kwd )
+ def __build_allow_push_select_field( self, trans, current_push_list, selected_value='none' ):
+ options = []
+ for user in trans.sa_session.query( trans.model.User ):
+ if user.username not in current_push_list:
+ options.append( user )
+ return build_select_field( trans,
+ objs=options,
+ label_attr='username',
+ select_field_name='allow_push',
+ selected_value=selected_value,
+ refresh_on_change=False,
+ multiple=True )
+ def __change_hgweb_config_entry( self, trans, repository, old_repository_name, new_repository_name ):
+ # Change an entry in the hgweb.config file for a repository. This only happens when
+ # the owner changes the name of the repository. An entry looks something like:
+ # repos/test/mira_assembler = database/community_files/000/repo_123.
+ hgweb_config = "%s/hgweb.config" % trans.app.config.root
+ # Make a backup of the hgweb.config file since we're going to be changing it.
+ self.__make_hgweb_config_copy( trans, hgweb_config )
+ repo_dir = repository.repo_path
+ old_lhs = "repos/%s/%s" % ( repository.user.username, old_repository_name )
+ new_entry = "repos/%s/%s = %s\n" % ( repository.user.username, new_repository_name, repo_dir )
+ tmp_fd, tmp_fname = tempfile.mkstemp()
+ new_hgweb_config = open( tmp_fname, 'wb' )
+ for i, line in enumerate( open( hgweb_config ) ):
+ if line.startswith( old_lhs ):
+ new_hgweb_config.write( new_entry )
+ else:
+ new_hgweb_config.write( line )
+ new_hgweb_config.flush()
+ shutil.move( tmp_fname, os.path.abspath( hgweb_config ) )
@web.expose
- def browse_invalid_tools( self, trans, **kwd ):
+ def check_for_updates( self, trans, **kwd ):
+ """
+ Handle a request from a local Galaxy instance. If the request originated with the Galaxy instances' UpdateManager, the value of 'webapp'
+ will be 'update_manager'.
+ """
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ # If the request originated with the UpdateManager, it will not include a galaxy_url.
+ galaxy_url = kwd.get( 'galaxy_url', '' )
+ name = params.get( 'name', None )
+ owner = params.get( 'owner', None )
+ changeset_revision = params.get( 'changeset_revision', None )
+ webapp = params.get( 'webapp', 'community' )
+ repository = get_repository_by_name_and_owner( trans, name, owner )
+ repo_dir = repository.repo_path
+ repo = hg.repository( get_configured_ui(), repo_dir )
+ from_update_manager = webapp == 'update_manager'
+ if from_update_manager:
+ update = 'true'
+ no_update = 'false'
+ else:
+ # Start building up the url to redirect back to the calling Galaxy instance.
+ url = '%sadmin_toolshed/update_to_changeset_revision?tool_shed_url=%s' % ( galaxy_url, url_for( '/', qualified=True ) )
+ url += '&name=%s&owner=%s&changeset_revision=%s&latest_changeset_revision=' % ( repository.name, repository.user.username, changeset_revision )
+ if changeset_revision == repository.tip:
+ # If changeset_revision is the repository tip, we know there are no additional updates for the tools.
+ if from_update_manager:
+ return no_update
+ # Return the same value for changeset_revision and latest_changeset_revision.
+ url += repository.tip
+ else:
+ repository_metadata = get_repository_metadata_by_changeset_revision( trans,
+ trans.security.encode_id( repository.id ),
+ changeset_revision )
+ if repository_metadata:
+ # If changeset_revision is in the repository_metadata table for this repository, then we know there are no additional updates
+ # for the tools.
+ if from_update_manager:
+ return no_update
+ else:
+ # Return the same value for changeset_revision and latest_changeset_revision.
+ url += changeset_revision
+ else:
+ # TODO: Re-engineer this to define the change set for update to be the one just before the next change set in the repository_metadata
+ # table for this repository.
+ # The changeset_revision column in the repository_metadata table has been updated with a new changeset_revision value since the
+ # repository was cloned. Load each tool in the repository's changeset_revision to generate a list of tool guids, since guids
+ # differentiate tools by id and version.
+ ctx = get_changectx_for_changeset( repo, changeset_revision )
+ if ctx is not None:
+ work_dir = make_tmp_directory()
+ tool_guids = []
+ for filename in ctx:
+ # Find all tool configs in this repository changeset_revision.
+ if filename not in NOT_TOOL_CONFIGS and filename.endswith( '.xml' ):
+ is_tool_config, valid, tool, error_message = load_tool_from_tmp_directory( trans,
+ repo,
+ repo_dir,
+ ctx,
+ filename,
+ work_dir )
+ if valid and tool is not None:
+ tool_guids.append( generate_tool_guid( trans, repository, tool ) )
+ tool_guids.sort()
+ if tool_guids:
+ # Compare our list of tool guids against those in each repository_metadata record for the repository to find the
+ # repository_metadata record with the changeset_revision value we want to pass back to the caller.
+ found = False
+ for repository_metadata in get_repository_metadata_by_repository_id( trans, trans.security.encode_id( repository.id ) ):
+ metadata = repository_metadata.metadata
+ metadata_tool_guids = []
+ for tool_dict in metadata[ 'tools' ]:
+ metadata_tool_guids.append( tool_dict[ 'guid' ] )
+ metadata_tool_guids.sort()
+ if tool_guids == metadata_tool_guids:
+ # We've found the repository_metadata record whose changeset_revision value has been updated.
+ if from_update_manager:
+ return update
+ url += repository_metadata.changeset_revision
+ # Get the ctx_rev for the discovered changeset_revision.
+ latest_ctx = get_changectx_for_changeset( repo, repository_metadata.changeset_revision )
+ found = True
+ break
+ if not found:
+ # There must be a problem in the data, so we'll just send back the received changeset_revision.
+ log.debug( "Possible data corruption - updated repository_metadata cannot be found for repository id %d." % repository.id )
+ if from_update_manager:
+ return no_update
+ url += changeset_revision
+ else:
+ # There are no tools in the changeset_revision, so no tool updates are possible.
+ if from_update_manager:
+ return no_update
+ url += changeset_revision
+ try:
+ shutil.rmtree( work_dir )
+ except:
+ pass
+ url += '&latest_ctx_rev=%s' % str( latest_ctx.rev() )
+ return trans.response.send_redirect( url )
+ @web.expose
+ def contact_owner( self, trans, id, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ repository = get_repository( trans, id )
+ if trans.user and trans.user.email:
+ return trans.fill_template( "/webapps/community/repository/contact_owner.mako",
+ repository=repository,
+ message=message,
+ status=status )
+ else:
+ # Do all we can to eliminate spam.
+ return trans.show_error_message( "You must be logged in to contact the owner of a repository." )
+ def __create_hgrc_file( self, repository ):
+ # At this point, an entry for the repository is required to be in the hgweb.config file so we can call repository.repo_path.
+ # Since we support both http and https, we set push_ssl to False to override the default (which is True) in the mercurial api.
+ # The hg purge extension purges all files and directories not being tracked by mercurial in the current repository. It'll
+ # remove unknown files and empty directories. This is not currently used because it is not supported in the mercurial API.
+ repo = hg.repository( get_configured_ui(), path=repository.repo_path )
+ fp = repo.opener( 'hgrc', 'wb' )
+ fp.write( '[paths]\n' )
+ fp.write( 'default = .\n' )
+ fp.write( 'default-push = .\n' )
+ fp.write( '[web]\n' )
+ fp.write( 'allow_push = %s\n' % repository.user.username )
+ fp.write( 'name = %s\n' % repository.name )
+ fp.write( 'push_ssl = false\n' )
+ fp.write( '[extensions]\n' )
+ fp.write( 'hgext.purge=' )
+ fp.close()
+ @web.expose
+ def create_repository( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ categories = get_categories( trans )
+ if not categories:
+ message = 'No categories have been configured in this instance of the Galaxy Tool Shed. ' + \
+ 'An administrator needs to create some via the Administrator control panel before creating repositories.',
+ status = 'error'
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='browse_repositories',
+ message=message,
+ status=status ) )
+ name = util.restore_text( params.get( 'name', '' ) )
+ description = util.restore_text( params.get( 'description', '' ) )
+ long_description = util.restore_text( params.get( 'long_description', '' ) )
+ category_ids = util.listify( params.get( 'category_id', '' ) )
+ selected_categories = [ trans.security.decode_id( id ) for id in category_ids ]
+ if params.get( 'create_repository_button', False ):
+ error = False
+ message = self.__validate_repository_name( name, trans.user )
+ if message:
+ error = True
+ if not description:
+ message = 'Enter a description.'
+ error = True
+ if not error:
+ # Add the repository record to the db
+ repository = trans.app.model.Repository( name=name,
+ description=description,
+ long_description=long_description,
+ user_id=trans.user.id )
+ # Flush to get the id
+ trans.sa_session.add( repository )
+ trans.sa_session.flush()
+ # Determine the repository's repo_path on disk
+ dir = os.path.join( trans.app.config.file_path, *directory_hash_id( repository.id ) )
+ # Create directory if it does not exist
+ if not os.path.exists( dir ):
+ os.makedirs( dir )
+ # Define repo name inside hashed directory
+ repository_path = os.path.join( dir, "repo_%d" % repository.id )
+ # Create local repository directory
+ if not os.path.exists( repository_path ):
+ os.makedirs( repository_path )
+ # Create the local repository
+ repo = hg.repository( get_configured_ui(), repository_path, create=True )
+ # Add an entry in the hgweb.config file for the local repository
+ # This enables calls to repository.repo_path
+ self.__add_hgweb_config_entry( trans, repository, repository_path )
+ # Create a .hg/hgrc file for the local repository
+ self.__create_hgrc_file( repository )
+ flush_needed = False
+ if category_ids:
+ # Create category associations
+ for category_id in category_ids:
+ category = trans.app.model.Category.get( trans.security.decode_id( category_id ) )
+ rca = trans.app.model.RepositoryCategoryAssociation( repository, category )
+ trans.sa_session.add( rca )
+ flush_needed = True
+ if flush_needed:
+ trans.sa_session.flush()
+ message = "Repository '%s' has been created." % repository.name
+ trans.response.send_redirect( web.url_for( controller='repository',
+ action='view_repository',
+ webapp='community',
+ message=message,
+ id=trans.security.encode_id( repository.id ) ) )
+ return trans.fill_template( '/webapps/community/repository/create_repository.mako',
+ name=name,
+ description=description,
+ long_description=long_description,
+ selected_categories=selected_categories,
+ categories=categories,
+ message=message,
+ status=status )
+ @web.expose
+ def display_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
webapp = params.get( 'webapp', 'community' )
- cntrller = params.get( 'cntrller', 'repository' )
- is_admin = trans.user_is_admin()
- invalid_tools_dict = odict()
- if is_admin and cntrller == 'admin':
- for repository in trans.sa_session.query( trans.model.Repository ) \
- .filter( trans.model.Repository.table.c.deleted == False ) \
- .order_by( trans.model.Repository.table.c.name ):
- for downloadable_revision in repository.downloadable_revisions:
- metadata = downloadable_revision.metadata
- invalid_tools = metadata.get( 'invalid_tools', [] )
- for invalid_tool_config in invalid_tools:
- invalid_tools_dict[ invalid_tool_config ] = ( repository.id, repository.name, downloadable_revision.changeset_revision )
- else:
- for repository in trans.sa_session.query( trans.model.Repository ) \
- .filter( and_( trans.model.Repository.table.c.deleted == False,
- trans.model.Repository.table.c.user_id == trans.user.id ) ) \
- .order_by( trans.model.Repository.table.c.name ):
- for downloadable_revision in repository.downloadable_revisions:
- metadata = downloadable_revision.metadata
- invalid_tools = metadata.get( 'invalid_tools', [] )
- for invalid_tool_config in invalid_tools:
- invalid_tools_dict[ invalid_tool_config ] = ( repository.id, repository.name, downloadable_revision.changeset_revision )
- return trans.fill_template( '/webapps/community/repository/browse_invalid_tools.mako',
- cntrller=cntrller,
- invalid_tools_dict=invalid_tools_dict,
+ repository = get_repository( trans, repository_id )
+ tool, message = load_tool_from_changeset_revision( trans, repository_id, changeset_revision, tool_config )
+ tool_state = self.__new_state( trans )
+ is_malicious = changeset_is_malicious( trans, repository_id, repository.tip )
+ try:
+ return trans.fill_template( "/webapps/community/repository/tool_form.mako",
+ repository=repository,
+ changeset_revision=changeset_revision,
+ tool=tool,
+ tool_state=tool_state,
+ is_malicious=is_malicious,
+ webapp=webapp,
+ message=message,
+ status=status )
+ except Exception, e:
+ message = "Error displaying tool, probably due to a problem in the tool config. The exception is: %s." % str( e )
+ if webapp == 'galaxy':
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='preview_tools_in_changeset',
+ repository_id=repository_id,
+ changeset_revision=changeset_revision,
+ message=message,
+ status='error' ) )
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='browse_repositories',
+ operation='view_or_manage_repository',
+ id=repository_id,
+ changeset_revision=changeset_revision,
+ message=message,
+ status='error' ) )
+ @web.expose
+ def download( self, trans, repository_id, changeset_revision, file_type, **kwd ):
+ # Download an archive of the repository files compressed as zip, gz or bz2.
+ params = util.Params( kwd )
+ repository = get_repository( trans, repository_id )
+ # Allow hgweb to handle the download. This requires the tool shed
+ # server account's .hgrc file to include the following setting:
+ # [web]
+ # allow_archive = bz2, gz, zip
+ if file_type == 'zip':
+ file_type_str = '%s.zip' % changeset_revision
+ elif file_type == 'bz2':
+ file_type_str = '%s.tar.bz2' % changeset_revision
+ elif file_type == 'gz':
+ file_type_str = '%s.tar.gz' % changeset_revision
+ repository.times_downloaded += 1
+ trans.sa_session.add( repository )
+ trans.sa_session.flush()
+ download_url = '/repos/%s/%s/archive/%s' % ( repository.user.username, repository.name, file_type_str )
+ return trans.response.send_redirect( download_url )
+ def __encode_repo_info_dict( self, trans, webapp, repository_metadata_ids ):
+ repo_info_dict = {}
+ includes_tools = False
+ for repository_metadata_id in repository_metadata_ids:
+ repository_metadata = get_repository_metadata_by_id( trans, repository_metadata_id )
+ if not includes_tools and 'tools' in repository_metadata.metadata:
+ includes_tools = True
+ repository = get_repository( trans, trans.security.encode_id( repository_metadata.repository_id ) )
+ # Get the changelog rev for this changeset_revision.
+ repo_dir = repository.repo_path
+ repo = hg.repository( get_configured_ui(), repo_dir )
+ changeset_revision = repository_metadata.changeset_revision
+ ctx = get_changectx_for_changeset( repo, changeset_revision )
+ repository_id = trans.security.encode_id( repository.id )
+ repository_clone_url = generate_clone_url( trans, repository_id )
+ repo_info_dict[ repository.name ] = ( repository.description, repository_clone_url, changeset_revision, str( ctx.rev() ) )
+ return encode( repo_info_dict ), includes_tools
+ @web.expose
+ def find_tools( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ webapp = params.get( 'webapp', 'community' )
+ galaxy_url = kwd.get( 'galaxy_url', None )
+ if galaxy_url:
+ trans.set_cookie( galaxy_url, name='toolshedgalaxyurl' )
+ if 'operation' in kwd:
+ item_id = kwd.get( 'id', '' )
+ if item_id:
+ operation = kwd[ 'operation' ].lower()
+ is_admin = trans.user_is_admin()
+ if operation == "view_or_manage_repository":
+ # The received id is a RepositoryMetadata id, so we have to get the repository id.
+ repository_metadata = get_repository_metadata_by_id( trans, item_id )
+ repository_id = trans.security.encode_id( repository_metadata.repository.id )
+ repository = get_repository( trans, repository_id )
+ kwd[ 'id' ] = repository_id
+ kwd[ 'changeset_revision' ] = repository_metadata.changeset_revision
+ if webapp == 'community' and ( is_admin or repository.user == trans.user ):
+ a = 'manage_repository'
+ else:
+ a = 'view_repository'
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action=a,
+ **kwd ) )
+ if operation == "install":
+ galaxy_url = trans.get_cookie( name='toolshedgalaxyurl' )
+ encoded_repo_info_dict, includes_tools = self.__encode_repo_info_dict( trans, webapp, util.listify( item_id ) )
+ url = '%sadmin_toolshed/install_repository?tool_shed_url=%s&webapp=%s&repo_info_dict=%s&includes_tools=%s' % \
+ ( galaxy_url, url_for( '/', qualified=True ), webapp, encoded_repo_info_dict, str( includes_tools ) )
+ return trans.response.send_redirect( url )
+ else:
+ # This can only occur when there is a multi-select grid with check boxes and an operation,
+ # and the user clicked the operation button without checking any of the check boxes.
+ return trans.show_error_message( "No items were selected." )
+ tool_ids = [ item.lower() for item in util.listify( kwd.get( 'tool_id', '' ) ) ]
+ tool_names = [ item.lower() for item in util.listify( kwd.get( 'tool_name', '' ) ) ]
+ tool_versions = [ item.lower() for item in util.listify( kwd.get( 'tool_version', '' ) ) ]
+ exact_matches = params.get( 'exact_matches', '' )
+ exact_matches_checked = CheckboxField.is_checked( exact_matches )
+ match_tuples = []
+ ok = True
+ if tool_ids or tool_names or tool_versions:
+ ok, match_tuples = self.__search_repository_metadata( trans, exact_matches_checked, tool_ids=tool_ids, tool_names=tool_names, tool_versions=tool_versions )
+ if ok:
+ kwd[ 'match_tuples' ] = match_tuples
+ # Render the list view
+ if webapp == 'galaxy':
+ # Our initial request originated from a Galaxy instance.
+ global_actions = [ grids.GridAction( "Browse valid repositories",
+ dict( controller='repository', action='browse_valid_repositories', webapp=webapp ) ),
+ grids.GridAction( "Search for valid tools",
+ dict( controller='repository', action='find_tools', webapp=webapp ) ),
+ grids.GridAction( "Search for workflows",
+ dict( controller='repository', action='find_workflows', webapp=webapp ) ) ]
+ self.install_matched_repository_list_grid.global_actions = global_actions
+ install_url_args = dict( controller='repository', action='find_tools', webapp=webapp )
+ operations = [ grids.GridOperation( "Install", url_args=install_url_args, allow_multiple=True, async_compatible=False ) ]
+ self.install_matched_repository_list_grid.operations = operations
+ return self.install_matched_repository_list_grid( trans, **kwd )
+ else:
+ kwd[ 'message' ] = "tool id: <b>%s</b><br/>tool name: <b>%s</b><br/>tool version: <b>%s</b><br/>exact matches only: <b>%s</b>" % \
+ ( self.__stringify( tool_ids ), self.__stringify( tool_names ), self.__stringify( tool_versions ), str( exact_matches_checked ) )
+ self.matched_repository_list_grid.title = "Repositories with matching tools"
+ return self.matched_repository_list_grid( trans, **kwd )
+ else:
+ message = "No search performed - each field must contain the same number of comma-separated items."
+ status = "error"
+ exact_matches_check_box = CheckboxField( 'exact_matches', checked=exact_matches_checked )
+ return trans.fill_template( '/webapps/community/repository/find_tools.mako',
webapp=webapp,
+ tool_id=self.__stringify( tool_ids ),
+ tool_name=self.__stringify( tool_names ),
+ tool_version=self.__stringify( tool_versions ),
+ exact_matches_check_box=exact_matches_check_box,
message=message,
- status=status )
+ status=status )
@web.expose
def find_workflows( self, trans, **kwd ):
params = util.Params( kwd )
@@ -521,160 +1027,127 @@
message=message,
status=status )
@web.expose
- def find_tools( self, trans, **kwd ):
+ def get_ctx_rev( self, trans, **kwd ):
+ """Given a repository and changeset_revision, return the correct ctx.rev() value."""
+ repository_name = kwd[ 'name' ]
+ repository_owner = kwd[ 'owner' ]
+ changeset_revision = kwd[ 'changeset_revision' ]
+ repository = get_repository_by_name_and_owner( trans, repository_name, repository_owner )
+ repo_dir = repository.repo_path
+ repo = hg.repository( get_configured_ui(), repo_dir )
+ ctx = get_changectx_for_changeset( repo, changeset_revision )
+ if ctx:
+ return str( ctx.rev() )
+ return ''
+ @web.json
+ def get_file_contents( self, trans, file_path ):
+ # Avoid caching
+ trans.response.headers['Pragma'] = 'no-cache'
+ trans.response.headers['Expires'] = '0'
+ if is_gzip( file_path ):
+ to_html = to_html_str( '\ngzip compressed file\n' )
+ elif is_bz2( file_path ):
+ to_html = to_html_str( '\nbz2 compressed file\n' )
+ elif check_zip( file_path ):
+ to_html = to_html_str( '\nzip compressed file\n' )
+ elif check_binary( file_path ):
+ to_html = to_html_str( '\nBinary file\n' )
+ else:
+ to_html = ''
+ for i, line in enumerate( open( file_path ) ):
+ to_html = '%s%s' % ( to_html, to_html_str( line ) )
+ if len( to_html ) > MAX_CONTENT_SIZE:
+ large_str = '\nFile contents truncated because file size is larger than maximum viewing size of %s\n' % util.nice_size( MAX_CONTENT_SIZE )
+ to_html = '%s%s' % ( to_html, to_html_str( large_str ) )
+ break
+ return to_html
+ def __get_files( self, trans, folder_path ):
+ contents = []
+ for item in os.listdir( folder_path ):
+ # Skip .hg directories
+ if str( item ).startswith( '.hg' ):
+ continue
+ if os.path.isdir( os.path.join( folder_path, item ) ):
+ # Append a '/' character so that our jquery dynatree will
+ # function properly.
+ item = '%s/' % item
+ contents.append( item )
+ if contents:
+ contents.sort()
+ return contents
+ @web.expose
+ def get_readme( self, trans, **kwd ):
+ """If the received changeset_revision includes a file named readme (case ignored), return it's contents."""
+ repository_name = kwd[ 'name' ]
+ repository_owner = kwd[ 'owner' ]
+ changeset_revision = kwd[ 'changeset_revision' ]
+ valid_filenames = [ r for r in README_FILES ]
+ for r in README_FILES:
+ valid_filenames.append( '%s.txt' % r )
+ valid_filenames.append( '%s.txt' % repository_name )
+ repository = get_repository_by_name_and_owner( trans, repository_name, repository_owner )
+ repo_dir = repository.repo_path
+ for root, dirs, files in os.walk( repo_dir ):
+ for name in files:
+ if name.lower() in valid_filenames:
+ f = open( os.path.join( root, name ), 'r' )
+ text = f.read()
+ f.close()
+ return str( text )
+ return ''
+ @web.expose
+ def get_tool_dependencies( self, trans, **kwd ):
+ # Handle a request from a local Galaxy instance. If the request originated with the Galaxy instances' InstallManager, the value of 'webapp'
+ # will be 'install_manager'.
params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
+ message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
+ # If the request originated with the UpdateManager, it will not include a galaxy_url.
+ galaxy_url = kwd.get( 'galaxy_url', '' )
+ name = params.get( 'name', None )
+ owner = params.get( 'owner', None )
+ changeset_revision = params.get( 'changeset_revision', None )
webapp = params.get( 'webapp', 'community' )
- galaxy_url = kwd.get( 'galaxy_url', None )
- if galaxy_url:
- trans.set_cookie( galaxy_url, name='toolshedgalaxyurl' )
- if 'operation' in kwd:
- item_id = kwd.get( 'id', '' )
- if item_id:
- operation = kwd[ 'operation' ].lower()
- is_admin = trans.user_is_admin()
- if operation == "view_or_manage_repository":
- # The received id is a RepositoryMetadata id, so we have to get the repository id.
- repository_metadata = get_repository_metadata_by_id( trans, item_id )
- repository_id = trans.security.encode_id( repository_metadata.repository.id )
- repository = get_repository( trans, repository_id )
- kwd[ 'id' ] = repository_id
- kwd[ 'changeset_revision' ] = repository_metadata.changeset_revision
- if webapp == 'community' and ( is_admin or repository.user == trans.user ):
- a = 'manage_repository'
- else:
- a = 'view_repository'
- return trans.response.send_redirect( web.url_for( controller='repository',
- action=a,
- **kwd ) )
- if operation == "install":
- galaxy_url = trans.get_cookie( name='toolshedgalaxyurl' )
- encoded_repo_info_dict, includes_tools = self.__encode_repo_info_dict( trans, webapp, util.listify( item_id ) )
- url = '%sadmin_toolshed/install_repository?tool_shed_url=%s&webapp=%s&repo_info_dict=%s&includes_tools=%s' % \
- ( galaxy_url, url_for( '/', qualified=True ), webapp, encoded_repo_info_dict, str( includes_tools ) )
- return trans.response.send_redirect( url )
- else:
- # This can only occur when there is a multi-select grid with check boxes and an operation,
- # and the user clicked the operation button without checking any of the check boxes.
- return trans.show_error_message( "No items were selected." )
- tool_ids = [ item.lower() for item in util.listify( kwd.get( 'tool_id', '' ) ) ]
- tool_names = [ item.lower() for item in util.listify( kwd.get( 'tool_name', '' ) ) ]
- tool_versions = [ item.lower() for item in util.listify( kwd.get( 'tool_version', '' ) ) ]
- exact_matches = params.get( 'exact_matches', '' )
- exact_matches_checked = CheckboxField.is_checked( exact_matches )
- match_tuples = []
- ok = True
- if tool_ids or tool_names or tool_versions:
- ok, match_tuples = self.__search_repository_metadata( trans, exact_matches_checked, tool_ids=tool_ids, tool_names=tool_names, tool_versions=tool_versions )
- if ok:
- kwd[ 'match_tuples' ] = match_tuples
- # Render the list view
- if webapp == 'galaxy':
- # Our initial request originated from a Galaxy instance.
- global_actions = [ grids.GridAction( "Browse valid repositories",
- dict( controller='repository', action='browse_valid_repositories', webapp=webapp ) ),
- grids.GridAction( "Search for valid tools",
- dict( controller='repository', action='find_tools', webapp=webapp ) ),
- grids.GridAction( "Search for workflows",
- dict( controller='repository', action='find_workflows', webapp=webapp ) ) ]
- self.install_matched_repository_list_grid.global_actions = global_actions
- install_url_args = dict( controller='repository', action='find_tools', webapp=webapp )
- operations = [ grids.GridOperation( "Install", url_args=install_url_args, allow_multiple=True, async_compatible=False ) ]
- self.install_matched_repository_list_grid.operations = operations
- return self.install_matched_repository_list_grid( trans, **kwd )
- else:
- kwd[ 'message' ] = "tool id: <b>%s</b><br/>tool name: <b>%s</b><br/>tool version: <b>%s</b><br/>exact matches only: <b>%s</b>" % \
- ( self.__stringify( tool_ids ), self.__stringify( tool_names ), self.__stringify( tool_versions ), str( exact_matches_checked ) )
- self.matched_repository_list_grid.title = "Repositories with matching tools"
- return self.matched_repository_list_grid( trans, **kwd )
- else:
- message = "No search performed - each field must contain the same number of comma-separated items."
- status = "error"
- exact_matches_check_box = CheckboxField( 'exact_matches', checked=exact_matches_checked )
- return trans.fill_template( '/webapps/community/repository/find_tools.mako',
- webapp=webapp,
- tool_id=self.__stringify( tool_ids ),
- tool_name=self.__stringify( tool_names ),
- tool_version=self.__stringify( tool_versions ),
- exact_matches_check_box=exact_matches_check_box,
- message=message,
- status=status )
- def __search_repository_metadata( self, trans, exact_matches_checked, tool_ids='', tool_names='', tool_versions='', workflow_names='', all_workflows=False ):
- match_tuples = []
- ok = True
- for repository_metadata in trans.sa_session.query( model.RepositoryMetadata ):
- metadata = repository_metadata.metadata
- if tool_ids or tool_names or tool_versions:
- if 'tools' in metadata:
- tools = metadata[ 'tools' ]
- else:
- tools = []
- for tool_dict in tools:
- if tool_ids and not tool_names and not tool_versions:
- for tool_id in tool_ids:
- if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_id=tool_id ):
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- elif tool_names and not tool_ids and not tool_versions:
- for tool_name in tool_names:
- if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_name=tool_name ):
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- elif tool_versions and not tool_ids and not tool_names:
- for tool_version in tool_versions:
- if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_version=tool_version ):
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- elif tool_ids and tool_names and not tool_versions:
- if len( tool_ids ) == len( tool_names ):
- match_tuples = self.__search_ids_names( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_names )
- elif len( tool_ids ) == 1 or len( tool_names ) == 1:
- tool_ids, tool_names = self.__make_same_length( tool_ids, tool_names )
- match_tuples = self.__search_ids_names( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_names )
- else:
- ok = False
- elif tool_ids and tool_versions and not tool_names:
- if len( tool_ids ) == len( tool_versions ):
- match_tuples = self.__search_ids_versions( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_versions )
- elif len( tool_ids ) == 1 or len( tool_versions ) == 1:
- tool_ids, tool_versions = self.__make_same_length( tool_ids, tool_versions )
- match_tuples = self.__search_ids_versions( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_versions )
- else:
- ok = False
- elif tool_versions and tool_names and not tool_ids:
- if len( tool_versions ) == len( tool_names ):
- match_tuples = self.__search_names_versions( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_names, tool_versions )
- elif len( tool_versions ) == 1 or len( tool_names ) == 1:
- tool_versions, tool_names = self.__make_same_length( tool_versions, tool_names )
- match_tuples = self.__search_names_versions( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_names, tool_versions )
- else:
- ok = False
- elif tool_versions and tool_names and tool_ids:
- if len( tool_versions ) == len( tool_names ) and len( tool_names ) == len( tool_ids ):
- for i, tool_version in enumerate( tool_versions ):
- tool_name = tool_names[ i ]
- tool_id = tool_ids[ i ]
- if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_id=tool_id, tool_name=tool_name, tool_version=tool_version ):
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- else:
- ok = False
- elif workflow_names:
- if 'workflows' in metadata:
- # metadata[ 'workflows' ] is a list of tuples where each contained tuple is
- # [ <relative path to the .ga file in the repository>, <exported workflow dict> ]
- workflow_tups = metadata[ 'workflows' ]
- workflows = [ workflow_tup[1] for workflow_tup in workflow_tups ]
- else:
- workflows = []
- for workflow_dict in workflows:
- for workflow_name in workflow_names:
- if self.__in_workflow_dict( workflow_dict, exact_matches_checked, workflow_name ):
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- elif all_workflows and 'workflows' in metadata:
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- return ok, match_tuples
- def __in_workflow_dict( self, workflow_dict, exact_matches_checked, workflow_name ):
- workflow_dict_workflow_name = workflow_dict[ 'name' ].lower()
- return ( workflow_name == workflow_dict_workflow_name ) or \
- ( not exact_matches_checked and workflow_dict_workflow_name.find( workflow_name ) >= 0 )
+ repository = get_repository_by_name_and_owner( trans, name, owner )
+ for downloadable_revision in repository.downloadable_revisions:
+ if downloadable_revision.changeset_revision == changeset_revision:
+ break
+ metadata = downloadable_revision.metadata
+ tool_dependencies = metadata.get( 'tool_dependencies', '' )
+ if webapp == 'install_manager':
+ if tool_dependencies:
+ return tool_shed_encode( tool_dependencies )
+ return ''
+ # TODO: future handler where request comes from some Galaxy admin feature.
+ @web.expose
+ def get_tool_versions( self, trans, **kwd ):
+ """
+ For each valid /downloadable change set (up to the received changeset_revision) in the repository's change log, append the change
+ set's tool_versions dictionary to the list that will be returned.
+ """
+ name = kwd[ 'name' ]
+ owner = kwd[ 'owner' ]
+ changeset_revision = kwd[ 'changeset_revision' ]
+ repository = get_repository_by_name_and_owner( trans, name, owner )
+ repo_dir = repository.repo_path
+ repo = hg.repository( get_configured_ui(), repo_dir )
+ tool_version_dicts = []
+ for changeset in repo.changelog:
+ current_changeset_revision = str( repo.changectx( changeset ) )
+ repository_metadata = get_repository_metadata_by_changeset_revision( trans, trans.security.encode_id( repository.id ), current_changeset_revision )
+ if repository_metadata and repository_metadata.tool_versions:
+ tool_version_dicts.append( repository_metadata.tool_versions )
+ if current_changeset_revision == changeset_revision:
+ break
+ if tool_version_dicts:
+ return to_json_string( tool_version_dicts )
+ return ''
+ @web.expose
+ def help( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ return trans.fill_template( '/webapps/community/repository/help.mako', message=message, status=status, **kwd )
def __in_tool_dict( self, tool_dict, exact_matches_checked, tool_id=None, tool_name=None, tool_version=None ):
found = False
if tool_id and not tool_name and not tool_version:
@@ -716,83 +1189,19 @@
tool_dict_tool_name.find( tool_name ) >= 0 and \
tool_dict_tool_id.find( tool_id ) >= 0 )
return found
- def __stringify( self, list ):
- if list:
- return ','.join( list )
- return ''
- def __make_same_length( self, list1, list2 ):
- # If either list is 1 item, we'll append to it until its
- # length is the same as the other.
- if len( list1 ) == 1:
- for i in range( 1, len( list2 ) ):
- list1.append( list1[ 0 ] )
- elif len( list2 ) == 1:
- for i in range( 1, len( list1 ) ):
- list2.append( list2[ 0 ] )
- return list1, list2
- def __search_ids_names( self, tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_names ):
- for i, tool_id in enumerate( tool_ids ):
- tool_name = tool_names[ i ]
- if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_id=tool_id, tool_name=tool_name ):
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- return match_tuples
- def __search_ids_versions( self, tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_versions ):
- for i, tool_id in enumerate( tool_ids ):
- tool_version = tool_versions[ i ]
- if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_id=tool_id, tool_version=tool_version ):
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- return match_tuples
- def __search_names_versions( self, tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_names, tool_versions ):
- for i, tool_name in enumerate( tool_names ):
- tool_version = tool_versions[ i ]
- if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_name=tool_name, tool_version=tool_version ):
- match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
- return match_tuples
- def __encode_repo_info_dict( self, trans, webapp, repository_metadata_ids ):
- repo_info_dict = {}
- includes_tools = False
- for repository_metadata_id in repository_metadata_ids:
- repository_metadata = get_repository_metadata_by_id( trans, repository_metadata_id )
- if not includes_tools and 'tools' in repository_metadata.metadata:
- includes_tools = True
- repository = get_repository( trans, trans.security.encode_id( repository_metadata.repository_id ) )
- # Get the changelog rev for this changeset_revision.
- repo_dir = repository.repo_path
- repo = hg.repository( get_configured_ui(), repo_dir )
- changeset_revision = repository_metadata.changeset_revision
- ctx = get_changectx_for_changeset( repo, changeset_revision )
- repository_id = trans.security.encode_id( repository.id )
- repository_clone_url = generate_clone_url( trans, repository_id )
- repo_info_dict[ repository.name ] = ( repository.description, repository_clone_url, changeset_revision, str( ctx.rev() ) )
- return encode( repo_info_dict ), includes_tools
+ def __in_workflow_dict( self, workflow_dict, exact_matches_checked, workflow_name ):
+ workflow_dict_workflow_name = workflow_dict[ 'name' ].lower()
+ return ( workflow_name == workflow_dict_workflow_name ) or \
+ ( not exact_matches_checked and workflow_dict_workflow_name.find( workflow_name ) >= 0 )
@web.expose
- def preview_tools_in_changeset( self, trans, repository_id, **kwd ):
+ def index( self, trans, **kwd ):
params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
+ message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- webapp = params.get( 'webapp', 'community' )
- repository = get_repository( trans, repository_id )
- changeset_revision = util.restore_text( params.get( 'changeset_revision', repository.tip ) )
- repository_metadata = get_repository_metadata_by_changeset_revision( trans, repository_id, changeset_revision )
- if repository_metadata:
- repository_metadata_id = trans.security.encode_id( repository_metadata.id ),
- metadata = repository_metadata.metadata
- else:
- repository_metadata_id = None
- metadata = None
- revision_label = get_revision_label( trans, repository, changeset_revision )
- changeset_revision_select_field = build_changeset_revision_select_field( trans,
- repository,
- selected_value=changeset_revision,
- add_id_to_name=False )
- return trans.fill_template( '/webapps/community/repository/preview_tools_in_changeset.mako',
- repository=repository,
- repository_metadata_id=repository_metadata_id,
- changeset_revision=changeset_revision,
- revision_label=revision_label,
- changeset_revision_select_field=changeset_revision_select_field,
- metadata=metadata,
- webapp=webapp,
+ # See if there are any RepositoryMetadata records since menu items require them.
+ repository_metadata = trans.sa_session.query( model.RepositoryMetadata ).first()
+ return trans.fill_template( '/webapps/community/index.mako',
+ repository_metadata=repository_metadata,
message=message,
status=status )
@web.expose
@@ -838,633 +1247,117 @@
( galaxy_url, url_for( '/', qualified=True ), encoded_repo_info_dict, str( includes_tools ) )
return trans.response.send_redirect( url )
@web.expose
- def get_ctx_rev( self, trans, **kwd ):
- """Given a repository and changeset_revision, return the correct ctx.rev() value."""
- repository_name = kwd[ 'name' ]
- repository_owner = kwd[ 'owner' ]
- changeset_revision = kwd[ 'changeset_revision' ]
- repository = get_repository_by_name_and_owner( trans, repository_name, repository_owner )
+ def load_invalid_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'error' )
+ webapp = params.get( 'webapp', 'community' )
+ repository = get_repository( trans, repository_id )
repo_dir = repository.repo_path
repo = hg.repository( get_configured_ui(), repo_dir )
ctx = get_changectx_for_changeset( repo, changeset_revision )
- if ctx:
- return str( ctx.rev() )
- return ''
- @web.expose
- def get_readme( self, trans, **kwd ):
- """If the received changeset_revision includes a file named readme (case ignored), return it's contents."""
- repository_name = kwd[ 'name' ]
- repository_owner = kwd[ 'owner' ]
- changeset_revision = kwd[ 'changeset_revision' ]
- valid_filenames = [ r for r in README_FILES ]
- for r in README_FILES:
- valid_filenames.append( '%s.txt' % r )
- valid_filenames.append( '%s.txt' % repository_name )
- repository = get_repository_by_name_and_owner( trans, repository_name, repository_owner )
- repo_dir = repository.repo_path
- for root, dirs, files in os.walk( repo_dir ):
- for name in files:
- if name.lower() in valid_filenames:
- f = open( os.path.join( root, name ), 'r' )
- text = f.read()
- f.close()
- return str( text )
- return ''
- @web.expose
- def get_tool_versions( self, trans, **kwd ):
- """
- For each valid /downloadable change set (up to the received changeset_revision) in the repository's change log, append the change
- set's tool_versions dictionary to the list that will be returned.
- """
- name = kwd[ 'name' ]
- owner = kwd[ 'owner' ]
- changeset_revision = kwd[ 'changeset_revision' ]
- repository = get_repository_by_name_and_owner( trans, name, owner )
- repo_dir = repository.repo_path
- repo = hg.repository( get_configured_ui(), repo_dir )
- tool_version_dicts = []
- for changeset in repo.changelog:
- current_changeset_revision = str( repo.changectx( changeset ) )
- repository_metadata = get_repository_metadata_by_changeset_revision( trans, trans.security.encode_id( repository.id ), current_changeset_revision )
- if repository_metadata and repository_metadata.tool_versions:
- tool_version_dicts.append( repository_metadata.tool_versions )
- if current_changeset_revision == changeset_revision:
- break
- if tool_version_dicts:
- return to_json_string( tool_version_dicts )
- return ''
- @web.expose
- def check_for_updates( self, trans, **kwd ):
- # Handle a request from a local Galaxy instance. If the request originated with the
- # Galaxy instances' UpdateManager, the value of 'webapp' will be 'update_manager'.
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- # If the request originated with the UpdateManager, it will not include a galaxy_url.
- galaxy_url = kwd.get( 'galaxy_url', '' )
- name = params.get( 'name', None )
- owner = params.get( 'owner', None )
- changeset_revision = params.get( 'changeset_revision', None )
- webapp = params.get( 'webapp', 'community' )
- repository = get_repository_by_name_and_owner( trans, name, owner )
- repo_dir = repository.repo_path
- repo = hg.repository( get_configured_ui(), repo_dir )
- latest_ctx = get_changectx_for_changeset( repo, changeset_revision )
- from_update_manager = webapp == 'update_manager'
- if from_update_manager:
- update = 'true'
- no_update = 'false'
- else:
- # Start building up the url to redirect back to the calling Galaxy instance.
- url = '%sadmin_toolshed/update_to_changeset_revision?tool_shed_url=%s' % ( galaxy_url, url_for( '/', qualified=True ) )
- url += '&name=%s&owner=%s&changeset_revision=%s&latest_changeset_revision=' % \
- ( repository.name, repository.user.username, changeset_revision )
- if changeset_revision == repository.tip:
- # If changeset_revision is the repository tip, we know there are no additional updates for the tools.
- if from_update_manager:
- return no_update
- # Return the same value for changeset_revision and latest_changeset_revision.
- url += repository.tip
- else:
- repository_metadata = get_repository_metadata_by_changeset_revision( trans,
- trans.security.encode_id( repository.id ),
- changeset_revision )
- if repository_metadata:
- # If changeset_revision is in the repository_metadata table for this repository, then we know there are no additional updates
- # for the tools.
- if from_update_manager:
- return no_update
- else:
- # Return the same value for changeset_revision and latest_changeset_revision.
- url += changeset_revision
- else:
- # TODO: Re-engineer this to define the change set for update to be the one just before the next change set in the repository_metadata
- # table for this repository.
- # The changeset_revision column in the repository_metadata table has been updated with a new changeset_revision value since the
- # repository was cloned. Load each tool in the repository's changeset_revision to generate a list of tool guids, since guids
- # differentiate tools by id and version.
- ctx = get_changectx_for_changeset( repo, changeset_revision )
- if ctx is not None:
- work_dir = make_tmp_directory()
- tool_guids = []
- for filename in ctx:
- # Find all tool configs in this repository changeset_revision.
- if filename not in NOT_TOOL_CONFIGS and filename.endswith( '.xml' ):
- is_tool_config, valid, tool, error_message = load_tool_from_tmp_directory( trans,
+ invalid_message = ''
+ work_dir = make_tmp_directory()
+ for filename in ctx:
+ ctx_file_name = strip_path( filename )
+ if ctx_file_name == tool_config:
+ tool_config_path = get_named_tmpfile_from_ctx( ctx, filename, work_dir )
+ break
+ metadata_dict, invalid_files, deleted_sample_files = generate_metadata_for_changeset_revision( trans,
repo,
+ repository_id,
+ ctx,
+ changeset_revision,
repo_dir,
- ctx,
- filename,
- work_dir )
- if valid and tool is not None:
- tool_guids.append( generate_tool_guid( trans, repository, tool ) )
- tool_guids.sort()
- if tool_guids:
- # Compare our list of tool guids against those in each repository_metadata record for the repository to find the
- # repository_metadata record with the changeset_revision value we want to pass back to the caller.
- found = False
- for repository_metadata in get_repository_metadata_by_repository_id( trans, trans.security.encode_id( repository.id ) ):
- metadata = repository_metadata.metadata
- metadata_tool_guids = []
- for tool_dict in metadata[ 'tools' ]:
- metadata_tool_guids.append( tool_dict[ 'guid' ] )
- metadata_tool_guids.sort()
- if tool_guids == metadata_tool_guids:
- # We've found the repository_metadata record whose changeset_revision value has been updated.
- if from_update_manager:
- return update
- url += repository_metadata.changeset_revision
- # Get the ctx_rev for the discovered changeset_revision.
- latest_ctx = get_changectx_for_changeset( repo, repository_metadata.changeset_revision )
- found = True
- break
- if not found:
- # There must be a problem in the data, so we'll just send back the received changeset_revision.
- log.debug( "Possible data corruption - updated repository_metadata cannot be found for repository id %d." % repository.id )
- if from_update_manager:
- return no_update
- url += changeset_revision
- else:
- # There are no tools in the changeset_revision, so no tool updates are possible.
- if from_update_manager:
- return no_update
- url += changeset_revision
- try:
- shutil.rmtree( work_dir )
- except:
- pass
- url += '&latest_ctx_rev=%s' % str( latest_ctx.rev() )
- return trans.response.send_redirect( url )
- @web.expose
- def get_tool_dependencies( self, trans, **kwd ):
- # Handle a request from a local Galaxy instance. If the request originated with the Galaxy instances' InstallManager, the value of 'webapp'
- # will be 'install_manager'.
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- # If the request originated with the UpdateManager, it will not include a galaxy_url.
- galaxy_url = kwd.get( 'galaxy_url', '' )
- name = params.get( 'name', None )
- owner = params.get( 'owner', None )
- changeset_revision = params.get( 'changeset_revision', None )
- webapp = params.get( 'webapp', 'community' )
- repository = get_repository_by_name_and_owner( trans, name, owner )
- for downloadable_revision in repository.downloadable_revisions:
- if downloadable_revision.changeset_revision == changeset_revision:
- break
- metadata = downloadable_revision.metadata
- tool_dependencies = metadata.get( 'tool_dependencies', '' )
- if webapp == 'install_manager':
- if tool_dependencies:
- return tool_shed_encode( tool_dependencies )
- return ''
- # TODO: future handler where request comes from some Galaxy admin feature.
- @web.expose
- def browse_repositories( self, trans, **kwd ):
- # We add params to the keyword dict in this method in order to rename the param
- # with an "f-" prefix, simulating filtering by clicking a search link. We have
- # to take this approach because the "-" character is illegal in HTTP requests.
- if 'webapp' not in kwd:
- kwd[ 'webapp' ] = 'community'
- if 'operation' in kwd:
- operation = kwd['operation'].lower()
- if operation == "view_or_manage_repository":
- repository_id = kwd[ 'id' ]
- repository = get_repository( trans, repository_id )
- is_admin = trans.user_is_admin()
- if is_admin or repository.user == trans.user:
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='manage_repository',
- **kwd ) )
- else:
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='view_repository',
- **kwd ) )
- elif operation == "edit_repository":
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='edit_repository',
- **kwd ) )
- elif operation == "repositories_by_user":
- # Eliminate the current filters if any exist.
- for k, v in kwd.items():
- if k.startswith( 'f-' ):
- del kwd[ k ]
- if 'user_id' in kwd:
- user = get_user( trans, kwd[ 'user_id' ] )
- kwd[ 'f-email' ] = user.email
- del kwd[ 'user_id' ]
- else:
- # The received id is the repository id, so we need to get the id of the user
- # that uploaded the repository.
- repository_id = kwd.get( 'id', None )
- repository = get_repository( trans, repository_id )
- kwd[ 'f-email' ] = repository.user.email
- elif operation == "my_repositories":
- # Eliminate the current filters if any exist.
- for k, v in kwd.items():
- if k.startswith( 'f-' ):
- del kwd[ k ]
- kwd[ 'f-email' ] = trans.user.email
- elif operation == "repositories_by_category":
- # Eliminate the current filters if any exist.
- for k, v in kwd.items():
- if k.startswith( 'f-' ):
- del kwd[ k ]
- category_id = kwd.get( 'id', None )
- category = get_category( trans, category_id )
- kwd[ 'f-Category.name' ] = category.name
- elif operation == "receive email alerts":
- if trans.user:
- if kwd[ 'id' ]:
- kwd[ 'caller' ] = 'browse_repositories'
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='set_email_alerts',
- **kwd ) )
- else:
- kwd[ 'message' ] = 'You must be logged in to set email alerts.'
- kwd[ 'status' ] = 'error'
- del kwd[ 'operation' ]
- # The changeset_revision_select_field in the RepositoryListGrid performs a refresh_on_change
- # which sends in request parameters like changeset_revison_1, changeset_revision_2, etc. One
- # of the many select fields on the grid performed the refresh_on_change, so we loop through
- # all of the received values to see which value is not the repository tip. If we find it, we
- # know the refresh_on_change occurred, and we have the necessary repository id and change set
- # revision to pass on.
- for k, v in kwd.items():
- changset_revision_str = 'changeset_revision_'
- if k.startswith( changset_revision_str ):
- repository_id = trans.security.encode_id( int( k.lstrip( changset_revision_str ) ) )
- repository = get_repository( trans, repository_id )
- if repository.tip != v:
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='browse_repositories',
- operation='view_or_manage_repository',
- id=trans.security.encode_id( repository.id ),
- changeset_revision=v ) )
- # Render the list view
- return self.repository_list_grid( trans, **kwd )
- @web.expose
- def create_repository( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- categories = get_categories( trans )
- if not categories:
- message = 'No categories have been configured in this instance of the Galaxy Tool Shed. ' + \
- 'An administrator needs to create some via the Administrator control panel before creating repositories.',
- status = 'error'
+ updating_tip=changeset_revision==repository.tip )
+ for invalid_file_tup in invalid_files:
+ invalid_tool_config, invalid_msg = invalid_file_tup
+ invalid_tool_config_name = strip_path( invalid_tool_config )
+ if tool_config == invalid_tool_config_name:
+ invalid_message = invalid_msg
+ break
+ tool, error_message = load_tool_from_changeset_revision( trans, repository_id, changeset_revision, tool_config )
+ #if error_message:
+ # message += error_message
+ tool_state = self.__new_state( trans )
+ is_malicious = changeset_is_malicious( trans, repository_id, repository.tip )
+ try:
+ shutil.rmtree( work_dir )
+ except:
+ pass
+ try:
+ if invalid_message:
+ message = invalid_message
+ return trans.fill_template( "/webapps/community/repository/tool_form.mako",
+ repository=repository,
+ changeset_revision=changeset_revision,
+ tool=tool,
+ tool_state=tool_state,
+ is_malicious=is_malicious,
+ webapp=webapp,
+ message=message,
+ status='error' )
+ except Exception, e:
+ message = "This tool is invalid because: %s." % str( e )
+ if webapp == 'galaxy':
return trans.response.send_redirect( web.url_for( controller='repository',
- action='browse_repositories',
+ action='preview_tools_in_changeset',
+ repository_id=repository_id,
+ changeset_revision=changeset_revision,
message=message,
- status=status ) )
- name = util.restore_text( params.get( 'name', '' ) )
- description = util.restore_text( params.get( 'description', '' ) )
- long_description = util.restore_text( params.get( 'long_description', '' ) )
- category_ids = util.listify( params.get( 'category_id', '' ) )
- selected_categories = [ trans.security.decode_id( id ) for id in category_ids ]
- if params.get( 'create_repository_button', False ):
- error = False
- message = self.__validate_repository_name( name, trans.user )
- if message:
- error = True
- if not description:
- message = 'Enter a description.'
- error = True
- if not error:
- # Add the repository record to the db
- repository = trans.app.model.Repository( name=name,
- description=description,
- long_description=long_description,
- user_id=trans.user.id )
- # Flush to get the id
- trans.sa_session.add( repository )
- trans.sa_session.flush()
- # Determine the repository's repo_path on disk
- dir = os.path.join( trans.app.config.file_path, *directory_hash_id( repository.id ) )
- # Create directory if it does not exist
- if not os.path.exists( dir ):
- os.makedirs( dir )
- # Define repo name inside hashed directory
- repository_path = os.path.join( dir, "repo_%d" % repository.id )
- # Create local repository directory
- if not os.path.exists( repository_path ):
- os.makedirs( repository_path )
- # Create the local repository
- repo = hg.repository( get_configured_ui(), repository_path, create=True )
- # Add an entry in the hgweb.config file for the local repository
- # This enables calls to repository.repo_path
- self.__add_hgweb_config_entry( trans, repository, repository_path )
- # Create a .hg/hgrc file for the local repository
- self.__create_hgrc_file( repository )
- flush_needed = False
- if category_ids:
- # Create category associations
- for category_id in category_ids:
- category = trans.app.model.Category.get( trans.security.decode_id( category_id ) )
- rca = trans.app.model.RepositoryCategoryAssociation( repository, category )
- trans.sa_session.add( rca )
- flush_needed = True
- if flush_needed:
- trans.sa_session.flush()
- message = "Repository '%s' has been created." % repository.name
- trans.response.send_redirect( web.url_for( controller='repository',
- action='view_repository',
- webapp='community',
- message=message,
- id=trans.security.encode_id( repository.id ) ) )
- return trans.fill_template( '/webapps/community/repository/create_repository.mako',
- name=name,
- description=description,
- long_description=long_description,
- selected_categories=selected_categories,
- categories=categories,
- message=message,
- status=status )
- def __validate_repository_name( self, name, user ):
- # Repository names must be unique for each user, must be at least four characters
- # in length and must contain only lower-case letters, numbers, and the '_' character.
- if name in [ 'None', None, '' ]:
- return 'Enter the required repository name.'
- for repository in user.active_repositories:
- if repository.name == name:
- return "You already have a repository named '%s', so choose a different name." % name
- if len( name ) < 4:
- return "Repository names must be at least 4 characters in length."
- if len( name ) > 80:
- return "Repository names cannot be more than 80 characters in length."
- if not( VALID_REPOSITORYNAME_RE.match( name ) ):
- return "Repository names must contain only lower-case letters, numbers and underscore '_'."
- return ''
+ status='error' ) )
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='browse_repositories',
+ operation='view_or_manage_repository',
+ id=repository_id,
+ changeset_revision=changeset_revision,
+ message=message,
+ status='error' ) )
def __make_hgweb_config_copy( self, trans, hgweb_config ):
# Make a backup of the hgweb.config file
today = date.today()
backup_date = today.strftime( "%Y_%m_%d" )
hgweb_config_copy = '%s/hgweb.config_%s_backup' % ( trans.app.config.root, backup_date )
shutil.copy( os.path.abspath( hgweb_config ), os.path.abspath( hgweb_config_copy ) )
- def __add_hgweb_config_entry( self, trans, repository, repository_path ):
- # Add an entry in the hgweb.config file for a new repository. An entry looks something like:
- # repos/test/mira_assembler = database/community_files/000/repo_123.
- hgweb_config = "%s/hgweb.config" % trans.app.config.root
- if repository_path.startswith( './' ):
- repository_path = repository_path.replace( './', '', 1 )
- entry = "repos/%s/%s = %s" % ( repository.user.username, repository.name, repository_path )
- tmp_fd, tmp_fname = tempfile.mkstemp()
- if os.path.exists( hgweb_config ):
- # Make a backup of the hgweb.config file since we're going to be changing it.
- self.__make_hgweb_config_copy( trans, hgweb_config )
- new_hgweb_config = open( tmp_fname, 'wb' )
- for i, line in enumerate( open( hgweb_config ) ):
- new_hgweb_config.write( line )
- else:
- new_hgweb_config = open( tmp_fname, 'wb' )
- new_hgweb_config.write( '[paths]\n' )
- new_hgweb_config.write( "%s\n" % entry )
- new_hgweb_config.flush()
- shutil.move( tmp_fname, os.path.abspath( hgweb_config ) )
- def __change_hgweb_config_entry( self, trans, repository, old_repository_name, new_repository_name ):
- # Change an entry in the hgweb.config file for a repository. This only happens when
- # the owner changes the name of the repository. An entry looks something like:
- # repos/test/mira_assembler = database/community_files/000/repo_123.
- hgweb_config = "%s/hgweb.config" % trans.app.config.root
- # Make a backup of the hgweb.config file since we're going to be changing it.
- self.__make_hgweb_config_copy( trans, hgweb_config )
- repo_dir = repository.repo_path
- old_lhs = "repos/%s/%s" % ( repository.user.username, old_repository_name )
- new_entry = "repos/%s/%s = %s\n" % ( repository.user.username, new_repository_name, repo_dir )
- tmp_fd, tmp_fname = tempfile.mkstemp()
- new_hgweb_config = open( tmp_fname, 'wb' )
- for i, line in enumerate( open( hgweb_config ) ):
- if line.startswith( old_lhs ):
- new_hgweb_config.write( new_entry )
- else:
- new_hgweb_config.write( line )
- new_hgweb_config.flush()
- shutil.move( tmp_fname, os.path.abspath( hgweb_config ) )
- def __create_hgrc_file( self, repository ):
- # At this point, an entry for the repository is required to be in the hgweb.config file so we can call repository.repo_path.
- # Since we support both http and https, we set push_ssl to False to override the default (which is True) in the mercurial api.
- # The hg purge extension purges all files and directories not being tracked by mercurial in the current repository. It'll
- # remove unknown files and empty directories. This is not currently used because it is not supported in the mercurial API.
- repo = hg.repository( get_configured_ui(), path=repository.repo_path )
- fp = repo.opener( 'hgrc', 'wb' )
- fp.write( '[paths]\n' )
- fp.write( 'default = .\n' )
- fp.write( 'default-push = .\n' )
- fp.write( '[web]\n' )
- fp.write( 'allow_push = %s\n' % repository.user.username )
- fp.write( 'name = %s\n' % repository.name )
- fp.write( 'push_ssl = false\n' )
- fp.write( '[extensions]\n' )
- fp.write( 'hgext.purge=' )
- fp.close()
+ def __make_same_length( self, list1, list2 ):
+ # If either list is 1 item, we'll append to it until its length is the same as the other.
+ if len( list1 ) == 1:
+ for i in range( 1, len( list2 ) ):
+ list1.append( list1[ 0 ] )
+ elif len( list2 ) == 1:
+ for i in range( 1, len( list1 ) ):
+ list2.append( list2[ 0 ] )
+ return list1, list2
@web.expose
- def browse_repository( self, trans, id, **kwd ):
+ @web.require_login( "manage email alerts" )
+ def manage_email_alerts( self, trans, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- webapp = params.get( 'webapp', 'community' )
- commit_message = util.restore_text( params.get( 'commit_message', 'Deleted selected files' ) )
- repository = get_repository( trans, id )
- repo = hg.repository( get_configured_ui(), repository.repo_path )
- current_working_dir = os.getcwd()
- # Update repository files for browsing.
- update_repository( repo )
- is_malicious = changeset_is_malicious( trans, id, repository.tip )
- return trans.fill_template( '/webapps/community/repository/browse_repository.mako',
- repo=repo,
- repository=repository,
- commit_message=commit_message,
- is_malicious=is_malicious,
- webapp=webapp,
- message=message,
- status=status )
- @web.expose
- def contact_owner( self, trans, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- repository = get_repository( trans, id )
- if trans.user and trans.user.email:
- return trans.fill_template( "/webapps/community/repository/contact_owner.mako",
- repository=repository,
- message=message,
- status=status )
- else:
- # Do all we can to eliminate spam.
- return trans.show_error_message( "You must be logged in to contact the owner of a repository." )
- @web.expose
- def send_to_owner( self, trans, id, message='' ):
- repository = get_repository( trans, id )
- if not message:
- message = 'Enter a message'
- status = 'error'
- elif trans.user and trans.user.email:
- smtp_server = trans.app.config.smtp_server
- from_address = trans.app.config.email_from
- if smtp_server is None or from_address is None:
- return trans.show_error_message( "Mail is not configured for this Galaxy tool shed instance" )
- to_address = repository.user.email
- # Get the name of the server hosting the tool shed instance.
- host = trans.request.host
- # Build the email message
- body = string.Template( contact_owner_template ) \
- .safe_substitute( username=trans.user.username,
- repository_name=repository.name,
- email=trans.user.email,
- message=message,
- host=host )
- subject = "Regarding your tool shed repository named %s" % repository.name
- # Send it
- try:
- util.send_mail( from_address, to_address, subject, body, trans.app.config )
- message = "Your message has been sent"
- status = "done"
- except Exception, e:
- message = "An error occurred sending your message by email: %s" % str( e )
- status = "error"
- else:
- # Do all we can to eliminate spam.
- return trans.show_error_message( "You must be logged in to contact the owner of a repository." )
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='contact_owner',
- id=id,
- message=message,
- status=status ) )
- @web.expose
- def select_files_to_delete( self, trans, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- commit_message = util.restore_text( params.get( 'commit_message', 'Deleted selected files' ) )
- repository = get_repository( trans, id )
- repo_dir = repository.repo_path
- repo = hg.repository( get_configured_ui(), repo_dir )
- selected_files_to_delete = util.restore_text( params.get( 'selected_files_to_delete', '' ) )
- if params.get( 'select_files_to_delete_button', False ):
- if selected_files_to_delete:
- selected_files_to_delete = selected_files_to_delete.split( ',' )
- current_working_dir = os.getcwd()
- # Get the current repository tip.
- tip = repository.tip
- for selected_file in selected_files_to_delete:
- try:
- commands.remove( repo.ui, repo, selected_file, force=True )
- except Exception, e:
- log.debug( "Error removing files using the mercurial API, so trying a different approach, the error was: %s" % str( e ))
- relative_selected_file = selected_file.split( 'repo_%d' % repository.id )[1].lstrip( '/' )
- repo.dirstate.remove( relative_selected_file )
- repo.dirstate.write()
- absolute_selected_file = os.path.abspath( selected_file )
- if os.path.isdir( absolute_selected_file ):
- try:
- os.rmdir( absolute_selected_file )
- except OSError, e:
- # The directory is not empty
- pass
- elif os.path.isfile( absolute_selected_file ):
- os.remove( absolute_selected_file )
- dir = os.path.split( absolute_selected_file )[0]
- try:
- os.rmdir( dir )
- except OSError, e:
- # The directory is not empty
- pass
- # Commit the change set.
- if not commit_message:
- commit_message = 'Deleted selected files'
- commands.commit( repo.ui, repo, repo_dir, user=trans.user.username, message=commit_message )
- handle_email_alerts( trans, repository )
- # Update the repository files for browsing.
- update_repository( repo )
- # Get the new repository tip.
- repo = hg.repository( get_configured_ui(), repo_dir )
- if tip == repository.tip:
- message += 'No changes to repository. '
- kwd[ 'message' ] = message
-
- else:
- message += 'The selected files were deleted from the repository. '
- kwd[ 'message' ] = message
- set_repository_metadata_due_to_new_tip( trans, id, repository, **kwd )
+ new_repo_alert = params.get( 'new_repo_alert', '' )
+ new_repo_alert_checked = CheckboxField.is_checked( new_repo_alert )
+ user = trans.user
+ if params.get( 'new_repo_alert_button', False ):
+ user.new_repo_alert = new_repo_alert_checked
+ trans.sa_session.add( user )
+ trans.sa_session.flush()
+ if new_repo_alert_checked:
+ message = 'You will receive email alerts for all new valid tool shed repositories.'
else:
- message = "Select at least 1 file to delete from the repository before clicking <b>Delete selected files</b>."
- status = "error"
- is_malicious = changeset_is_malicious( trans, id, repository.tip )
- return trans.fill_template( '/webapps/community/repository/browse_repository.mako',
- repo=repo,
- repository=repository,
- commit_message=commit_message,
- is_malicious=is_malicious,
- message=message,
- status=status )
- @web.expose
- def view_repository( self, trans, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- repository = get_repository( trans, id )
- webapp = params.get( 'webapp', 'community' )
- repo = hg.repository( get_configured_ui(), repository.repo_path )
- avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model )
- changeset_revision = util.restore_text( params.get( 'changeset_revision', repository.tip ) )
- display_reviews = util.string_as_bool( params.get( 'display_reviews', False ) )
- alerts = params.get( 'alerts', '' )
- alerts_checked = CheckboxField.is_checked( alerts )
- if repository.email_alerts:
- email_alerts = from_json_string( repository.email_alerts )
- else:
- email_alerts = []
- user = trans.user
- if user and params.get( 'receive_email_alerts_button', False ):
- flush_needed = False
- if alerts_checked:
- if user.email not in email_alerts:
- email_alerts.append( user.email )
- repository.email_alerts = to_json_string( email_alerts )
- flush_needed = True
- else:
- if user.email in email_alerts:
- email_alerts.remove( user.email )
- repository.email_alerts = to_json_string( email_alerts )
- flush_needed = True
- if flush_needed:
- trans.sa_session.add( repository )
- trans.sa_session.flush()
- checked = alerts_checked or ( user and user.email in email_alerts )
- alerts_check_box = CheckboxField( 'alerts', checked=checked )
- changeset_revision_select_field = build_changeset_revision_select_field( trans,
- repository,
- selected_value=changeset_revision,
- add_id_to_name=False )
- revision_label = get_revision_label( trans, repository, changeset_revision )
- repository_metadata = get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
- if repository_metadata:
- repository_metadata_id = trans.security.encode_id( repository_metadata.id ),
- metadata = repository_metadata.metadata
- else:
- repository_metadata_id = None
- metadata = None
- is_malicious = changeset_is_malicious( trans, id, repository.tip )
- if is_malicious:
- if trans.app.security_agent.can_push( trans.user, repository ):
- message += malicious_error_can_push
- else:
- message += malicious_error
- status = 'error'
- return trans.fill_template( '/webapps/community/repository/view_repository.mako',
- repo=repo,
- repository=repository,
- repository_metadata_id=repository_metadata_id,
- metadata=metadata,
- avg_rating=avg_rating,
- display_reviews=display_reviews,
- num_ratings=num_ratings,
- alerts_check_box=alerts_check_box,
- changeset_revision=changeset_revision,
- changeset_revision_select_field=changeset_revision_select_field,
- revision_label=revision_label,
- is_malicious=is_malicious,
- webapp=webapp,
+ message = 'You will not receive any email alerts for new valid tool shed repositories.'
+ checked = new_repo_alert_checked or ( user and user.new_repo_alert )
+ new_repo_alert_check_box = CheckboxField( 'new_repo_alert', checked=checked )
+ email_alert_repositories = []
+ for repository in trans.sa_session.query( trans.model.Repository ) \
+ .filter( and_( trans.model.Repository.table.c.deleted == False,
+ trans.model.Repository.table.c.email_alerts != None ) ) \
+ .order_by( trans.model.Repository.table.c.name ):
+ if user.email in repository.email_alerts:
+ email_alert_repositories.append( repository )
+ return trans.fill_template( "/webapps/community/user/manage_email_alerts.mako",
+ webapp='community',
+ new_repo_alert_check_box=new_repo_alert_check_box,
+ email_alert_repositories=email_alert_repositories,
message=message,
status=status )
@web.expose
@@ -1621,6 +1514,429 @@
message=message,
status=status )
@web.expose
+ @web.require_login( "multi select email alerts" )
+ def multi_select_email_alerts( self, trans, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ if 'webapp' not in kwd:
+ kwd[ 'webapp' ] = 'community'
+ if 'operation' in kwd:
+ operation = kwd['operation'].lower()
+ if operation == "receive email alerts":
+ if trans.user:
+ if kwd[ 'id' ]:
+ kwd[ 'caller' ] = 'multi_select_email_alerts'
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='set_email_alerts',
+ **kwd ) )
+ else:
+ kwd[ 'message' ] = 'You must be logged in to set email alerts.'
+ kwd[ 'status' ] = 'error'
+ del kwd[ 'operation' ]
+ return self.email_alerts_repository_list_grid( trans, **kwd )
+ def __new_state( self, trans, all_pages=False ):
+ """
+ Create a new `DefaultToolState` for this tool. It will not be initialized
+ with default values for inputs.
+
+ Only inputs on the first page will be initialized unless `all_pages` is
+ True, in which case all inputs regardless of page are initialized.
+ """
+ state = DefaultToolState()
+ state.inputs = {}
+ return state
+ @web.json
+ def open_folder( self, trans, repository_id, key ):
+ # The tool shed includes a repository source file browser, which currently depends upon
+ # copies of the hg repository file store in the repo_path for browsing.
+ # Avoid caching
+ trans.response.headers['Pragma'] = 'no-cache'
+ trans.response.headers['Expires'] = '0'
+ repository = trans.sa_session.query( trans.model.Repository ).get( trans.security.decode_id( repository_id ) )
+ folder_path = key
+ try:
+ files_list = self.__get_files( trans, folder_path )
+ except OSError, e:
+ if str( e ).find( 'No such file or directory' ) >= 0:
+ # We have a repository with no contents.
+ return []
+ folder_contents = []
+ for filename in files_list:
+ is_folder = False
+ if filename and filename[-1] == os.sep:
+ is_folder = True
+ if filename:
+ full_path = os.path.join( folder_path, filename )
+ node = { "title": filename,
+ "isFolder": is_folder,
+ "isLazy": is_folder,
+ "tooltip": full_path,
+ "key": full_path }
+ folder_contents.append( node )
+ return folder_contents
+ @web.expose
+ def preview_tools_in_changeset( self, trans, repository_id, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ webapp = params.get( 'webapp', 'community' )
+ repository = get_repository( trans, repository_id )
+ changeset_revision = util.restore_text( params.get( 'changeset_revision', repository.tip ) )
+ repository_metadata = get_repository_metadata_by_changeset_revision( trans, repository_id, changeset_revision )
+ if repository_metadata:
+ repository_metadata_id = trans.security.encode_id( repository_metadata.id ),
+ metadata = repository_metadata.metadata
+ else:
+ repository_metadata_id = None
+ metadata = None
+ revision_label = get_revision_label( trans, repository, changeset_revision )
+ changeset_revision_select_field = build_changeset_revision_select_field( trans,
+ repository,
+ selected_value=changeset_revision,
+ add_id_to_name=False )
+ return trans.fill_template( '/webapps/community/repository/preview_tools_in_changeset.mako',
+ repository=repository,
+ repository_metadata_id=repository_metadata_id,
+ changeset_revision=changeset_revision,
+ revision_label=revision_label,
+ changeset_revision_select_field=changeset_revision_select_field,
+ metadata=metadata,
+ webapp=webapp,
+ message=message,
+ status=status )
+ @web.expose
+ @web.require_login( "rate repositories" )
+ def rate_repository( self, trans, **kwd ):
+ """ Rate a repository and return updated rating data. """
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='browse_repositories',
+ message='Select a repository to rate',
+ status='error' ) )
+ repository = get_repository( trans, id )
+ repo = hg.repository( get_configured_ui(), repository.repo_path )
+ if repository.user == trans.user:
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='browse_repositories',
+ message="You are not allowed to rate your own repository",
+ status='error' ) )
+ if params.get( 'rate_button', False ):
+ rating = int( params.get( 'rating', '0' ) )
+ comment = util.restore_text( params.get( 'comment', '' ) )
+ rating = self.rate_item( trans, trans.user, repository, rating, comment )
+ avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model )
+ display_reviews = util.string_as_bool( params.get( 'display_reviews', False ) )
+ rra = self.get_user_item_rating( trans.sa_session, trans.user, repository, webapp_model=trans.model )
+ is_malicious = changeset_is_malicious( trans, id, repository.tip )
+ return trans.fill_template( '/webapps/community/repository/rate_repository.mako',
+ repository=repository,
+ avg_rating=avg_rating,
+ display_reviews=display_reviews,
+ num_ratings=num_ratings,
+ rra=rra,
+ is_malicious=is_malicious,
+ message=message,
+ status=status )
+ @web.expose
+ def reset_all_metadata( self, trans, id, **kwd ):
+ error_message, status = reset_all_metadata_on_repository( trans, id, **kwd )
+ if error_message:
+ message = error_message
+ status = 'error'
+ else:
+ message = "All repository metadata has been reset."
+ status = 'done'
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='manage_repository',
+ id=id,
+ message=message,
+ status=status ) )
+ def __search_ids_names( self, tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_names ):
+ for i, tool_id in enumerate( tool_ids ):
+ tool_name = tool_names[ i ]
+ if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_id=tool_id, tool_name=tool_name ):
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ return match_tuples
+ def __search_ids_versions( self, tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_versions ):
+ for i, tool_id in enumerate( tool_ids ):
+ tool_version = tool_versions[ i ]
+ if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_id=tool_id, tool_version=tool_version ):
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ return match_tuples
+ def __search_names_versions( self, tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_names, tool_versions ):
+ for i, tool_name in enumerate( tool_names ):
+ tool_version = tool_versions[ i ]
+ if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_name=tool_name, tool_version=tool_version ):
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ return match_tuples
+ def __search_repository_metadata( self, trans, exact_matches_checked, tool_ids='', tool_names='', tool_versions='', workflow_names='', all_workflows=False ):
+ match_tuples = []
+ ok = True
+ for repository_metadata in trans.sa_session.query( model.RepositoryMetadata ):
+ metadata = repository_metadata.metadata
+ if tool_ids or tool_names or tool_versions:
+ if 'tools' in metadata:
+ tools = metadata[ 'tools' ]
+ else:
+ tools = []
+ for tool_dict in tools:
+ if tool_ids and not tool_names and not tool_versions:
+ for tool_id in tool_ids:
+ if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_id=tool_id ):
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ elif tool_names and not tool_ids and not tool_versions:
+ for tool_name in tool_names:
+ if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_name=tool_name ):
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ elif tool_versions and not tool_ids and not tool_names:
+ for tool_version in tool_versions:
+ if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_version=tool_version ):
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ elif tool_ids and tool_names and not tool_versions:
+ if len( tool_ids ) == len( tool_names ):
+ match_tuples = self.__search_ids_names( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_names )
+ elif len( tool_ids ) == 1 or len( tool_names ) == 1:
+ tool_ids, tool_names = self.__make_same_length( tool_ids, tool_names )
+ match_tuples = self.__search_ids_names( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_names )
+ else:
+ ok = False
+ elif tool_ids and tool_versions and not tool_names:
+ if len( tool_ids ) == len( tool_versions ):
+ match_tuples = self.__search_ids_versions( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_versions )
+ elif len( tool_ids ) == 1 or len( tool_versions ) == 1:
+ tool_ids, tool_versions = self.__make_same_length( tool_ids, tool_versions )
+ match_tuples = self.__search_ids_versions( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_ids, tool_versions )
+ else:
+ ok = False
+ elif tool_versions and tool_names and not tool_ids:
+ if len( tool_versions ) == len( tool_names ):
+ match_tuples = self.__search_names_versions( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_names, tool_versions )
+ elif len( tool_versions ) == 1 or len( tool_names ) == 1:
+ tool_versions, tool_names = self.__make_same_length( tool_versions, tool_names )
+ match_tuples = self.__search_names_versions( tool_dict, exact_matches_checked, match_tuples, repository_metadata, tool_names, tool_versions )
+ else:
+ ok = False
+ elif tool_versions and tool_names and tool_ids:
+ if len( tool_versions ) == len( tool_names ) and len( tool_names ) == len( tool_ids ):
+ for i, tool_version in enumerate( tool_versions ):
+ tool_name = tool_names[ i ]
+ tool_id = tool_ids[ i ]
+ if self.__in_tool_dict( tool_dict, exact_matches_checked, tool_id=tool_id, tool_name=tool_name, tool_version=tool_version ):
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ else:
+ ok = False
+ elif workflow_names:
+ if 'workflows' in metadata:
+ # metadata[ 'workflows' ] is a list of tuples where each contained tuple is
+ # [ <relative path to the .ga file in the repository>, <exported workflow dict> ]
+ workflow_tups = metadata[ 'workflows' ]
+ workflows = [ workflow_tup[1] for workflow_tup in workflow_tups ]
+ else:
+ workflows = []
+ for workflow_dict in workflows:
+ for workflow_name in workflow_names:
+ if self.__in_workflow_dict( workflow_dict, exact_matches_checked, workflow_name ):
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ elif all_workflows and 'workflows' in metadata:
+ match_tuples.append( ( repository_metadata.repository_id, repository_metadata.changeset_revision ) )
+ return ok, match_tuples
+ @web.expose
+ def select_files_to_delete( self, trans, id, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ commit_message = util.restore_text( params.get( 'commit_message', 'Deleted selected files' ) )
+ repository = get_repository( trans, id )
+ repo_dir = repository.repo_path
+ repo = hg.repository( get_configured_ui(), repo_dir )
+ selected_files_to_delete = util.restore_text( params.get( 'selected_files_to_delete', '' ) )
+ if params.get( 'select_files_to_delete_button', False ):
+ if selected_files_to_delete:
+ selected_files_to_delete = selected_files_to_delete.split( ',' )
+ current_working_dir = os.getcwd()
+ # Get the current repository tip.
+ tip = repository.tip
+ for selected_file in selected_files_to_delete:
+ try:
+ commands.remove( repo.ui, repo, selected_file, force=True )
+ except Exception, e:
+ log.debug( "Error removing files using the mercurial API, so trying a different approach, the error was: %s" % str( e ))
+ relative_selected_file = selected_file.split( 'repo_%d' % repository.id )[1].lstrip( '/' )
+ repo.dirstate.remove( relative_selected_file )
+ repo.dirstate.write()
+ absolute_selected_file = os.path.abspath( selected_file )
+ if os.path.isdir( absolute_selected_file ):
+ try:
+ os.rmdir( absolute_selected_file )
+ except OSError, e:
+ # The directory is not empty
+ pass
+ elif os.path.isfile( absolute_selected_file ):
+ os.remove( absolute_selected_file )
+ dir = os.path.split( absolute_selected_file )[0]
+ try:
+ os.rmdir( dir )
+ except OSError, e:
+ # The directory is not empty
+ pass
+ # Commit the change set.
+ if not commit_message:
+ commit_message = 'Deleted selected files'
+ commands.commit( repo.ui, repo, repo_dir, user=trans.user.username, message=commit_message )
+ handle_email_alerts( trans, repository )
+ # Update the repository files for browsing.
+ update_repository( repo )
+ # Get the new repository tip.
+ repo = hg.repository( get_configured_ui(), repo_dir )
+ if tip == repository.tip:
+ message += 'No changes to repository. '
+ kwd[ 'message' ] = message
+
+ else:
+ message += 'The selected files were deleted from the repository. '
+ kwd[ 'message' ] = message
+ set_repository_metadata_due_to_new_tip( trans, id, repository, **kwd )
+ else:
+ message = "Select at least 1 file to delete from the repository before clicking <b>Delete selected files</b>."
+ status = "error"
+ is_malicious = changeset_is_malicious( trans, id, repository.tip )
+ return trans.fill_template( '/webapps/community/repository/browse_repository.mako',
+ repo=repo,
+ repository=repository,
+ commit_message=commit_message,
+ is_malicious=is_malicious,
+ message=message,
+ status=status )
+ @web.expose
+ def send_to_owner( self, trans, id, message='' ):
+ repository = get_repository( trans, id )
+ if not message:
+ message = 'Enter a message'
+ status = 'error'
+ elif trans.user and trans.user.email:
+ smtp_server = trans.app.config.smtp_server
+ from_address = trans.app.config.email_from
+ if smtp_server is None or from_address is None:
+ return trans.show_error_message( "Mail is not configured for this Galaxy tool shed instance" )
+ to_address = repository.user.email
+ # Get the name of the server hosting the tool shed instance.
+ host = trans.request.host
+ # Build the email message
+ body = string.Template( contact_owner_template ) \
+ .safe_substitute( username=trans.user.username,
+ repository_name=repository.name,
+ email=trans.user.email,
+ message=message,
+ host=host )
+ subject = "Regarding your tool shed repository named %s" % repository.name
+ # Send it
+ try:
+ util.send_mail( from_address, to_address, subject, body, trans.app.config )
+ message = "Your message has been sent"
+ status = "done"
+ except Exception, e:
+ message = "An error occurred sending your message by email: %s" % str( e )
+ status = "error"
+ else:
+ # Do all we can to eliminate spam.
+ return trans.show_error_message( "You must be logged in to contact the owner of a repository." )
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='contact_owner',
+ id=id,
+ message=message,
+ status=status ) )
+ @web.expose
+ @web.require_login( "set email alerts" )
+ def set_email_alerts( self, trans, **kwd ):
+ # Set email alerts for selected repositories
+ # This method is called from multiple grids, so
+ # the caller must be passed.
+ caller = kwd[ 'caller' ]
+ user = trans.user
+ if user:
+ repository_ids = util.listify( kwd.get( 'id', '' ) )
+ total_alerts_added = 0
+ total_alerts_removed = 0
+ flush_needed = False
+ for repository_id in repository_ids:
+ repository = get_repository( trans, repository_id )
+ if repository.email_alerts:
+ email_alerts = from_json_string( repository.email_alerts )
+ else:
+ email_alerts = []
+ if user.email in email_alerts:
+ email_alerts.remove( user.email )
+ repository.email_alerts = to_json_string( email_alerts )
+ trans.sa_session.add( repository )
+ flush_needed = True
+ total_alerts_removed += 1
+ else:
+ email_alerts.append( user.email )
+ repository.email_alerts = to_json_string( email_alerts )
+ trans.sa_session.add( repository )
+ flush_needed = True
+ total_alerts_added += 1
+ if flush_needed:
+ trans.sa_session.flush()
+ message = 'Total alerts added: %d, total alerts removed: %d' % ( total_alerts_added, total_alerts_removed )
+ kwd[ 'message' ] = message
+ kwd[ 'status' ] = 'done'
+ del kwd[ 'operation' ]
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action=caller,
+ **kwd ) )
+ @web.expose
+ @web.require_login( "set repository metadata" )
+ def set_metadata( self, trans, id, ctx_str, **kwd ):
+ malicious = kwd.get( 'malicious', '' )
+ if kwd.get( 'malicious_button', False ):
+ repository_metadata = get_repository_metadata_by_changeset_revision( trans, id, ctx_str )
+ malicious_checked = CheckboxField.is_checked( malicious )
+ repository_metadata.malicious = malicious_checked
+ trans.sa_session.add( repository_metadata )
+ trans.sa_session.flush()
+ if malicious_checked:
+ message = "The repository tip has been defined as malicious."
+ else:
+ message = "The repository tip has been defined as <b>not</b> malicious."
+ status = 'done'
+ else:
+ # The set_metadata_button was clicked
+ message, status = set_repository_metadata( trans, id, ctx_str, **kwd )
+ if not message:
+ message = "Metadata for change set revision '%s' has been reset." % str( ctx_str )
+ return trans.response.send_redirect( web.url_for( controller='repository',
+ action='manage_repository',
+ id=id,
+ changeset_revision=ctx_str,
+ malicious=malicious,
+ message=message,
+ status=status ) )
+ def __stringify( self, list ):
+ if list:
+ return ','.join( list )
+ return ''
+ def __validate_repository_name( self, name, user ):
+ # Repository names must be unique for each user, must be at least four characters
+ # in length and must contain only lower-case letters, numbers, and the '_' character.
+ if name in [ 'None', None, '' ]:
+ return 'Enter the required repository name.'
+ for repository in user.active_repositories:
+ if repository.name == name:
+ return "You already have a repository named '%s', so choose a different name." % name
+ if len( name ) < 4:
+ return "Repository names must be at least 4 characters in length."
+ if len( name ) > 80:
+ return "Repository names cannot be more than 80 characters in length."
+ if not( VALID_REPOSITORYNAME_RE.match( name ) ):
+ return "Repository names must contain only lower-case letters, numbers and underscore '_'."
+ return ''
+ @web.expose
def view_changelog( self, trans, id, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
@@ -1694,291 +2010,76 @@
message=message,
status=status )
@web.expose
- @web.require_login( "rate repositories" )
- def rate_repository( self, trans, **kwd ):
- """ Rate a repository and return updated rating data. """
+ def view_repository( self, trans, id, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- id = params.get( 'id', None )
- if not id:
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='browse_repositories',
- message='Select a repository to rate',
- status='error' ) )
repository = get_repository( trans, id )
+ webapp = params.get( 'webapp', 'community' )
repo = hg.repository( get_configured_ui(), repository.repo_path )
- if repository.user == trans.user:
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='browse_repositories',
- message="You are not allowed to rate your own repository",
- status='error' ) )
- if params.get( 'rate_button', False ):
- rating = int( params.get( 'rating', '0' ) )
- comment = util.restore_text( params.get( 'comment', '' ) )
- rating = self.rate_item( trans, trans.user, repository, rating, comment )
avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model )
+ changeset_revision = util.restore_text( params.get( 'changeset_revision', repository.tip ) )
display_reviews = util.string_as_bool( params.get( 'display_reviews', False ) )
- rra = self.get_user_item_rating( trans.sa_session, trans.user, repository, webapp_model=trans.model )
+ alerts = params.get( 'alerts', '' )
+ alerts_checked = CheckboxField.is_checked( alerts )
+ if repository.email_alerts:
+ email_alerts = from_json_string( repository.email_alerts )
+ else:
+ email_alerts = []
+ user = trans.user
+ if user and params.get( 'receive_email_alerts_button', False ):
+ flush_needed = False
+ if alerts_checked:
+ if user.email not in email_alerts:
+ email_alerts.append( user.email )
+ repository.email_alerts = to_json_string( email_alerts )
+ flush_needed = True
+ else:
+ if user.email in email_alerts:
+ email_alerts.remove( user.email )
+ repository.email_alerts = to_json_string( email_alerts )
+ flush_needed = True
+ if flush_needed:
+ trans.sa_session.add( repository )
+ trans.sa_session.flush()
+ checked = alerts_checked or ( user and user.email in email_alerts )
+ alerts_check_box = CheckboxField( 'alerts', checked=checked )
+ changeset_revision_select_field = build_changeset_revision_select_field( trans,
+ repository,
+ selected_value=changeset_revision,
+ add_id_to_name=False )
+ revision_label = get_revision_label( trans, repository, changeset_revision )
+ repository_metadata = get_repository_metadata_by_changeset_revision( trans, id, changeset_revision )
+ if repository_metadata:
+ repository_metadata_id = trans.security.encode_id( repository_metadata.id ),
+ metadata = repository_metadata.metadata
+ else:
+ repository_metadata_id = None
+ metadata = None
is_malicious = changeset_is_malicious( trans, id, repository.tip )
- return trans.fill_template( '/webapps/community/repository/rate_repository.mako',
+ if is_malicious:
+ if trans.app.security_agent.can_push( trans.user, repository ):
+ message += malicious_error_can_push
+ else:
+ message += malicious_error
+ status = 'error'
+ return trans.fill_template( '/webapps/community/repository/view_repository.mako',
+ repo=repo,
repository=repository,
+ repository_metadata_id=repository_metadata_id,
+ metadata=metadata,
avg_rating=avg_rating,
display_reviews=display_reviews,
num_ratings=num_ratings,
- rra=rra,
+ alerts_check_box=alerts_check_box,
+ changeset_revision=changeset_revision,
+ changeset_revision_select_field=changeset_revision_select_field,
+ revision_label=revision_label,
is_malicious=is_malicious,
+ webapp=webapp,
message=message,
status=status )
@web.expose
- @web.require_login( "set email alerts" )
- def set_email_alerts( self, trans, **kwd ):
- # Set email alerts for selected repositories
- # This method is called from multiple grids, so
- # the caller must be passed.
- caller = kwd[ 'caller' ]
- user = trans.user
- if user:
- repository_ids = util.listify( kwd.get( 'id', '' ) )
- total_alerts_added = 0
- total_alerts_removed = 0
- flush_needed = False
- for repository_id in repository_ids:
- repository = get_repository( trans, repository_id )
- if repository.email_alerts:
- email_alerts = from_json_string( repository.email_alerts )
- else:
- email_alerts = []
- if user.email in email_alerts:
- email_alerts.remove( user.email )
- repository.email_alerts = to_json_string( email_alerts )
- trans.sa_session.add( repository )
- flush_needed = True
- total_alerts_removed += 1
- else:
- email_alerts.append( user.email )
- repository.email_alerts = to_json_string( email_alerts )
- trans.sa_session.add( repository )
- flush_needed = True
- total_alerts_added += 1
- if flush_needed:
- trans.sa_session.flush()
- message = 'Total alerts added: %d, total alerts removed: %d' % ( total_alerts_added, total_alerts_removed )
- kwd[ 'message' ] = message
- kwd[ 'status' ] = 'done'
- del kwd[ 'operation' ]
- return trans.response.send_redirect( web.url_for( controller='repository',
- action=caller,
- **kwd ) )
- @web.expose
- @web.require_login( "manage email alerts" )
- def manage_email_alerts( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- new_repo_alert = params.get( 'new_repo_alert', '' )
- new_repo_alert_checked = CheckboxField.is_checked( new_repo_alert )
- user = trans.user
- if params.get( 'new_repo_alert_button', False ):
- user.new_repo_alert = new_repo_alert_checked
- trans.sa_session.add( user )
- trans.sa_session.flush()
- if new_repo_alert_checked:
- message = 'You will receive email alerts for all new valid tool shed repositories.'
- else:
- message = 'You will not receive any email alerts for new valid tool shed repositories.'
- checked = new_repo_alert_checked or ( user and user.new_repo_alert )
- new_repo_alert_check_box = CheckboxField( 'new_repo_alert', checked=checked )
- email_alert_repositories = []
- for repository in trans.sa_session.query( trans.model.Repository ) \
- .filter( and_( trans.model.Repository.table.c.deleted == False,
- trans.model.Repository.table.c.email_alerts != None ) ) \
- .order_by( trans.model.Repository.table.c.name ):
- if user.email in repository.email_alerts:
- email_alert_repositories.append( repository )
- return trans.fill_template( "/webapps/community/user/manage_email_alerts.mako",
- webapp='community',
- new_repo_alert_check_box=new_repo_alert_check_box,
- email_alert_repositories=email_alert_repositories,
- message=message,
- status=status )
- @web.expose
- @web.require_login( "manage email alerts" )
- def multi_select_email_alerts( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- if 'webapp' not in kwd:
- kwd[ 'webapp' ] = 'community'
- if 'operation' in kwd:
- operation = kwd['operation'].lower()
- if operation == "receive email alerts":
- if trans.user:
- if kwd[ 'id' ]:
- kwd[ 'caller' ] = 'multi_select_email_alerts'
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='set_email_alerts',
- **kwd ) )
- else:
- kwd[ 'message' ] = 'You must be logged in to set email alerts.'
- kwd[ 'status' ] = 'error'
- del kwd[ 'operation' ]
- return self.email_alerts_repository_list_grid( trans, **kwd )
- @web.expose
- @web.require_login( "set repository metadata" )
- def set_metadata( self, trans, id, ctx_str, **kwd ):
- malicious = kwd.get( 'malicious', '' )
- if kwd.get( 'malicious_button', False ):
- repository_metadata = get_repository_metadata_by_changeset_revision( trans, id, ctx_str )
- malicious_checked = CheckboxField.is_checked( malicious )
- repository_metadata.malicious = malicious_checked
- trans.sa_session.add( repository_metadata )
- trans.sa_session.flush()
- if malicious_checked:
- message = "The repository tip has been defined as malicious."
- else:
- message = "The repository tip has been defined as <b>not</b> malicious."
- status = 'done'
- else:
- # The set_metadata_button was clicked
- message, status = set_repository_metadata( trans, id, ctx_str, **kwd )
- if not message:
- message = "Metadata for change set revision '%s' has been reset." % str( ctx_str )
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='manage_repository',
- id=id,
- changeset_revision=ctx_str,
- malicious=malicious,
- message=message,
- status=status ) )
- @web.expose
- def reset_all_metadata( self, trans, id, **kwd ):
- error_message, status = reset_all_metadata_on_repository( trans, id, **kwd )
- if error_message:
- message = error_message
- else:
- message = "All repository metadata has been reset."
- status = 'done'
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='manage_repository',
- id=id,
- message=message,
- status=status ) )
- @web.expose
- def display_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- webapp = params.get( 'webapp', 'community' )
- repository = get_repository( trans, repository_id )
- tool, message = load_tool_from_changeset_revision( trans, repository_id, changeset_revision, tool_config )
- tool_state = self.__new_state( trans )
- is_malicious = changeset_is_malicious( trans, repository_id, repository.tip )
- try:
- return trans.fill_template( "/webapps/community/repository/tool_form.mako",
- repository=repository,
- changeset_revision=changeset_revision,
- tool=tool,
- tool_state=tool_state,
- is_malicious=is_malicious,
- webapp=webapp,
- message=message,
- status=status )
- except Exception, e:
- message = "Error displaying tool, probably due to a problem in the tool config. The exception is: %s." % str( e )
- if webapp == 'galaxy':
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='preview_tools_in_changeset',
- repository_id=repository_id,
- changeset_revision=changeset_revision,
- message=message,
- status='error' ) )
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='browse_repositories',
- operation='view_or_manage_repository',
- id=repository_id,
- changeset_revision=changeset_revision,
- message=message,
- status='error' ) )
- @web.expose
- def load_invalid_tool( self, trans, repository_id, tool_config, changeset_revision, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'error' )
- webapp = params.get( 'webapp', 'community' )
- repository = get_repository( trans, repository_id )
- repo_dir = repository.repo_path
- repo = hg.repository( get_configured_ui(), repo_dir )
- ctx = get_changectx_for_changeset( repo, changeset_revision )
- invalid_message = ''
- work_dir = make_tmp_directory()
- for filename in ctx:
- ctx_file_name = strip_path( filename )
- if ctx_file_name == tool_config:
- tool_config_path = get_named_tmpfile_from_ctx( ctx, filename, work_dir )
- break
- metadata_dict, invalid_files = generate_metadata_for_changeset_revision( trans,
- repo,
- repository_id,
- ctx,
- changeset_revision,
- repo_dir,
- updating_tip=changeset_revision==repository.tip )
- for invalid_file_tup in invalid_files:
- invalid_tool_config, invalid_msg = invalid_file_tup
- invalid_tool_config_name = strip_path( invalid_tool_config )
- if tool_config == invalid_tool_config_name:
- invalid_message = invalid_msg
- break
- tool, error_message = load_tool_from_changeset_revision( trans, repository_id, changeset_revision, tool_config )
- if error_message:
- message += error_message
- tool_state = self.__new_state( trans )
- is_malicious = changeset_is_malicious( trans, repository_id, repository.tip )
- try:
- shutil.rmtree( work_dir )
- except:
- pass
- try:
- if invalid_message:
- message = invalid_message
- return trans.fill_template( "/webapps/community/repository/tool_form.mako",
- repository=repository,
- changeset_revision=changeset_revision,
- tool=tool,
- tool_state=tool_state,
- is_malicious=is_malicious,
- webapp=webapp,
- message=message,
- status='error' )
- except Exception, e:
- message = "This tool is invalid because: %s." % str( e )
- if webapp == 'galaxy':
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='preview_tools_in_changeset',
- repository_id=repository_id,
- changeset_revision=changeset_revision,
- message=message,
- status='error' ) )
- return trans.response.send_redirect( web.url_for( controller='repository',
- action='browse_repositories',
- operation='view_or_manage_repository',
- id=repository_id,
- changeset_revision=changeset_revision,
- message=message,
- status='error' ) )
- def __new_state( self, trans, all_pages=False ):
- """
- Create a new `DefaultToolState` for this tool. It will not be initialized
- with default values for inputs.
-
- Only inputs on the first page will be initialized unless `all_pages` is
- True, in which case all inputs regardless of page are initialized.
- """
- state = DefaultToolState()
- state.inputs = {}
- return state
- @web.expose
def view_tool_metadata( self, trans, repository_id, changeset_revision, tool_id, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
@@ -2015,106 +2116,3 @@
webapp=webapp,
message=message,
status=status )
- @web.expose
- def download( self, trans, repository_id, changeset_revision, file_type, **kwd ):
- # Download an archive of the repository files compressed as zip, gz or bz2.
- params = util.Params( kwd )
- repository = get_repository( trans, repository_id )
- # Allow hgweb to handle the download. This requires the tool shed
- # server account's .hgrc file to include the following setting:
- # [web]
- # allow_archive = bz2, gz, zip
- if file_type == 'zip':
- file_type_str = '%s.zip' % changeset_revision
- elif file_type == 'bz2':
- file_type_str = '%s.tar.bz2' % changeset_revision
- elif file_type == 'gz':
- file_type_str = '%s.tar.gz' % changeset_revision
- repository.times_downloaded += 1
- trans.sa_session.add( repository )
- trans.sa_session.flush()
- download_url = '/repos/%s/%s/archive/%s' % ( repository.user.username, repository.name, file_type_str )
- return trans.response.send_redirect( download_url )
- @web.json
- def open_folder( self, trans, repository_id, key ):
- # The tool shed includes a repository source file browser, which currently depends upon
- # copies of the hg repository file store in the repo_path for browsing.
- # Avoid caching
- trans.response.headers['Pragma'] = 'no-cache'
- trans.response.headers['Expires'] = '0'
- repository = trans.sa_session.query( trans.model.Repository ).get( trans.security.decode_id( repository_id ) )
- folder_path = key
- try:
- files_list = self.__get_files( trans, folder_path )
- except OSError, e:
- if str( e ).find( 'No such file or directory' ) >= 0:
- # We have a repository with no contents.
- return []
- folder_contents = []
- for filename in files_list:
- is_folder = False
- if filename and filename[-1] == os.sep:
- is_folder = True
- if filename:
- full_path = os.path.join( folder_path, filename )
- node = { "title": filename,
- "isFolder": is_folder,
- "isLazy": is_folder,
- "tooltip": full_path,
- "key": full_path }
- folder_contents.append( node )
- return folder_contents
- def __get_files( self, trans, folder_path ):
- contents = []
- for item in os.listdir( folder_path ):
- # Skip .hg directories
- if str( item ).startswith( '.hg' ):
- continue
- if os.path.isdir( os.path.join( folder_path, item ) ):
- # Append a '/' character so that our jquery dynatree will
- # function properly.
- item = '%s/' % item
- contents.append( item )
- if contents:
- contents.sort()
- return contents
- @web.json
- def get_file_contents( self, trans, file_path ):
- # Avoid caching
- trans.response.headers['Pragma'] = 'no-cache'
- trans.response.headers['Expires'] = '0'
- if is_gzip( file_path ):
- to_html = to_html_str( '\ngzip compressed file\n' )
- elif is_bz2( file_path ):
- to_html = to_html_str( '\nbz2 compressed file\n' )
- elif check_zip( file_path ):
- to_html = to_html_str( '\nzip compressed file\n' )
- elif check_binary( file_path ):
- to_html = to_html_str( '\nBinary file\n' )
- else:
- to_html = ''
- for i, line in enumerate( open( file_path ) ):
- to_html = '%s%s' % ( to_html, to_html_str( line ) )
- if len( to_html ) > MAX_CONTENT_SIZE:
- large_str = '\nFile contents truncated because file size is larger than maximum viewing size of %s\n' % util.nice_size( MAX_CONTENT_SIZE )
- to_html = '%s%s' % ( to_html, to_html_str( large_str ) )
- break
- return to_html
- @web.expose
- def help( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- return trans.fill_template( '/webapps/community/repository/help.mako', message=message, status=status, **kwd )
- def __build_allow_push_select_field( self, trans, current_push_list, selected_value='none' ):
- options = []
- for user in trans.sa_session.query( trans.model.User ):
- if user.username not in current_push_list:
- options.append( user )
- return build_select_field( trans,
- objs=options,
- label_attr='username',
- select_field_name='allow_push',
- selected_value=selected_value,
- refresh_on_change=False,
- multiple=True )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: inithello: Made genome tool backwards compatible with python 2.5.
by Bitbucket 04 Jun '12
by Bitbucket 04 Jun '12
04 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/29b8e39db109/
changeset: 29b8e39db109
user: inithello
date: 2012-06-04 19:18:58
summary: Made genome tool backwards compatible with python 2.5.
affected #: 9 files
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c lib/galaxy/jobs/deferred/genome_transfer.py
--- a/lib/galaxy/jobs/deferred/genome_transfer.py
+++ b/lib/galaxy/jobs/deferred/genome_transfer.py
@@ -1,6 +1,8 @@
"""
Module for managing genome transfer jobs.
"""
+from __future__ import with_statement
+
import logging, shutil, gzip, bz2, zipfile, tempfile, tarfile, sys
from galaxy import eggs
@@ -212,13 +214,17 @@
transfer.state = 'done'
self.sa_session.add( job )
self.sa_session.add( transfer )
+ if transfer.state == 'done':
+ if params[ 'indexes' ] is not None:
+ for indexer in params[ 'indexes' ]:
+ incoming = dict(indexer=indexer, dbkey=params[ 'dbkey' ], intname=params[ 'intname' ], path=transfer.path, user=params['user'] )
+ deferred = self.tool.execute( self, set_output_hid=False, history=None, incoming=incoming, transfer=transfer, deferred=job )
+ job.params[ 'indexjobs' ].append( deferred[0].id )
+ else:
+ job.state = self.app.model.DeferredJob.states.OK
+ self.sa_session.add( job )
+ return self.app.model.DeferredJob.states.OK
self.sa_session.flush()
- if transfer.state == 'done' and params[ 'indexes' ] is not None:
- for indexer in params[ 'indexes' ]:
- incoming = dict(indexer=indexer, dbkey=params[ 'dbkey' ], intname=params[ 'intname' ], path=transfer.path, user=params['user'] )
- deferred = self.tool.execute( self, set_output_hid=False, history=None, incoming=incoming, transfer=transfer, deferred=job )
- job.params[ 'indexjobs' ].append( deferred[0].id )
- return self.app.model.DeferredJob.states.OK
def _check_compress( self, filepath ):
retval = ''
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c lib/galaxy/jobs/deferred/liftover_transfer.py
--- a/lib/galaxy/jobs/deferred/liftover_transfer.py
+++ b/lib/galaxy/jobs/deferred/liftover_transfer.py
@@ -1,6 +1,8 @@
"""
Module for managing genome transfer jobs.
"""
+from __future__ import with_statement
+
import logging, shutil, gzip, tempfile, sys
from galaxy import eggs
@@ -31,11 +33,11 @@
self.app = app
self.sa_session = app.model.context.current
- def create_job( self, trans, url, dbkey, from_genome, to_genome, destfile ):
+ def create_job( self, trans, url, dbkey, from_genome, to_genome, destfile, parentjob ):
job = trans.app.transfer_manager.new( protocol='http', url=url )
params = dict( user=trans.user.id, transfer_job_id=job.id, protocol='http',
type='init_transfer', dbkey=dbkey, from_genome=from_genome,
- to_genome=to_genome, destfile=destfile )
+ to_genome=to_genome, destfile=destfile, parentjob=parentjob )
deferred = trans.app.model.DeferredJob( state = self.app.model.DeferredJob.states.NEW, plugin = 'LiftOverTransferPlugin', params = params )
self.sa_session.add( deferred )
self.sa_session.flush()
@@ -59,7 +61,19 @@
return self.job_states.WAIT
elif job.transfer_job.state == 'new':
assert job.params[ 'protocol' ] in [ 'http', 'ftp', 'https' ], 'Unknown protocol %s' % job.params[ 'protocol' ]
- self.app.transfer_manager.run( job.transfer_job )
+ ready = True
+ parent = self.sa_session.query( self.app.model.DeferredJob ).get( int( job.params[ 'parentjob' ] ) )
+ if not hasattr( parent, 'transfer_job' ):
+ parent.transfer_job = self.sa_session.query( self.app.model.TransferJob ).get( int( parent.params[ 'transfer_job_id' ] ) )
+ if parent.transfer_job.state not in [ 'ok', 'error', 'done' ]:
+ ready = False
+ for lo_job in parent.params[ 'liftover' ]:
+ liftoverjob = self.sa_session.query( self.app.model.TransferJob ).get( int( lo_job ) )
+ if liftoverjob:
+ if liftoverjob.state not in [ 'ok', 'error', 'new', 'done' ]:
+ ready = False
+ if ready:
+ self.app.transfer_manager.run( job.transfer_job )
self.sa_session.add( job.transfer_job )
self.sa_session.flush()
return self.job_states.WAIT
@@ -116,6 +130,15 @@
job.params[ 'type' ] = 'finish_transfer'
transfer.path = os.path.abspath(destfilepath)
transfer.state = 'done'
+ parentjob = self.sa_session.query( self.app.model.DeferredJob ).get( int( job.params[ 'parentjob' ] ) )
+ finished = True
+ for i in parentjob.params[ 'liftover' ]:
+ sibling = self.sa_session.query( self.app.model.DeferredJob ).get( int( i ) )
+ if sibling.state not in [ 'done', 'ok', 'error' ]:
+ finished = False
+ if finished:
+ parentjob.state = self.app.model.DeferredJob.states.OK
+ self.sa_session.add( parentjob )
self.sa_session.add( job )
self.sa_session.add( transfer )
self.sa_session.flush()
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c lib/galaxy/tools/genome_index/__init__.py
--- a/lib/galaxy/tools/genome_index/__init__.py
+++ b/lib/galaxy/tools/genome_index/__init__.py
@@ -1,4 +1,7 @@
+from __future__ import with_statement
+
import os, shutil, logging, tempfile, json, tarfile
+
from galaxy import model, util
from galaxy.web.framework.helpers import to_unicode
from galaxy.model.item_attrs import UsesAnnotations
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c lib/galaxy/tools/genome_index/index_genome.py
--- a/lib/galaxy/tools/genome_index/index_genome.py
+++ b/lib/galaxy/tools/genome_index/index_genome.py
@@ -5,6 +5,8 @@
usage: %prog history_attrs dataset_attrs job_attrs out_file
-G, --gzip: gzip archive file
"""
+from __future__ import with_statement
+
import optparse, sys, os, tempfile, time, subprocess, shlex, json, tarfile, shutil
class ManagedIndexer():
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c lib/galaxy/web/controllers/data_admin.py
--- a/lib/galaxy/web/controllers/data_admin.py
+++ b/lib/galaxy/web/controllers/data_admin.py
@@ -30,6 +30,8 @@
@web.expose
@web.require_admin
def manage_data( self, trans, **kwd ):
+ if trans.app.config.get_bool( 'enable_beta_job_managers', False ) == False:
+ return trans.fill_template( '/admin/data_admin/betajob.mako' )
dbkeys = trans.db_builds
return trans.fill_template( '/admin/data_admin/data_form.mako', dbkeys=dbkeys )
@@ -87,6 +89,7 @@
newlift = None
pass
ftp.retrlines('NLST /goldenPath/%s/bigZips/' % dbkey, checker.append)
+ ftp.quit()
for filename in [ dbkey, 'chromFa' ]:
for extension in [ '.tar.gz', '.tar.bz2', '.zip', '.fa.gz', '.fa.bz2' ]:
testfile = '/goldenPath/%s/bigZips/%s%s' % ( dbkey, filename, extension )
@@ -129,7 +132,7 @@
from_genome = chain[1]
to_genome = chain[2]
destfile = liftover_url.split('/')[-1].replace('.gz', '')
- chainjob.append( trans.app.job_manager.deferred_job_queue.plugins['LiftOverTransferPlugin'].create_job( trans, liftover_url, dbkey, from_genome, to_genome, destfile ) )
+ chainjob.append( trans.app.job_manager.deferred_job_queue.plugins['LiftOverTransferPlugin'].create_job( trans, liftover_url, dbkey, from_genome, to_genome, destfile, jobid ) )
job = trans.app.job_manager.deferred_job_queue.plugins['GenomeTransferPlugin'].get_job_status( jobid )
job.params['liftover'] = chainjob
trans.app.model.context.current.add( job )
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c templates/admin/data_admin/betajob.mako
--- /dev/null
+++ b/templates/admin/data_admin/betajob.mako
@@ -0,0 +1,35 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+<%namespace file="/library/common/common.mako" import="common_javascripts" />
+
+<%!
+ def inherit(context):
+ if context.get('use_panels'):
+ return '/webapps/galaxy/base_panels.mako'
+ else:
+ return '/base.mako'
+%>
+<%inherit file="${inherit(context)}"/>
+
+<%def name="init()">
+<%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.message_box_visible=False
+ self.active_view="user"
+ self.overlay_visible=False
+ self.has_accessible_datasets = False
+%>
+</%def>
+<%def name="stylesheets()">
+ ${parent.stylesheets()}
+ ${h.css( "autocomplete_tagging" )}
+</%def>
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js("jquery.autocomplete", "autocomplete_tagging" )}
+</%def>
+##
+## Override methods from base.mako and base_panels.mako
+##
+<p class="panel-error-message">This feature requires that enable_beta_job_managers be set to True in your Galaxy configuration.</p>
\ No newline at end of file
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c templates/admin/data_admin/data_form.mako
--- a/templates/admin/data_admin/data_form.mako
+++ b/templates/admin/data_admin/data_form.mako
@@ -52,7 +52,6 @@
<div class="form-row"><label for="source">Data Source</label><select id="datasource" name="source" label="Data Source">
- <option value="local">localhost</option><option value="UCSC">UCSC</option><option value="Broad">Broad Institute</option><option value="NCBI">NCBI</option>
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c templates/admin/data_admin/download_status.mako
--- a/templates/admin/data_admin/download_status.mako
+++ b/templates/admin/data_admin/download_status.mako
@@ -39,6 +39,7 @@
<a href="${h.url_for( controller='data_admin', action='manage_data' )}">Return to the download form</a><script type="text/javascript">
jobs = ${jsonjobs}
+ finalstates = new Array('done', 'error', 'ok');
function makeHTML(jobrow) {
jc = 'jobrow ' + jobrow['style'];
@@ -50,18 +51,6 @@
'<td style="padding: 0px 5px;">' + jobrow['status'] + '</td></tr>';
}
- function getNewHtml(jobid, jobtype, elm) {
- $.get('${h.url_for( controller='data_admin', action='job_status' )}', { jobid: jobid, jobtype: jobtype }, function(data) {
- jsondata = JSON.parse(data);
- status = jsondata['status'];
- htmldata = makeHTML(jsondata);
- idval = '#' + jobtype + '-job-' + jobid;
- if (htmldata != undefined) {
- $(elm).replaceWith(htmldata);
- }
- });
- }
-
function checkJobs() {
var alldone = true;
var mainjob;
@@ -70,9 +59,8 @@
if ($(this).attr('data-jobtype') == 'deferred') {
mainjob = $(this).attr('data-jobid');
}
- if (status != 'done' && status != 'error' && status != 'ok') {
+ if ($.inArray(status, finalstates) == -1) {
alldone = false;
- getNewHtml($(this).attr('data-jobid'), $(this).attr('data-jobtype'), $(this));
}
});
if (!alldone) {
@@ -88,10 +76,12 @@
$.get('${h.url_for( controller='data_admin', action='get_jobs' )}', { jobid: mainjob }, function(data) {
jsondata = JSON.parse(data);
for (i in jsondata) {
+ currentjob = jsondata[i]
if (jobs[i] == undefined) {
$('#jobStatus').append(makeHTML(jsondata[i]));
jobs.push(jsondata[i]);
}
+ $('#' + currentjob['type'] + '-job-' + currentjob['jobid']).replaceWith(makeHTML(currentjob));
}
});
}
diff -r e276278db3b01601b0432294fa0eef297a0e7937 -r 29b8e39db1094cb5a5586c4f7b404428641d518c templates/webapps/galaxy/admin/index.mako
--- a/templates/webapps/galaxy/admin/index.mako
+++ b/templates/webapps/galaxy/admin/index.mako
@@ -57,7 +57,9 @@
<div class="toolSectionBg"><div class="toolTitle"><a href="${h.url_for( controller='admin', action='quotas', webapp=webapp )}" target="galaxy_main">Manage quotas</a></div><div class="toolTitle"><a href="${h.url_for( controller='library_admin', action='browse_libraries' )}" target="galaxy_main">Manage data libraries</a></div>
- <div class="toolTitle"><a href="${h.url_for( controller='data_admin', action='manage_data' )}" target="galaxy_main">Manage local data</a></div>
+ %if trans.app.config.enable_beta_job_managers:
+ <div class="toolTitle"><a href="${h.url_for( controller='data_admin', action='manage_data' )}" target="galaxy_main">Manage local data</a></div>
+ %endif
</div></div><div class="toolSectionPad"></div>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: jgoecks: Place all JavaScript visualization code under static/scripts/viz directory.
by Bitbucket 04 Jun '12
by Bitbucket 04 Jun '12
04 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/e276278db3b0/
changeset: e276278db3b0
user: jgoecks
date: 2012-06-04 18:26:52
summary: Place all JavaScript visualization code under static/scripts/viz directory.
affected #: 14 files
Diff too large to display.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: jgoecks: Clarify inputs for forward, reverse reads in Tophat2 wrapper.
by Bitbucket 04 Jun '12
by Bitbucket 04 Jun '12
04 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/4b799028749a/
changeset: 4b799028749a
user: jgoecks
date: 2012-06-04 15:29:51
summary: Clarify inputs for forward, reverse reads in Tophat2 wrapper.
affected #: 1 file
diff -r 82f11c6b5da6339115dd4499ad317eb341835fa8 -r 4b799028749a3e8c0138a8a507c79f78e6edd349 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -124,8 +124,8 @@
<param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Nucleotide-space: Must have Sanger-scaled quality values with ASCII offset 33"/></when><when value="paired">
- <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file" help="Nucleotide-space: Must have Sanger-scaled quality values with ASCII offset 33" />
- <param format="fastqsanger" name="input2" type="data" label="RNA-Seq FASTQ file" help="Nucleotide-space: Must have Sanger-scaled quality values with ASCII offset 33" />
+ <param format="fastqsanger" name="input1" type="data" label="RNA-Seq FASTQ file, forward reads" help="Nucleotide-space: Must have Sanger-scaled quality values with ASCII offset 33" />
+ <param format="fastqsanger" name="input2" type="data" label="RNA-Seq FASTQ file, reverse reads" help="Nucleotide-space: Must have Sanger-scaled quality values with ASCII offset 33" /><param name="mate_inner_distance" type="integer" value="300" label="Mean Inner Distance between Mate Pairs" /><param name="mate_std_dev" type="integer" value="20" label="Std. Dev for Distance between Mate Pairs" help="The standard deviation for the distribution on inner distances between mate pairs."/><!-- Discordant pairs. -->
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: jgoecks: Add entry for Tophat2 indices to tool data table.
by Bitbucket 04 Jun '12
by Bitbucket 04 Jun '12
04 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/82f11c6b5da6/
changeset: 82f11c6b5da6
user: jgoecks
date: 2012-06-04 15:07:19
summary: Add entry for Tophat2 indices to tool data table.
affected #: 1 file
diff -r 44cc39cf2bd41154aa1573e253b87f77ec97ae99 -r 82f11c6b5da6339115dd4499ad317eb341835fa8 tool_data_table_conf.xml.sample
--- a/tool_data_table_conf.xml.sample
+++ b/tool_data_table_conf.xml.sample
@@ -105,6 +105,11 @@
<columns>value, dbkey, name, path</columns><file path="tool-data/bowtie_indices_color.loc" /></table>
+ <!-- Locations of indexes in the Bowtie2 mapper format for TopHat2 to use -->
+ <table name="tophat2_indexes" comment_char="#">
+ <columns>value, dbkey, name, path</columns>
+ <file path="tool-data/bowtie2_indices.loc" />
+ </table><!-- Locations of configurations in the CCAT peak/region caller format --><table name="ccat_configurations" comment_char="#"><columns>value, name, path</columns>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/97338d705f3a/
changeset: 97338d705f3a
user: jgoecks
date: 2012-06-04 05:07:54
summary: Fix import to be compatible with previous changeset.
affected #: 1 file
diff -r 4051e35d4cfd4f0f692b392553d9d99c9f388724 -r 97338d705f3acb4baa4e2f4d460c74fd8dde5761 lib/galaxy/web/controllers/tracks.py
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -15,7 +15,7 @@
from galaxy.datatypes.interval import Gff, Bed
from galaxy.model import NoConverterException, ConverterDependencyException
from galaxy.visualization.tracks.data_providers import *
-from galaxy.visualization.tracks.genomes import decode_dbkey, Genomes
+from galaxy.visualization.genomes import decode_dbkey, Genomes
from galaxy.visualization.tracks.visual_analytics import get_tool_def, get_dataset_job
# Message strings returned to browser
https://bitbucket.org/galaxy/galaxy-central/changeset/44cc39cf2bd4/
changeset: 44cc39cf2bd4
user: jgoecks
date: 2012-06-04 05:09:08
summary: Merge
affected #: 1 file
diff -r 97338d705f3acb4baa4e2f4d460c74fd8dde5761 -r 44cc39cf2bd41154aa1573e253b87f77ec97ae99 lib/galaxy/visualization/genomes.py
--- a/lib/galaxy/visualization/genomes.py
+++ b/lib/galaxy/visualization/genomes.py
@@ -32,47 +32,12 @@
self.key = key
self.len_file = len_file
self.twobit_file = twobit_file
-
-
-class Genomes( object ):
- """
- Provides information about available genome data and methods for manipulating that data.
- """
-
- def __init__( self, app ):
- # Create list of known genomes from len files.
- self.genomes = {}
- len_files = glob.glob( os.path.join( app.config.len_file_path, "*.len" ) )
- for f in len_files:
- key = os.path.split( f )[1].split( ".len" )[0]
- self.genomes[ key ] = Genome( key, len_file=f )
-
- # Add genome data (twobit files) to genomes.
- for line in open( os.path.join( app.config.tool_data_path, "twobit.loc" ) ):
- if line.startswith("#"): continue
- val = line.split()
- if len( val ) == 2:
- key, path = val
- if key in self.genomes:
- self.genomes[ key ].twobit_file = path
-
- def get_dbkeys_with_chrom_info( self, trans ):
- """ Returns all valid dbkeys that have chromosome information. """
-
- # All user keys have a len file.
- user_keys = {}
- user = trans.get_user()
- if 'dbkeys' in user.preferences:
- user_keys = from_json_string( user.preferences['dbkeys'] )
-
- dbkeys = [ (v, k) for k, v in trans.db_builds if ( ( k in self.genomes and self.genomes[ k ].len_file ) or k in user_keys ) ]
- return dbkeys
-
- def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ):
+
+ def to_dict( self, num=None, chrom=None, low=None ):
"""
- Returns a naturally sorted list of chroms/contigs for a given dbkey.
- Use either chrom or low to specify the starting chrom in the return list.
+ Returns representation of self as a dictionary.
"""
+
def check_int(s):
if s.isdigit():
return int(s)
@@ -97,47 +62,13 @@
else:
low = 0
- # If there is no dbkey owner, default to current user.
- dbkey_owner, dbkey = decode_dbkey( dbkey )
- if dbkey_owner:
- dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first()
- else:
- dbkey_user = trans.user
-
- #
- # Get len file.
- #
-
- # Look first in user's custom builds.
- len_file = None
- len_ds = None
- user_keys = {}
- if dbkey_user and 'dbkeys' in dbkey_user.preferences:
- user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
- if dbkey in user_keys:
- dbkey_attributes = user_keys[ dbkey ]
- if 'fasta' in dbkey_attributes:
- build_fasta = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] )
- len_file = build_fasta.get_converted_dataset( trans, 'len' ).file_name
- # Backwards compatibility: look for len file directly.
- elif 'len' in dbkey_attributes:
- len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name
-
- # Look in system builds.
- if not len_file:
- len_ds = trans.db_dataset_for( dbkey )
- if not len_ds:
- len_file = self.genomes[ dbkey ].len_file
- else:
- len_file = len_ds.file_name
-
#
# Get chroms data:
# (a) chrom name, len;
# (b) whether there are previous, next chroms;
# (c) index of start chrom.
#
- len_file_enumerate = enumerate( open( len_file ) )
+ len_file_enumerate = enumerate( open( self.len_file ) )
chroms = {}
prev_chroms = False
start_index = 0
@@ -169,11 +100,6 @@
start_index = low
# Read chrom data from len file.
- # TODO: this may be too slow for very large numbers of chroms/contigs,
- # but try it out for now.
- if not os.path.exists( len_file ):
- return None
-
for line_num, line in len_file_enumerate:
if line_num < low:
continue
@@ -197,9 +123,99 @@
to_sort = [{ 'chrom': chrom, 'len': length } for chrom, length in chroms.iteritems()]
to_sort.sort(lambda a,b: cmp( split_by_number(a['chrom']), split_by_number(b['chrom']) ))
- return { 'reference': self.has_reference_data( trans, dbkey, dbkey_user ), 'chrom_info': to_sort,
- 'prev_chroms' : prev_chroms, 'next_chroms' : next_chroms, 'start_index' : start_index }
+ return {
+ 'id': self.key,
+ 'reference': self.twobit_file is not None,
+ 'chrom_info': to_sort,
+ 'prev_chroms' : prev_chroms,
+ 'next_chroms' : next_chroms,
+ 'start_index' : start_index
+ }
+
+class Genomes( object ):
+ """
+ Provides information about available genome data and methods for manipulating that data.
+ """
+
+ def __init__( self, app ):
+ # Create list of known genomes from len files.
+ self.genomes = {}
+ len_files = glob.glob( os.path.join( app.config.len_file_path, "*.len" ) )
+ for f in len_files:
+ key = os.path.split( f )[1].split( ".len" )[0]
+ self.genomes[ key ] = Genome( key, len_file=f )
+
+ # Add genome data (twobit files) to genomes.
+ for line in open( os.path.join( app.config.tool_data_path, "twobit.loc" ) ):
+ if line.startswith("#"): continue
+ val = line.split()
+ if len( val ) == 2:
+ key, path = val
+ if key in self.genomes:
+ self.genomes[ key ].twobit_file = path
+
+ def get_build( self, dbkey ):
+ """ Returns build for the given key. """
+ rval = None
+ if dbkey in self.genomes:
+ rval = self.genomes[ dbkey ]
+ return rval
+
+ def get_dbkeys_with_chrom_info( self, trans ):
+ """ Returns all valid dbkeys that have chromosome information. """
+ # All user keys have a len file.
+ user_keys = {}
+ user = trans.get_user()
+ if 'dbkeys' in user.preferences:
+ user_keys = from_json_string( user.preferences['dbkeys'] )
+
+ dbkeys = [ (v, k) for k, v in trans.db_builds if ( ( k in self.genomes and self.genomes[ k ].len_file ) or k in user_keys ) ]
+ return dbkeys
+
+ def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ):
+ """
+ Returns a naturally sorted list of chroms/contigs for a given dbkey.
+ Use either chrom or low to specify the starting chrom in the return list.
+ """
+
+ # If there is no dbkey owner, default to current user.
+ dbkey_owner, dbkey = decode_dbkey( dbkey )
+ if dbkey_owner:
+ dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first()
+ else:
+ dbkey_user = trans.user
+
+ #
+ # Get/create genome object.
+ #
+ genome = None
+
+ # Look first in user's custom builds.
+ if dbkey_user and 'dbkeys' in dbkey_user.preferences:
+ user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
+ if dbkey in user_keys:
+ dbkey_attributes = user_keys[ dbkey ]
+ if 'fasta' in dbkey_attributes:
+ build_fasta = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] )
+ len_file = build_fasta.get_converted_dataset( trans, 'len' ).file_name
+ # Backwards compatibility: look for len file directly.
+ elif 'len' in dbkey_attributes:
+ len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name
+ if len_file:
+ genome = Genome( dbkey, len_file=len_file )
+
+
+ # Look in system builds.
+ if not genome:
+ len_ds = trans.db_dataset_for( dbkey )
+ if not len_ds:
+ genome = self.genomes[ dbkey ]
+ else:
+ gneome = Genome( dbkey, len_file=len_ds.file_name )
+
+ return genome.to_dict( num=num, chrom=chrom, low=low )
+
def has_reference_data( self, trans, dbkey, dbkey_owner=None ):
"""
Returns true if there is reference data for the specified dbkey. If dbkey is custom,
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/a3ca6efab775/
changeset: a3ca6efab775
user: jgoecks
date: 2012-06-03 17:08:45
summary: Enhance genome support: (a) make genome information available in Galaxy app and (b) refactor genome code out of visualization mixin and into Genome objects.
affected #: 6 files
diff -r 1890cb0d1cfbb3ef5a09affcdd18d2b8acf7d811 -r a3ca6efab775b82435770daea85c842617912727 lib/galaxy/app.py
--- a/lib/galaxy/app.py
+++ b/lib/galaxy/app.py
@@ -12,6 +12,7 @@
from galaxy.objectstore import build_object_store_from_config
import galaxy.quota
from galaxy.tags.tag_handler import GalaxyTagHandler
+from galaxy.visualization.genomes import Genomes
from galaxy.tools.imp_exp import load_history_imp_exp_tools
from galaxy.tools.genome_index import load_genome_index_tools
from galaxy.sample_tracking import external_service_types
@@ -70,6 +71,8 @@
self.security = security.SecurityHelper( id_secret=self.config.id_secret )
# Tag handler
self.tag_handler = GalaxyTagHandler()
+ # Genomes
+ self.genomes = Genomes( self )
# Tool data tables
self.tool_data_tables = galaxy.tools.data.ToolDataTableManager( self.config.tool_data_table_config_path )
# Initialize the tools, making sure the list of tool configs includes the reserved migrated_tools_conf.xml file.
diff -r 1890cb0d1cfbb3ef5a09affcdd18d2b8acf7d811 -r a3ca6efab775b82435770daea85c842617912727 lib/galaxy/visualization/genomes.py
--- /dev/null
+++ b/lib/galaxy/visualization/genomes.py
@@ -0,0 +1,289 @@
+import os, re, sys, glob
+from bx.seq.twobit import TwoBitFile
+from galaxy.util.json import from_json_string
+from galaxy import model
+from galaxy.util.bunch import Bunch
+
+# FIXME: copied from tracks.py
+# Message strings returned to browser
+messages = Bunch(
+ PENDING = "pending",
+ NO_DATA = "no data",
+ NO_CHROMOSOME = "no chromosome",
+ NO_CONVERTER = "no converter",
+ NO_TOOL = "no tool",
+ DATA = "data",
+ ERROR = "error",
+ OK = "ok"
+)
+
+def decode_dbkey( dbkey ):
+ """ Decodes dbkey and returns tuple ( username, dbkey )"""
+ if ':' in dbkey:
+ return dbkey.split( ':' )
+ else:
+ return None, dbkey
+
+class Genome( object ):
+ """
+ Encapsulates information about a known genome/dbkey.
+ """
+ def __init__( self, key, len_file=None, twobit_file=None ):
+ self.key = key
+ self.len_file = len_file
+ self.twobit_file = twobit_file
+
+
+class Genomes( object ):
+ """
+ Provides information about available genome data and methods for manipulating that data.
+ """
+
+ def __init__( self, app ):
+ # Create list of known genomes from len files.
+ self.genomes = {}
+ len_files = glob.glob( os.path.join( app.config.len_file_path, "*.len" ) )
+ for f in len_files:
+ key = os.path.split( f )[1].split( ".len" )[0]
+ self.genomes[ key ] = Genome( key, len_file=f )
+
+ # Add genome data (twobit files) to genomes.
+ for line in open( os.path.join( app.config.tool_data_path, "twobit.loc" ) ):
+ if line.startswith("#"): continue
+ val = line.split()
+ if len( val ) == 2:
+ key, path = val
+ if key in self.genomes:
+ self.genomes[ key ].twobit_file = path
+
+ def get_dbkeys_with_chrom_info( self, trans ):
+ """ Returns all valid dbkeys that have chromosome information. """
+
+ # All user keys have a len file.
+ user_keys = {}
+ user = trans.get_user()
+ if 'dbkeys' in user.preferences:
+ user_keys = from_json_string( user.preferences['dbkeys'] )
+
+ dbkeys = [ (v, k) for k, v in trans.db_builds if ( ( k in self.genomes and self.genomes[ k ].len_file ) or k in user_keys ) ]
+ return dbkeys
+
+ def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ):
+ """
+ Returns a naturally sorted list of chroms/contigs for a given dbkey.
+ Use either chrom or low to specify the starting chrom in the return list.
+ """
+ def check_int(s):
+ if s.isdigit():
+ return int(s)
+ else:
+ return s
+
+ def split_by_number(s):
+ return [ check_int(c) for c in re.split('([0-9]+)', s) ]
+
+ #
+ # Parameter check, setting.
+ #
+ if num:
+ num = int( num )
+ else:
+ num = sys.maxint
+
+ if low:
+ low = int( low )
+ if low < 0:
+ low = 0
+ else:
+ low = 0
+
+ # If there is no dbkey owner, default to current user.
+ dbkey_owner, dbkey = decode_dbkey( dbkey )
+ if dbkey_owner:
+ dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first()
+ else:
+ dbkey_user = trans.user
+
+ #
+ # Get len file.
+ #
+ len_file = None
+ len_ds = None
+ user_keys = {}
+ if dbkey_user and 'dbkeys' in dbkey_user.preferences:
+ user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
+ if dbkey in user_keys:
+ dbkey_attributes = user_keys[ dbkey ]
+ if 'fasta' in dbkey_attributes:
+ build_fasta = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] )
+ len_file = build_fasta.get_converted_dataset( trans, 'len' ).file_name
+ # Backwards compatibility: look for len file directly.
+ elif 'len' in dbkey_attributes:
+ len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name
+
+ if not len_file:
+ len_ds = trans.db_dataset_for( dbkey )
+ if not len_ds:
+ len_file = os.path.join( trans.app.config.len_file_path, "%s.len" % dbkey )
+ else:
+ len_file = len_ds.file_name
+
+ #
+ # Get chroms data:
+ # (a) chrom name, len;
+ # (b) whether there are previous, next chroms;
+ # (c) index of start chrom.
+ #
+ len_file_enumerate = enumerate( open( len_file ) )
+ chroms = {}
+ prev_chroms = False
+ start_index = 0
+ if chrom:
+ # Use starting chrom to start list.
+ found = False
+ count = 0
+ for line_num, line in len_file_enumerate:
+ if line.startswith("#"):
+ continue
+ name, len = line.split("\t")
+ if found:
+ chroms[ name ] = int( len )
+ count += 1
+ elif name == chrom:
+ # Found starting chrom.
+ chroms[ name ] = int ( len )
+ count += 1
+ found = True
+ start_index = line_num
+ if line_num != 0:
+ prev_chroms = True
+ if count >= num:
+ break
+ else:
+ # Use low to start list.
+ high = low + int( num )
+ prev_chroms = ( low != 0 )
+ start_index = low
+
+ # Read chrom data from len file.
+ # TODO: this may be too slow for very large numbers of chroms/contigs,
+ # but try it out for now.
+ if not os.path.exists( len_file ):
+ return None
+
+ for line_num, line in len_file_enumerate:
+ if line_num < low:
+ continue
+ if line_num >= high:
+ break
+ if line.startswith("#"):
+ continue
+ # LEN files have format:
+ # <chrom_name><tab><chrom_length>
+ fields = line.split("\t")
+ chroms[ fields[0] ] = int( fields[1] )
+
+ # Set flag to indicate whether there are more chroms after list.
+ next_chroms = False
+ try:
+ len_file_enumerate.next()
+ next_chroms = True
+ except:
+ # No more chroms to read.
+ pass
+
+ to_sort = [{ 'chrom': chrom, 'len': length } for chrom, length in chroms.iteritems()]
+ to_sort.sort(lambda a,b: cmp( split_by_number(a['chrom']), split_by_number(b['chrom']) ))
+ return { 'reference': self.has_reference_data( trans, dbkey, dbkey_user ), 'chrom_info': to_sort,
+ 'prev_chroms' : prev_chroms, 'next_chroms' : next_chroms, 'start_index' : start_index }
+
+ def has_reference_data( self, trans, dbkey, dbkey_owner=None ):
+ """
+ Returns true if there is reference data for the specified dbkey. If dbkey is custom,
+ dbkey_owner is needed to determine if there is reference data.
+ """
+ # Look for key in built-in builds.
+ if dbkey in self.available_genomes:
+ # There is built-in reference data.
+ return True
+
+ # Look for key in owner's custom builds.
+ if dbkey_owner and 'dbkeys' in dbkey_owner.preferences:
+ user_keys = from_json_string( dbkey_owner.preferences[ 'dbkeys' ] )
+ if dbkey in user_keys:
+ dbkey_attributes = user_keys[ dbkey ]
+ if 'fasta' in dbkey_attributes:
+ # Fasta + converted datasets can provide reference data.
+ return True
+
+ return False
+
+ def reference( self, trans, dbkey, chrom, low, high, **kwargs ):
+ """
+ Return reference data for a build.
+ """
+
+ # If there is no dbkey owner, default to current user.
+ dbkey_owner, dbkey = decode_dbkey( dbkey )
+ if dbkey_owner:
+ dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first()
+ else:
+ dbkey_user = trans.user
+
+ if not self.has_reference_data( trans, dbkey, dbkey_user ):
+ return None
+
+ #
+ # Get twobit file with reference data.
+ #
+ twobit_file_name = None
+ if dbkey in self.available_genomes:
+ # Built-in twobit.
+ twobit_file_name = self.available_genomes[dbkey]
+ else:
+ user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
+ dbkey_attributes = user_keys[ dbkey ]
+ fasta_dataset = trans.app.model.HistoryDatasetAssociation.get( dbkey_attributes[ 'fasta' ] )
+ error = self._convert_dataset( trans, fasta_dataset, 'twobit' )
+ if error:
+ return error
+ else:
+ twobit_dataset = fasta_dataset.get_converted_dataset( trans, 'twobit' )
+ twobit_file_name = twobit_dataset.file_name
+
+ # Read and return reference data.
+ try:
+ twobit = TwoBitFile( open( twobit_file_name ) )
+ if chrom in twobit:
+ seq_data = twobit[chrom].get( int(low), int(high) )
+ return { 'dataset_type': 'refseq', 'data': seq_data }
+ except IOError:
+ return None
+
+ ## FIXME: copied from tracks.py (tracks controller) - this should be consolidated when possible.
+ def _convert_dataset( self, trans, dataset, target_type ):
+ """
+ Converts a dataset to the target_type and returns a message indicating
+ status of the conversion. None is returned to indicate that dataset
+ was converted successfully.
+ """
+
+ # Get converted dataset; this will start the conversion if necessary.
+ try:
+ converted_dataset = dataset.get_converted_dataset( trans, target_type )
+ except NoConverterException:
+ return messages.NO_CONVERTER
+ except ConverterDependencyException, dep_error:
+ return { 'kind': messages.ERROR, 'message': dep_error.value }
+
+ # Check dataset state and return any messages.
+ msg = None
+ if converted_dataset and converted_dataset.state == model.Dataset.states.ERROR:
+ job_id = trans.sa_session.query( trans.app.model.JobToOutputDatasetAssociation ) \
+ .filter_by( dataset_id=converted_dataset.id ).first().job_id
+ job = trans.sa_session.query( trans.app.model.Job ).get( job_id )
+ msg = { 'kind': messages.ERROR, 'message': job.stderr }
+ elif not converted_dataset or converted_dataset.state != model.Dataset.states.OK:
+ msg = messages.PENDING
+
+ return msg
\ No newline at end of file
diff -r 1890cb0d1cfbb3ef5a09affcdd18d2b8acf7d811 -r a3ca6efab775b82435770daea85c842617912727 lib/galaxy/visualization/tracks/genomes.py
--- a/lib/galaxy/visualization/tracks/genomes.py
+++ /dev/null
@@ -1,264 +0,0 @@
-import os, re, sys
-from bx.seq.twobit import TwoBitFile
-from galaxy.util.json import from_json_string
-from galaxy import model
-from galaxy.util.bunch import Bunch
-
-# FIXME: copied from tracks.py
-# Message strings returned to browser
-messages = Bunch(
- PENDING = "pending",
- NO_DATA = "no data",
- NO_CHROMOSOME = "no chromosome",
- NO_CONVERTER = "no converter",
- NO_TOOL = "no tool",
- DATA = "data",
- ERROR = "error",
- OK = "ok"
-)
-
-def decode_dbkey( dbkey ):
- """ Decodes dbkey and returns tuple ( username, dbkey )"""
- if ':' in dbkey:
- return dbkey.split( ':' )
- else:
- return None, dbkey
-
-
-class Genomes( object ):
- """
- Provides information about available genome data and methods for manipulating that data.
- """
-
- def __init__( self, app ):
- # Create list of available genomes.
- self.available_genomes = None
- avail_genomes = {}
- for line in open( os.path.join( app.config.tool_data_path, "twobit.loc" ) ):
- if line.startswith("#"): continue
- val = line.split()
- if len( val ) == 2:
- key, path = val
- avail_genomes[ key ] = path
- self.available_genomes = avail_genomes
-
- def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ):
- """
- Returns a naturally sorted list of chroms/contigs for a given dbkey.
- Use either chrom or low to specify the starting chrom in the return list.
- """
- def check_int(s):
- if s.isdigit():
- return int(s)
- else:
- return s
-
- def split_by_number(s):
- return [ check_int(c) for c in re.split('([0-9]+)', s) ]
-
- #
- # Parameter check, setting.
- #
- if num:
- num = int( num )
- else:
- num = sys.maxint
-
- if low:
- low = int( low )
- if low < 0:
- low = 0
- else:
- low = 0
-
- # If there is no dbkey owner, default to current user.
- dbkey_owner, dbkey = decode_dbkey( dbkey )
- if dbkey_owner:
- dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first()
- else:
- dbkey_user = trans.user
-
- #
- # Get len file.
- #
- len_file = None
- len_ds = None
- user_keys = {}
- if dbkey_user and 'dbkeys' in dbkey_user.preferences:
- user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
- if dbkey in user_keys:
- dbkey_attributes = user_keys[ dbkey ]
- if 'fasta' in dbkey_attributes:
- build_fasta = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] )
- len_file = build_fasta.get_converted_dataset( trans, 'len' ).file_name
- # Backwards compatibility: look for len file directly.
- elif 'len' in dbkey_attributes:
- len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name
-
- if not len_file:
- len_ds = trans.db_dataset_for( dbkey )
- if not len_ds:
- len_file = os.path.join( trans.app.config.len_file_path, "%s.len" % dbkey )
- else:
- len_file = len_ds.file_name
-
- #
- # Get chroms data:
- # (a) chrom name, len;
- # (b) whether there are previous, next chroms;
- # (c) index of start chrom.
- #
- len_file_enumerate = enumerate( open( len_file ) )
- chroms = {}
- prev_chroms = False
- start_index = 0
- if chrom:
- # Use starting chrom to start list.
- found = False
- count = 0
- for line_num, line in len_file_enumerate:
- if line.startswith("#"):
- continue
- name, len = line.split("\t")
- if found:
- chroms[ name ] = int( len )
- count += 1
- elif name == chrom:
- # Found starting chrom.
- chroms[ name ] = int ( len )
- count += 1
- found = True
- start_index = line_num
- if line_num != 0:
- prev_chroms = True
- if count >= num:
- break
- else:
- # Use low to start list.
- high = low + int( num )
- prev_chroms = ( low != 0 )
- start_index = low
-
- # Read chrom data from len file.
- # TODO: this may be too slow for very large numbers of chroms/contigs,
- # but try it out for now.
- if not os.path.exists( len_file ):
- return None
-
- for line_num, line in len_file_enumerate:
- if line_num < low:
- continue
- if line_num >= high:
- break
- if line.startswith("#"):
- continue
- # LEN files have format:
- # <chrom_name><tab><chrom_length>
- fields = line.split("\t")
- chroms[ fields[0] ] = int( fields[1] )
-
- # Set flag to indicate whether there are more chroms after list.
- next_chroms = False
- try:
- len_file_enumerate.next()
- next_chroms = True
- except:
- # No more chroms to read.
- pass
-
- to_sort = [{ 'chrom': chrom, 'len': length } for chrom, length in chroms.iteritems()]
- to_sort.sort(lambda a,b: cmp( split_by_number(a['chrom']), split_by_number(b['chrom']) ))
- return { 'reference': self.has_reference_data( trans, dbkey, dbkey_user ), 'chrom_info': to_sort,
- 'prev_chroms' : prev_chroms, 'next_chroms' : next_chroms, 'start_index' : start_index }
-
- def has_reference_data( self, trans, dbkey, dbkey_owner=None ):
- """
- Returns true if there is reference data for the specified dbkey. If dbkey is custom,
- dbkey_owner is needed to determine if there is reference data.
- """
- # Look for key in built-in builds.
- if dbkey in self.available_genomes:
- # There is built-in reference data.
- return True
-
- # Look for key in owner's custom builds.
- if dbkey_owner and 'dbkeys' in dbkey_owner.preferences:
- user_keys = from_json_string( dbkey_owner.preferences[ 'dbkeys' ] )
- if dbkey in user_keys:
- dbkey_attributes = user_keys[ dbkey ]
- if 'fasta' in dbkey_attributes:
- # Fasta + converted datasets can provide reference data.
- return True
-
- return False
-
- def reference( self, trans, dbkey, chrom, low, high, **kwargs ):
- """
- Return reference data for a build.
- """
-
- # If there is no dbkey owner, default to current user.
- dbkey_owner, dbkey = decode_dbkey( dbkey )
- if dbkey_owner:
- dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first()
- else:
- dbkey_user = trans.user
-
- if not self.has_reference_data( trans, dbkey, dbkey_user ):
- return None
-
- #
- # Get twobit file with reference data.
- #
- twobit_file_name = None
- if dbkey in self.available_genomes:
- # Built-in twobit.
- twobit_file_name = self.available_genomes[dbkey]
- else:
- user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
- dbkey_attributes = user_keys[ dbkey ]
- fasta_dataset = trans.app.model.HistoryDatasetAssociation.get( dbkey_attributes[ 'fasta' ] )
- error = self._convert_dataset( trans, fasta_dataset, 'twobit' )
- if error:
- return error
- else:
- twobit_dataset = fasta_dataset.get_converted_dataset( trans, 'twobit' )
- twobit_file_name = twobit_dataset.file_name
-
- # Read and return reference data.
- try:
- twobit = TwoBitFile( open( twobit_file_name ) )
- if chrom in twobit:
- seq_data = twobit[chrom].get( int(low), int(high) )
- return { 'dataset_type': 'refseq', 'data': seq_data }
- except IOError:
- return None
-
- ## FIXME: copied from tracks.py (tracks controller) - this should be consolidated when possible.
- def _convert_dataset( self, trans, dataset, target_type ):
- """
- Converts a dataset to the target_type and returns a message indicating
- status of the conversion. None is returned to indicate that dataset
- was converted successfully.
- """
-
- # Get converted dataset; this will start the conversion if necessary.
- try:
- converted_dataset = dataset.get_converted_dataset( trans, target_type )
- except NoConverterException:
- return messages.NO_CONVERTER
- except ConverterDependencyException, dep_error:
- return { 'kind': messages.ERROR, 'message': dep_error.value }
-
- # Check dataset state and return any messages.
- msg = None
- if converted_dataset and converted_dataset.state == model.Dataset.states.ERROR:
- job_id = trans.sa_session.query( trans.app.model.JobToOutputDatasetAssociation ) \
- .filter_by( dataset_id=converted_dataset.id ).first().job_id
- job = trans.sa_session.query( trans.app.model.Job ).get( job_id )
- msg = { 'kind': messages.ERROR, 'message': job.stderr }
- elif not converted_dataset or converted_dataset.state != model.Dataset.states.OK:
- msg = messages.PENDING
-
- return msg
-
\ No newline at end of file
diff -r 1890cb0d1cfbb3ef5a09affcdd18d2b8acf7d811 -r a3ca6efab775b82435770daea85c842617912727 lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -370,22 +370,6 @@
encoded_id = trans.security.encode_id( vis.id )
return { "vis_id": encoded_id, "url": url_for( action='browser', id=encoded_id ) }
- def _get_dbkeys( self, trans ):
- """ Returns all valid dbkeys that a user can use in a visualization. """
-
- # Read len files.
- if not self.len_files:
- len_files = glob.glob( os.path.join(trans.app.config.len_file_path, "*.len") )
- self.len_files = [ os.path.split(f)[1].split(".len")[0] for f in len_files ] # get xxx.len
-
- user_keys = {}
- user = trans.get_user()
- if 'dbkeys' in user.preferences:
- user_keys = from_json_string( user.preferences['dbkeys'] )
-
- dbkeys = [ (v, k) for k, v in trans.db_builds if k in self.len_files or k in user_keys ]
- return dbkeys
-
def get_visualization( self, trans, id, check_ownership=True, check_accessible=False ):
""" Get a Visualization from the database by id, verifying ownership. """
# Load workflow from database
diff -r 1890cb0d1cfbb3ef5a09affcdd18d2b8acf7d811 -r a3ca6efab775b82435770daea85c842617912727 lib/galaxy/web/controllers/tracks.py
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -173,10 +173,6 @@
histories_grid = HistorySelectionGrid()
history_datasets_grid = HistoryDatasetsSelectionGrid()
tracks_grid = TracksterSelectionGrid()
-
- def __init__(self, app ):
- BaseUIController.__init__( self, app )
- self.genomes = Genomes( self.app )
@web.expose
@web.require_login()
@@ -188,7 +184,7 @@
@web.expose
@web.require_login()
def new_browser( self, trans, **kwargs ):
- return trans.fill_template( "tracks/new_browser.mako", dbkeys=self._get_dbkeys( trans ), default_dbkey=kwargs.get("default_dbkey", None) )
+ return trans.fill_template( "tracks/new_browser.mako", dbkeys=trans.app.genomes.get_dbkeys_with_chrom_info( trans ), default_dbkey=kwargs.get("default_dbkey", None) )
@web.json
def add_track_async(self, trans, hda_id=None, ldda_id=None):
diff -r 1890cb0d1cfbb3ef5a09affcdd18d2b8acf7d811 -r a3ca6efab775b82435770daea85c842617912727 lib/galaxy/web/controllers/visualization.py
--- a/lib/galaxy/web/controllers/visualization.py
+++ b/lib/galaxy/web/controllers/visualization.py
@@ -377,7 +377,7 @@
from the visualization title, but can be edited. This field
must contain only lowercase letters, numbers, and
the '-' character.""" )
- .add_select( "visualization_dbkey", "Visualization DbKey/Build", value=visualization_dbkey, options=self._get_dbkeys( trans ), error=None)
+ .add_select( "visualization_dbkey", "Visualization DbKey/Build", value=visualization_dbkey, options=trans.app.genomes.get_dbkeys_with_chrom_info( trans ), error=None)
.add_text( "visualization_annotation", "Visualization annotation", value=visualization_annotation, error=visualization_annotation_err,
help="A description of the visualization; annotation is shown alongside published visualizations."),
template="visualization/create.mako" )
https://bitbucket.org/galaxy/galaxy-central/changeset/4051e35d4cfd/
changeset: 4051e35d4cfd
user: jgoecks
date: 2012-06-03 18:19:30
summary: More support for genomes as first-class objects: (a) add genomes API; (b) fix bugs in providing genome data.
affected #: 4 files
diff -r a3ca6efab775b82435770daea85c842617912727 -r 4051e35d4cfd4f0f692b392553d9d99c9f388724 lib/galaxy/visualization/genomes.py
--- a/lib/galaxy/visualization/genomes.py
+++ b/lib/galaxy/visualization/genomes.py
@@ -107,6 +107,8 @@
#
# Get len file.
#
+
+ # Look first in user's custom builds.
len_file = None
len_ds = None
user_keys = {}
@@ -121,10 +123,11 @@
elif 'len' in dbkey_attributes:
len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name
+ # Look in system builds.
if not len_file:
len_ds = trans.db_dataset_for( dbkey )
if not len_ds:
- len_file = os.path.join( trans.app.config.len_file_path, "%s.len" % dbkey )
+ len_file = self.genomes[ dbkey ].len_file
else:
len_file = len_ds.file_name
@@ -203,7 +206,7 @@
dbkey_owner is needed to determine if there is reference data.
"""
# Look for key in built-in builds.
- if dbkey in self.available_genomes:
+ if dbkey in self.genomes and self.genomes[ dbkey ].twobit_file:
# There is built-in reference data.
return True
diff -r a3ca6efab775b82435770daea85c842617912727 -r 4051e35d4cfd4f0f692b392553d9d99c9f388724 lib/galaxy/web/api/genomes.py
--- /dev/null
+++ b/lib/galaxy/web/api/genomes.py
@@ -0,0 +1,25 @@
+from galaxy import config, tools, web, util
+from galaxy.web.base.controller import BaseController, BaseAPIController
+from galaxy.util.bunch import Bunch
+
+class GenomesController( BaseAPIController ):
+ """
+ RESTful controller for interactions with genome data.
+ """
+
+ @web.expose_api
+ def index( self, trans, **kwds ):
+ """
+ GET /api/genomes: returns a list of installed genomes
+ """
+
+ return []
+
+ @web.json
+ def show( self, trans, id, num=None, chrom=None, low=None ):
+ """
+ GET /api/genomes/{id}
+
+ Returns information about build <id>
+ """
+ return self.app.genomes.chroms( trans, dbkey=id, num=num, chrom=chrom, low=low )
\ No newline at end of file
diff -r a3ca6efab775b82435770daea85c842617912727 -r 4051e35d4cfd4f0f692b392553d9d99c9f388724 lib/galaxy/web/buildapp.py
--- a/lib/galaxy/web/buildapp.py
+++ b/lib/galaxy/web/buildapp.py
@@ -132,6 +132,7 @@
webapp.api_mapper.resource_with_deleted( 'quota', 'quotas', path_prefix='/api' )
webapp.api_mapper.resource( 'tool', 'tools', path_prefix='/api' )
webapp.api_mapper.resource_with_deleted( 'user', 'users', path_prefix='/api' )
+ webapp.api_mapper.resource( 'genome', 'genomes', path_prefix='/api' )
webapp.api_mapper.resource( 'visualization', 'visualizations', path_prefix='/api' )
webapp.api_mapper.resource( 'workflow', 'workflows', path_prefix='/api' )
webapp.api_mapper.resource_with_deleted( 'history', 'histories', path_prefix='/api' )
diff -r a3ca6efab775b82435770daea85c842617912727 -r 4051e35d4cfd4f0f692b392553d9d99c9f388724 lib/galaxy/web/controllers/tracks.py
--- a/lib/galaxy/web/controllers/tracks.py
+++ b/lib/galaxy/web/controllers/tracks.py
@@ -268,11 +268,11 @@
@web.json
def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ):
- return self.genomes.chroms( trans, dbkey=dbkey, num=num, chrom=chrom, low=low )
+ return self.app.genomes.chroms( trans, dbkey=dbkey, num=num, chrom=chrom, low=low )
@web.json
def reference( self, trans, dbkey, chrom, low, high, **kwargs ):
- return self.genomes.reference( trans, dbkey, chrom, low, high, **kwargs )
+ return self.app.genomes.reference( trans, dbkey, chrom, low, high, **kwargs )
@web.json
def raw_data( self, trans, dataset_id, chrom, low, high, **kwargs ):
@@ -771,7 +771,7 @@
# Get genome info.
dbkey = dataset.dbkey
- chroms_info = self.genomes.chroms( trans, dbkey=dbkey )
+ chroms_info = self.app.genomes.chroms( trans, dbkey=dbkey )
genome = { 'dbkey': dbkey, 'chroms_info': chroms_info }
# Get summary tree data for dataset.
https://bitbucket.org/galaxy/galaxy-central/changeset/ae4a2de89589/
changeset: ae4a2de89589
user: jgoecks
date: 2012-06-03 19:52:12
summary: Enable Genome objects and chromosome information to be dictified.
affected #: 1 file
diff -r 4051e35d4cfd4f0f692b392553d9d99c9f388724 -r ae4a2de89589270d6d872eef6c99848168a97240 lib/galaxy/visualization/genomes.py
--- a/lib/galaxy/visualization/genomes.py
+++ b/lib/galaxy/visualization/genomes.py
@@ -32,47 +32,12 @@
self.key = key
self.len_file = len_file
self.twobit_file = twobit_file
-
-
-class Genomes( object ):
- """
- Provides information about available genome data and methods for manipulating that data.
- """
-
- def __init__( self, app ):
- # Create list of known genomes from len files.
- self.genomes = {}
- len_files = glob.glob( os.path.join( app.config.len_file_path, "*.len" ) )
- for f in len_files:
- key = os.path.split( f )[1].split( ".len" )[0]
- self.genomes[ key ] = Genome( key, len_file=f )
-
- # Add genome data (twobit files) to genomes.
- for line in open( os.path.join( app.config.tool_data_path, "twobit.loc" ) ):
- if line.startswith("#"): continue
- val = line.split()
- if len( val ) == 2:
- key, path = val
- if key in self.genomes:
- self.genomes[ key ].twobit_file = path
-
- def get_dbkeys_with_chrom_info( self, trans ):
- """ Returns all valid dbkeys that have chromosome information. """
-
- # All user keys have a len file.
- user_keys = {}
- user = trans.get_user()
- if 'dbkeys' in user.preferences:
- user_keys = from_json_string( user.preferences['dbkeys'] )
-
- dbkeys = [ (v, k) for k, v in trans.db_builds if ( ( k in self.genomes and self.genomes[ k ].len_file ) or k in user_keys ) ]
- return dbkeys
-
- def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ):
+
+ def to_dict( self, num=None, chrom=None, low=None ):
"""
- Returns a naturally sorted list of chroms/contigs for a given dbkey.
- Use either chrom or low to specify the starting chrom in the return list.
+ Returns representation of self as a dictionary.
"""
+
def check_int(s):
if s.isdigit():
return int(s)
@@ -97,47 +62,13 @@
else:
low = 0
- # If there is no dbkey owner, default to current user.
- dbkey_owner, dbkey = decode_dbkey( dbkey )
- if dbkey_owner:
- dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first()
- else:
- dbkey_user = trans.user
-
- #
- # Get len file.
- #
-
- # Look first in user's custom builds.
- len_file = None
- len_ds = None
- user_keys = {}
- if dbkey_user and 'dbkeys' in dbkey_user.preferences:
- user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
- if dbkey in user_keys:
- dbkey_attributes = user_keys[ dbkey ]
- if 'fasta' in dbkey_attributes:
- build_fasta = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] )
- len_file = build_fasta.get_converted_dataset( trans, 'len' ).file_name
- # Backwards compatibility: look for len file directly.
- elif 'len' in dbkey_attributes:
- len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name
-
- # Look in system builds.
- if not len_file:
- len_ds = trans.db_dataset_for( dbkey )
- if not len_ds:
- len_file = self.genomes[ dbkey ].len_file
- else:
- len_file = len_ds.file_name
-
#
# Get chroms data:
# (a) chrom name, len;
# (b) whether there are previous, next chroms;
# (c) index of start chrom.
#
- len_file_enumerate = enumerate( open( len_file ) )
+ len_file_enumerate = enumerate( open( self.len_file ) )
chroms = {}
prev_chroms = False
start_index = 0
@@ -169,11 +100,6 @@
start_index = low
# Read chrom data from len file.
- # TODO: this may be too slow for very large numbers of chroms/contigs,
- # but try it out for now.
- if not os.path.exists( len_file ):
- return None
-
for line_num, line in len_file_enumerate:
if line_num < low:
continue
@@ -197,9 +123,99 @@
to_sort = [{ 'chrom': chrom, 'len': length } for chrom, length in chroms.iteritems()]
to_sort.sort(lambda a,b: cmp( split_by_number(a['chrom']), split_by_number(b['chrom']) ))
- return { 'reference': self.has_reference_data( trans, dbkey, dbkey_user ), 'chrom_info': to_sort,
- 'prev_chroms' : prev_chroms, 'next_chroms' : next_chroms, 'start_index' : start_index }
+ return {
+ 'id': self.key,
+ 'reference': self.twobit_file is not None,
+ 'chrom_info': to_sort,
+ 'prev_chroms' : prev_chroms,
+ 'next_chroms' : next_chroms,
+ 'start_index' : start_index
+ }
+
+class Genomes( object ):
+ """
+ Provides information about available genome data and methods for manipulating that data.
+ """
+
+ def __init__( self, app ):
+ # Create list of known genomes from len files.
+ self.genomes = {}
+ len_files = glob.glob( os.path.join( app.config.len_file_path, "*.len" ) )
+ for f in len_files:
+ key = os.path.split( f )[1].split( ".len" )[0]
+ self.genomes[ key ] = Genome( key, len_file=f )
+
+ # Add genome data (twobit files) to genomes.
+ for line in open( os.path.join( app.config.tool_data_path, "twobit.loc" ) ):
+ if line.startswith("#"): continue
+ val = line.split()
+ if len( val ) == 2:
+ key, path = val
+ if key in self.genomes:
+ self.genomes[ key ].twobit_file = path
+
+ def get_build( self, dbkey ):
+ """ Returns build for the given key. """
+ rval = None
+ if dbkey in self.genomes:
+ rval = self.genomes[ dbkey ]
+ return rval
+
+ def get_dbkeys_with_chrom_info( self, trans ):
+ """ Returns all valid dbkeys that have chromosome information. """
+ # All user keys have a len file.
+ user_keys = {}
+ user = trans.get_user()
+ if 'dbkeys' in user.preferences:
+ user_keys = from_json_string( user.preferences['dbkeys'] )
+
+ dbkeys = [ (v, k) for k, v in trans.db_builds if ( ( k in self.genomes and self.genomes[ k ].len_file ) or k in user_keys ) ]
+ return dbkeys
+
+ def chroms( self, trans, dbkey=None, num=None, chrom=None, low=None ):
+ """
+ Returns a naturally sorted list of chroms/contigs for a given dbkey.
+ Use either chrom or low to specify the starting chrom in the return list.
+ """
+
+ # If there is no dbkey owner, default to current user.
+ dbkey_owner, dbkey = decode_dbkey( dbkey )
+ if dbkey_owner:
+ dbkey_user = trans.sa_session.query( trans.app.model.User ).filter_by( username=dbkey_owner ).first()
+ else:
+ dbkey_user = trans.user
+
+ #
+ # Get/create genome object.
+ #
+ genome = None
+
+ # Look first in user's custom builds.
+ if dbkey_user and 'dbkeys' in dbkey_user.preferences:
+ user_keys = from_json_string( dbkey_user.preferences['dbkeys'] )
+ if dbkey in user_keys:
+ dbkey_attributes = user_keys[ dbkey ]
+ if 'fasta' in dbkey_attributes:
+ build_fasta = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dbkey_attributes[ 'fasta' ] )
+ len_file = build_fasta.get_converted_dataset( trans, 'len' ).file_name
+ # Backwards compatibility: look for len file directly.
+ elif 'len' in dbkey_attributes:
+ len_file = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( user_keys[ dbkey ][ 'len' ] ).file_name
+ if len_file:
+ genome = Genome( dbkey, len_file=len_file )
+
+
+ # Look in system builds.
+ if not genome:
+ len_ds = trans.db_dataset_for( dbkey )
+ if not len_ds:
+ genome = self.genomes[ dbkey ]
+ else:
+ gneome = Genome( dbkey, len_file=len_ds.file_name )
+
+ return genome.to_dict( num=num, chrom=chrom, low=low )
+
def has_reference_data( self, trans, dbkey, dbkey_owner=None ):
"""
Returns true if there is reference data for the specified dbkey. If dbkey is custom,
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0

commit/galaxy-central: greg: Fix for finding the desired file within a specified change set of a tool shed reposity manifest.
by Bitbucket 01 Jun '12
by Bitbucket 01 Jun '12
01 Jun '12
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/changeset/1890cb0d1cfb/
changeset: 1890cb0d1cfb
user: greg
date: 2012-06-01 21:37:38
summary: Fix for finding the desired file within a specified change set of a tool shed reposity manifest.
affected #: 1 file
diff -r 018179ad4c9bcec30baea5aee0918f45d254deb2 -r 1890cb0d1cfbb3ef5a09affcdd18d2b8acf7d811 lib/galaxy/webapps/community/controllers/common.py
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -360,20 +360,32 @@
"""Copy a file named filename from somewhere in the repository manifest to the directory to which dir refers."""
filename = strip_path( filename )
fctx = None
- # First see if the file is in ctx.
+ found = False
+ # First see if the file is in ctx. We have to be careful in determining if we found the correct file because multiple files
+ # with the same name may be in different directories within ctx if the repository owner moved the files as part of the change set.
+ # For example, in the following ctx.files() list, the former may have been moved to the latter:
+ # ['tmap_wrapper_0.0.19/tool_data_table_conf.xml.sample', 'tmap_wrapper_0.3.3/tool_data_table_conf.xml.sample']
for ctx_file in ctx.files():
ctx_file_name = strip_path( ctx_file )
if filename == ctx_file_name:
- fctx = ctx[ ctx_file ]
- else:
+ try:
+ fctx = ctx[ ctx_file ]
+ found = True
+ break
+ except:
+ continue
+ if not found:
# Find the file in the repository manifest.
for changeset in repo.changelog:
prev_ctx = repo.changectx( changeset )
for ctx_file in prev_ctx.files():
ctx_file_name = strip_path( ctx_file )
if filename == ctx_file_name:
- fctx = prev_ctx[ ctx_file ]
- break
+ try:
+ fctx = prev_ctx[ ctx_file ]
+ break
+ except:
+ continue
if fctx:
file_path = os.path.join( dir, filename )
fh = open( file_path, 'wb' )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0