details: http://www.bx.psu.edu/hg/galaxy/rev/0890f7bf9d49 changeset: 3562:0890f7bf9d49 user: Kanwei Li <kanwei@gmail.com> date: Thu Mar 25 15:21:57 2010 -0400 description: trackster: - New summary tree data structure that specializes in aggregation at defined levels - BED converter for summary_tree - Feature tracks now have a summary display when there are a lot of things to be rendered on screen, but will display simple lines and full detailed view as you zoom in and can fit more on screen - Added option to display number of features in a region of summary view - Line tracks can now be displayed as intensity - Pack scripts diffstat: datatypes_conf.xml.sample | 5 +- lib/galaxy/datatypes/binary.py | 2 +- lib/galaxy/datatypes/converters/bam_to_array_tree_converter.py | 45 -- lib/galaxy/datatypes/converters/bam_to_array_tree_converter.xml | 15 - lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py | 45 ++ lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.xml | 15 + lib/galaxy/datatypes/converters/bed_to_array_tree_converter.py | 29 - lib/galaxy/datatypes/converters/bed_to_array_tree_converter.xml | 14 - lib/galaxy/datatypes/converters/bed_to_summary_tree_converter.py | 25 + lib/galaxy/datatypes/converters/bed_to_summary_tree_converter.xml | 14 + lib/galaxy/datatypes/interval.py | 2 +- lib/galaxy/visualization/tracks/data/array_tree.py | 74 +-- lib/galaxy/visualization/tracks/data/interval_index.py | 32 +- lib/galaxy/visualization/tracks/data/summary_tree.py | 37 + lib/galaxy/visualization/tracks/summary.py | 88 ++++ lib/galaxy/web/controllers/tracks.py | 19 +- lib/galaxy/web/controllers/visualization.py | 4 +- static/scripts/packed/galaxy.workflow_editor.canvas.js | 2 +- static/scripts/packed/trackster.js | 2 +- static/scripts/trackster.js | 210 +++++---- templates/tracks/browser.mako | 2 +- 21 files changed, 397 insertions(+), 284 deletions(-) diffs (1067 lines): diff -r 44f2713a7279 -r 0890f7bf9d49 datatypes_conf.xml.sample --- a/datatypes_conf.xml.sample Wed Mar 24 15:13:57 2010 -0400 +++ b/datatypes_conf.xml.sample Thu Mar 25 15:21:57 2010 -0400 @@ -5,14 +5,14 @@ <datatype extension="axt" type="galaxy.datatypes.sequence:Axt" display_in_upload="true"/> <datatype extension="bam" type="galaxy.datatypes.binary:Bam" mimetype="application/octet-stream" display_in_upload="true"> <converter file="bam_to_bai.xml" target_datatype="bai"/> - <converter file="bam_to_array_tree_converter.xml" target_datatype="array_tree"/> + <converter file="bam_to_summary_tree_converter.xml" target_datatype="summary_tree"/> <display file="ucsc/bam.xml" /> </datatype> <datatype extension="bed" type="galaxy.datatypes.interval:Bed" display_in_upload="true"> <converter file="bed_to_gff_converter.xml" target_datatype="gff"/> <converter file="interval_to_coverage.xml" target_datatype="coverage"/> <converter file="bed_to_interval_index_converter.xml" target_datatype="interval_index"/> - <converter file="bed_to_array_tree_converter.xml" target_datatype="array_tree"/> + <converter file="bed_to_summary_tree_converter.xml" target_datatype="summary_tree"/> <converter file="bed_to_genetrack_converter.xml" target_datatype="genetrack"/> <!-- <display file="ucsc/interval_as_bed.xml" /> --> <display file="genetrack.xml" /> @@ -80,6 +80,7 @@ <converter file="wiggle_to_simple_converter.xml" target_datatype="interval"/> </datatype> <datatype extension="array_tree" type="galaxy.datatypes.data:Data" /> + <datatype extension="summary_tree" type="galaxy.datatypes.data:Data" /> <datatype extension="interval_index" type="galaxy.datatypes.data:Data" /> <!-- Start EMBOSS tools --> <datatype extension="acedb" type="galaxy.datatypes.data:Text"/> diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/binary.py --- a/lib/galaxy/datatypes/binary.py Wed Mar 24 15:13:57 2010 -0400 +++ b/lib/galaxy/datatypes/binary.py Thu Mar 25 15:21:57 2010 -0400 @@ -139,7 +139,7 @@ except: return "Binary bam alignments file (%s)" % ( data.nice_size( dataset.get_size() ) ) def get_track_type( self ): - return "ReadTrack", ["bai", "array_tree"] + return "ReadTrack", ["bai", "summary_tree"] class Binseq( Binary ): """Class describing a zip archive of binary sequence files""" diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/converters/bam_to_array_tree_converter.py --- a/lib/galaxy/datatypes/converters/bam_to_array_tree_converter.py Wed Mar 24 15:13:57 2010 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -#!/usr/bin/env python - -from __future__ import division - -import sys -from galaxy import eggs -import pkg_resources; pkg_resources.require( "bx-python" ); pkg_resources.require( "pysam" ) - -from pysam import csamtools -from bx.arrays.array_tree import * - -BLOCK_SIZE = 1000 - -class BamReader: - def __init__( self, input_fname, index_fname ): - self.bamfile = csamtools.Samfile( filename=input_fname, mode='rb', index_filename=index_fname ) - self.iterator = self.bamfile.fetch() - - def __iter__( self ): - return self - - def __next__( self ): - while True: - read = self.iterator.next() - return read.rname, read.mpos, read.pos + read.rlen, None, mapq - - -def main(): - - input_fname = sys.argv[1] - index_fname = sys.argv[2] - out_fname = sys.argv[3] - - reader = BamReader( input_fname, index_fname ) - - # Fill array from reader - d = array_tree_dict_from_reader( reader, {}, block_size = BLOCK_SIZE ) - - for array_tree in d.itervalues(): - array_tree.root.build_summary() - - FileArrayTreeDict.dict_to_file( d, open( out_fname, "w" ), no_leaves=True ) - -if __name__ == "__main__": - main() \ No newline at end of file diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/converters/bam_to_array_tree_converter.xml --- a/lib/galaxy/datatypes/converters/bam_to_array_tree_converter.xml Wed Mar 24 15:13:57 2010 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -<tool id="CONVERTER_bam_to_array_tree_0" name="Convert BAM to Array Tree" version="1.0.0"> -<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> --> - <command interpreter="python">bam_to_array_tree_converter.py $input1 $output1</command> - <inputs> - <page> - <param format="bam" name="input1" type="data" label="Choose BAM file"/> - <param format="bai" name="index" type="data" label="BAM index file"/> - </page> - </inputs> - <outputs> - <data format="array_tree" name="output1"/> - </outputs> - <help> - </help> -</tool> diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.py Thu Mar 25 15:21:57 2010 -0400 @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +from __future__ import division + +import sys +from galaxy import eggs +import pkg_resources; pkg_resources.require( "bx-python" ); pkg_resources.require( "pysam" ) + +from pysam import csamtools +from bx.arrays.array_tree import * + +BLOCK_SIZE = 1000 + +class BamReader: + def __init__( self, input_fname, index_fname ): + self.bamfile = csamtools.Samfile( filename=input_fname, mode='rb', index_filename=index_fname ) + self.iterator = self.bamfile.fetch() + + def __iter__( self ): + return self + + def __next__( self ): + while True: + read = self.iterator.next() + return read.rname, read.mpos, read.pos + read.rlen, None, mapq + + +def main(): + + input_fname = sys.argv[1] + index_fname = sys.argv[2] + out_fname = sys.argv[3] + + reader = BamReader( input_fname, index_fname ) + + # Fill array from reader + d = array_tree_dict_from_reader( reader, {}, block_size = BLOCK_SIZE ) + + for array_tree in d.itervalues(): + array_tree.root.build_summary() + + FileArrayTreeDict.dict_to_file( d, open( out_fname, "w" ), no_leaves=True ) + +if __name__ == "__main__": + main() \ No newline at end of file diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/datatypes/converters/bam_to_summary_tree_converter.xml Thu Mar 25 15:21:57 2010 -0400 @@ -0,0 +1,15 @@ +<tool id="CONVERTER_bam_to_summary_tree_0" name="Convert BAM to Summary Tree" version="1.0.0"> +<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> --> + <command interpreter="python">bam_to_summary_tree_converter.py $input1 $output1</command> + <inputs> + <page> + <param format="bam" name="input1" type="data" label="Choose BAM file"/> + <param format="bai" name="index" type="data" label="BAM index file"/> + </page> + </inputs> + <outputs> + <data format="summary_tree" name="output1"/> + </outputs> + <help> + </help> +</tool> diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/converters/bed_to_array_tree_converter.py --- a/lib/galaxy/datatypes/converters/bed_to_array_tree_converter.py Wed Mar 24 15:13:57 2010 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -from __future__ import division - -import sys -from galaxy import eggs -import pkg_resources; pkg_resources.require( "bx-python" ) -from bx.arrays.array_tree import * -from bx.arrays.bed import BedReader - -BLOCK_SIZE = 1000 - -def main(): - - input_fname = sys.argv[1] - out_fname = sys.argv[2] - - reader = BedReader( open( input_fname ) ) - - # Fill array from reader - d = array_tree_dict_from_reader( reader, {}, block_size = BLOCK_SIZE ) - - for array_tree in d.itervalues(): - array_tree.root.build_summary() - - FileArrayTreeDict.dict_to_file( d, open( out_fname, "w" ), no_leaves=True ) - -if __name__ == "__main__": - main() \ No newline at end of file diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/converters/bed_to_array_tree_converter.xml --- a/lib/galaxy/datatypes/converters/bed_to_array_tree_converter.xml Wed Mar 24 15:13:57 2010 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -<tool id="CONVERTER_bed_to_array_tree_0" name="Convert BED to Array Tree" version="1.0.0"> -<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> --> - <command interpreter="python">bed_to_array_tree_converter.py $input1 $output1</command> - <inputs> - <page> - <param format="bed" name="input1" type="data" label="Choose BED file"/> - </page> - </inputs> - <outputs> - <data format="array_tree" name="output1"/> - </outputs> - <help> - </help> -</tool> diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/converters/bed_to_summary_tree_converter.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/datatypes/converters/bed_to_summary_tree_converter.py Thu Mar 25 15:21:57 2010 -0400 @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +from __future__ import division + +import sys +from galaxy import eggs +import pkg_resources; pkg_resources.require( "bx-python" ) +from galaxy.visualization.tracks.summary import * +from bx.arrays.bed import BedReader + +def main(): + + input_fname = sys.argv[1] + out_fname = sys.argv[2] + + reader = BedReader( open( input_fname ) ) + + st = SummaryTree(block_size=100, levels=4, draw_cutoff=100, detail_cutoff=20) + for chrom, chrom_start, chrom_end, name, score in reader: + st.insert_range(chrom, chrom_start, chrom_end) + + st.write(out_fname) + +if __name__ == "__main__": + main() \ No newline at end of file diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/converters/bed_to_summary_tree_converter.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/datatypes/converters/bed_to_summary_tree_converter.xml Thu Mar 25 15:21:57 2010 -0400 @@ -0,0 +1,14 @@ +<tool id="CONVERTER_bed_to_summary_tree_0" name="Convert BED to Summary Tree" version="1.0.0"> +<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> --> + <command interpreter="python">bed_to_summary_tree_converter.py $input1 $output1</command> + <inputs> + <page> + <param format="bed" name="input1" type="data" label="Choose BED file"/> + </page> + </inputs> + <outputs> + <data format="summary_tree" name="output1"/> + </outputs> + <help> + </help> +</tool> diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/datatypes/interval.py --- a/lib/galaxy/datatypes/interval.py Wed Mar 24 15:13:57 2010 -0400 +++ b/lib/galaxy/datatypes/interval.py Thu Mar 25 15:21:57 2010 -0400 @@ -508,7 +508,7 @@ except: return False def get_track_type( self ): - return "FeatureTrack", ["interval_index", "array_tree"] + return "FeatureTrack", ["interval_index", "summary_tree"] class BedStrict( Bed ): """Tab delimited data in strict BED format - no non-standard columns allowed""" diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/visualization/tracks/data/array_tree.py --- a/lib/galaxy/visualization/tracks/data/array_tree.py Wed Mar 24 15:13:57 2010 -0400 +++ b/lib/galaxy/visualization/tracks/data/array_tree.py Thu Mar 25 15:21:57 2010 -0400 @@ -3,26 +3,13 @@ """ import pkg_resources; pkg_resources.require( "bx-python" ) -try: - from bx.arrays.array_tree import FileArrayTreeDict -except: - pass +from bx.arrays.array_tree import FileArrayTreeDict from math import floor, ceil, log, pow -import logging -logger = logging.getLogger(__name__) - -# Maybe this should be included in the datatype itself, so users can add their -# own types to the browser as long as they return the right format of data? - -SUMMARIZE_N = 200 class ArrayTreeDataProvider( object ): def __init__( self, dataset, original_dataset ): self.dataset = dataset - # def calc_resolution(self, start, end, density): - # return pow( 10, ceil( log( (end - start) / density , 10 ) ) ) - def get_stats( self, chrom ): f = open( self.dataset.file_name ) d = FileArrayTreeDict( f ) @@ -52,8 +39,8 @@ if n != n: # NaN != NaN return None else: - return float(n) - + return float(n) + def get_data( self, chrom, start, end, **kwargs ): f = open( self.dataset.file_name ) d = FileArrayTreeDict( f ) @@ -61,7 +48,7 @@ # Get the right chromosome try: chrom_array_tree = d[chrom] - except KeyError: + except: f.close() return None @@ -70,53 +57,26 @@ end = int( end ) resolution = max(1, ceil(float(kwargs['resolution']))) - level = int( ceil( log( resolution, block_size ) ) ) + level = int( floor( log( resolution, block_size ) ) ) level = max( level, 0 ) stepsize = block_size ** level # Is the requested level valid? assert 0 <= level <= chrom_array_tree.levels - if "frequencies" in kwargs: - if level <= 0: - # Low level enough to always display features - f.close() - return None + results = [] + for block_start in range( start, end, stepsize * block_size ): + # print block_start + # Return either data point or a summary depending on the level + indexes = range( block_start, block_start + stepsize * block_size, stepsize ) + if level > 0: + s = chrom_array_tree.get_summary( block_start, level ) + if s is not None: + results.extend( zip( indexes, map( self.float_nan, s.sums / s.counts ) ) ) else: - # Round to nearest bin - bin_start = start // (stepsize * block_size) * (stepsize * block_size) - - indexes = range( bin_start, (bin_start + stepsize * block_size), stepsize ) - summary = chrom_array_tree.get_summary( bin_start, level ) - if summary: - results = zip( indexes, map( int, summary.frequencies ) ) - filtered = filter(lambda tup: tup[0] >= start and tup[0] <= end, results) - sums = 0 - max_f = 0 - for tup in filtered: - sums += tup[1] - max_f = max(max_f, tup[1]) - - if max_f > 10000: - f.close() - return filtered, int(sums), float(sums)/len(filtered) - f.close() - return None - - else: - results = [] - for block_start in range( start, end, stepsize * block_size ): - # print block_start - # Return either data point or a summary depending on the level - indexes = range( block_start, block_start + stepsize * block_size, stepsize ) - if level > 0: - s = chrom_array_tree.get_summary( block_start, level ) - if s: - results.extend( zip( indexes, map( self.float_nan, s.sums / s.counts ) ) ) - else: - l = chrom_array_tree.get_leaf( block_start ) - if l: - results.extend( zip( indexes, map( self.float_nan, l ) ) ) + l = chrom_array_tree.get_leaf( block_start ) + if l is not None: + results.extend( zip( indexes, map( self.float_nan, l ) ) ) f.close() return results diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/visualization/tracks/data/interval_index.py --- a/lib/galaxy/visualization/tracks/data/interval_index.py Wed Mar 24 15:13:57 2010 -0400 +++ b/lib/galaxy/visualization/tracks/data/interval_index.py Thu Mar 25 15:21:57 2010 -0400 @@ -1,6 +1,8 @@ """ Interval index data provider for the Galaxy track browser. Kanwei Li, 2009 + +Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end, blocks ] """ import pkg_resources; pkg_resources.require( "bx-python" ) @@ -21,26 +23,22 @@ for start, end, offset in index.find(chrom, start, end): source.seek(offset) feature = source.readline().split() - payload = { 'uid': offset, 'start': start, 'end': end, 'name': feature[3] } - try: - payload['strand'] = feature[5] - except IndexError: - pass - - if 'include_blocks' in kwargs: - try: + payload = [ offset, start, end ] + if "no_detail" not in kwargs: + length = len(feature) + payload.append(feature[3]) # name + if length >= 6: # strand + payload.append(feature[5]) + + if length >= 8: + payload.append(int(feature[6])) + payload.append(int(feature[7])) + + if length >= 12: block_sizes = [ int(n) for n in feature[10].split(',') if n != ''] block_starts = [ int(n) for n in feature[11].split(',') if n != '' ] blocks = zip(block_sizes, block_starts) - payload['blocks'] = [ (start + block[1], start + block[1] + block[0]) for block in blocks] - except IndexError: - pass - - try: - payload['thick_start'] = int(feature[6]) - payload['thick_end'] = int(feature[7]) - except IndexError: - pass + payload.append( [ (start + block[1], start + block[1] + block[0]) for block in blocks] ) results.append(payload) diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/visualization/tracks/data/summary_tree.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/visualization/tracks/data/summary_tree.py Thu Mar 25 15:21:57 2010 -0400 @@ -0,0 +1,37 @@ +""" +Summary tree data provider for the Galaxy track browser. +""" + +import pkg_resources; pkg_resources.require( "bx-python" ) +from galaxy.visualization.tracks.summary import * +from math import ceil, log +from galaxy.util.lrucache import LRUCache + +CACHE = LRUCache(20) # Store 20 recently accessed indices for performance + +class SummaryTreeDataProvider( object ): + def __init__( self, dataset, original_dataset ): + self.dataset = dataset + + def get_summary( self, chrom, start, end, **kwargs): + filename = self.dataset.file_name + st = CACHE[filename] + if st is None: + st = summary_tree_from_file( self.dataset.file_name ) + CACHE[filename] = st + if chrom not in st.chrom_blocks: + return None + + resolution = max(1, ceil(float(kwargs['resolution']))) + + level = ceil( log( resolution, st.block_size ) ) + level = int(max( level, 0 )) + + stats = st.chrom_stats[chrom] + results = st.query(chrom, int(start), int(end), level) + if results == "detail" or level <= 0: + return None + elif results == "draw": + return "no_detail", None, None + else: + return results, stats["max"], stats["avg"] diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/visualization/tracks/summary.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/galaxy/visualization/tracks/summary.py Thu Mar 25 15:21:57 2010 -0400 @@ -0,0 +1,88 @@ +''' +2010, Kanwei Li +Summary tree data structure for aggregation +''' + +import sys, os +import cPickle + +VERSION = 1 + +class SummaryTree: + def __init__(self, block_size, levels, draw_cutoff, detail_cutoff): + self.version = VERSION + self.chrom_blocks = {} + self.levels = levels + self.draw_cutoff = draw_cutoff + self.detail_cutoff = detail_cutoff + self.block_size = block_size + self.chrom_stats = {} + + def find_block(self, num, level): + return (num / self.block_size ** level) + + def insert_range(self, chrom, start, end): + if chrom in self.chrom_blocks: + blocks = self.chrom_blocks[chrom] + else: + blocks = self.chrom_blocks[chrom] = {} + self.chrom_stats[chrom] = {} + for level in range(1, self.levels+1): + blocks[level] = {} + + + for level in range(1, self.levels+1): + block_level = blocks[level] + starting_block = self.find_block(start, level) + ending_block = self.find_block(end, level) + for block in range(starting_block, ending_block+1): + if block in block_level: + block_level[block] += 1 + else: + block_level[block] = 1 + + def finish(self): + ''' Checks for cutoff and only stores levels above it ''' + for chrom, blocks in self.chrom_blocks.iteritems(): + cur_best = 999 + for level in range(self.levels, 0, -1): + max_val = max(blocks[level].values()) + if max_val < self.draw_cutoff: + if "draw_level" not in self.chrom_stats[chrom]: + self.chrom_stats[chrom]["draw_level"] = level + elif max_val < self.detail_cutoff: + self.chrom_stats[chrom]["detail_level"] = level + break + else: + self.chrom_stats[chrom]["max"] = max_val + self.chrom_stats[chrom]["avg"] = float(max_val) / len(blocks[level]) + cur_best = level + + self.chrom_blocks[chrom] = dict([ (key, value) for key, value in blocks.iteritems() if key >= cur_best ]) + + def query(self, chrom, start, end, level): + if chrom in self.chrom_blocks: + stats = self.chrom_stats[chrom] + if "detail_level" in stats and level <= stats["detail_level"]: + return "detail" + elif "draw_level" in stats and level <= stats["draw_level"]: + return "draw" + blocks = self.chrom_blocks[chrom] + results = [] + multiplier = self.block_size ** level + starting_block = self.find_block(start, level) + ending_block = self.find_block(end, level) + for block in range(starting_block, ending_block+1): + if block in blocks[level]: + results.append( (block * multiplier, blocks[level][block]) ) + return results + + return None + + def write(self, filename): + self.finish() + cPickle.dump(self, open(filename, 'wb'), 2) + +def summary_tree_from_file(filename): + return cPickle.load(open(filename, "r")) + diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py Wed Mar 24 15:13:57 2010 -0400 +++ b/lib/galaxy/web/controllers/tracks.py Thu Mar 25 15:21:57 2010 -0400 @@ -25,6 +25,7 @@ from galaxy.visualization.tracks.data.array_tree import ArrayTreeDataProvider from galaxy.visualization.tracks.data.interval_index import IntervalIndexDataProvider from galaxy.visualization.tracks.data.bam import BamDataProvider +from galaxy.visualization.tracks.data.summary_tree import SummaryTreeDataProvider # Message strings returned to browser messages = Bunch( @@ -41,7 +42,8 @@ dataset_type_to_data_provider = { "array_tree": ArrayTreeDataProvider, "interval_index": IntervalIndexDataProvider, - "bai": BamDataProvider + "bai": BamDataProvider, + "summary_tree": SummaryTreeDataProvider } class DatasetSelectionGrid( grids.Grid ): @@ -199,6 +201,7 @@ track_type, indexes = dataset.datatype.get_track_type() converted = dict([ (index, self.__dataset_as_type( trans, dataset, index )) for index in indexes ]) + extra_info = None for index, converted_dataset in converted.iteritems(): if not converted_dataset: @@ -212,11 +215,15 @@ if len(converted) > 1: # Have to choose between array_tree and other provider - array_tree = ArrayTreeDataProvider( converted['array_tree'], dataset ) - freqs = array_tree.get_data( chrom, low, high, frequencies=True, **kwargs ) + summary_tree = SummaryTreeDataProvider( converted['summary_tree'], dataset ) + freqs = summary_tree.get_summary( chrom, low, high, **kwargs ) if freqs is not None: - frequencies, sums, avg_f = freqs - return { "dataset_type": "array_tree", "data": frequencies, "sums": sums, "avg_f": avg_f } + frequencies, max_v, avg_v = freqs + if frequencies != "no_detail": + return { "dataset_type": "summary_tree", "data": frequencies, "max": max_v, "avg": avg_v } + else: + kwargs["no_detail"] = True # meh + extra_info = "no_detail" dataset_type = "interval_index" else: dataset_type = converted.keys()[0] @@ -228,7 +235,7 @@ else: data = data_provider.get_data( chrom, low, high, **kwargs ) - return { "dataset_type": dataset_type, "data": data } + return { "dataset_type": dataset_type, "extra_info": extra_info, "data": data } def __dataset_as_type( self, trans, dataset, type ): """ diff -r 44f2713a7279 -r 0890f7bf9d49 lib/galaxy/web/controllers/visualization.py --- a/lib/galaxy/web/controllers/visualization.py Wed Mar 24 15:13:57 2010 -0400 +++ b/lib/galaxy/web/controllers/visualization.py Thu Mar 25 15:21:57 2010 -0400 @@ -25,8 +25,8 @@ key="free-text-search", visible=False, filterable="standard" ) ) operations = [ - grids.GridOperation( "Edit content", allow_multiple=False, url_args=dict( controller='tracks', action='browser' ) ), - grids.GridOperation( "Edit attributes", allow_multiple=False, url_args=dict( action='edit') ), + grids.GridOperation( "View", allow_multiple=False, url_args=dict( controller='tracks', action='browser' ) ), + grids.GridOperation( "Edit Attributes", allow_multiple=False, url_args=dict( action='edit') ), grids.GridOperation( "Share or Publish", allow_multiple=False, condition=( lambda item: not item.deleted ), async_compatible=False ), grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ), async_compatible=True, confirm="Are you sure you want to delete this visualization?" ), ] diff -r 44f2713a7279 -r 0890f7bf9d49 static/scripts/packed/galaxy.workflow_editor.canvas.js --- a/static/scripts/packed/galaxy.workflow_editor.canvas.js Wed Mar 24 15:13:57 2010 -0400 +++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js Thu Mar 25 15:21:57 2010 -0400 @@ -1,1 +1,1 @@ -function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatype=b}OutputTerminal.prototype=new Terminal();function InputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1){for(var b in this.datatypes){if(a.datatype=="input"){return true}if(issubtype(a.datatype,this.datatypes[b])){return true}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a! ){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;this.handle1.connect(this);this.handle2=a;this.handle2.connect(this)},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};var h=n(this.handle1.element)+5;var g=i(this.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*! f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canv as.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(d,a,b){var c=this;$(d).each(function(){var f=this.terminal=new InputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dropstart",function(g){g.dragProxy.terminal.connectors[0].inner_color="#BBFFBB"}).bind("dropend",function(g){g.dragProxy.terminal.connectors[0].inner_color="#FFFFFF"}).bind("drop",function(g){(new Connector(g.dragTarget.terminal,g.dropTarget.terminal)).redraw()}).bind("hover",function(){if(f.connectors.length>0){var g=$("<div class='callou! t'></div>").css({display:"none"}).appendTo("body").append($("<div class='buttons'></div>").append($("<img src='../images/delete_icon.png' />").click(function(){$.each(f.connectors,function(i,h){h.destroy()});g.remove()}))).bind("mouseleave",function(){$(this).remove()});g.css({top:$(this).offset().top-2,left:$(this).offset().left-g.width(),"padding-right":$(this).width()}).show()}});c.input_terminals[a]=f})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j){var i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var k=new Connector();k.dragging=true;k.connect(this.terminal,i.terminal);$.dropManage({filter:function(h){return this.terminal.can_accept(f)}}).addClass("input-terminal-active");return i}).bind("drag",function(i){var h=function(){var k=$(i.dragProx! y).offsetParent().offset(),j=i.offsetX-k.left,l=i.offsetY-k.top;$(i.dr agProxy).css({left:j,top:l});i.dragProxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h){h.dragProxy.terminal.connectors[0].destroy();$(h.dragProxy).remove();$.dropManage().removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(g){var d=this.element;if(g.type){this.type=! g.type}this.name=g.name;this.form_html=g.form_html;this.tool_state=g.tool_state;this.tool_errors=g.tool_errors;this.tooltip=g.tooltip?g.tooltip:"";this.annotation=g.annotation;if(this.tool_errors){d.addClass("tool-node-error")}else{d.removeClass("tool-node-error")}var c=this;var a=d.find(".toolFormBody");a.find("div").remove();var h=$("<div class='inputs'></div>").appendTo(a);$.each(g.data_inputs,function(j,b){var f=$("<div class='terminal input-terminal'></div>");c.enable_input_terminal(f,b.name,b.extensions);h.append($("<div class='form-row dataRow input-data-row' name='"+b.name+"'>"+b.label+"</div>").prepend(f))});if((g.data_inputs.length>0)&&(g.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(g.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");c.enable_output_terminal(j,b.name,b.extension);var f=b.name;if(b.extension!="input"){f=f+" ("+b.extension+")"}a.append($("<div class='form-row dataRow'>"+f+"</div>").append(j)! )});workflow.node_changed(this)},update_field_data:function(f){var c=$ (this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var g=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=g.find("div.input-data-row");$.each(f.data_inputs,function(k,h){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,h.name,h.extensions);g.find("div[name="+h.name+"]").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){j[0].terminal.connectors[0]=i;i.handle2=j[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+h.name+"'>"+h.label+"</div>").prepend(j))});g.replaceWith(b);g.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='! color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},to_simple:function(){var a={};$.each(this.nodes,function(b,d){var f={};$.each(d.input_terminals,function(g,h){f[h.name]=null;$.each(h.connectors,function(j,k){f[h.name]={id:k.handle1.node.id,output_name:k.handle1.name}})});var c={id:d.id,type:d.type,tool_id:d.tool_id,tool_state:d.t! ool_state,tool_errors:d.tool_errors,input_connections:f,position:$(d.e lement).position(),annotation:d.annotation};a[d.id]=c});return{steps:a}},from_simple:function(a){wf=this;var b=0;wf.name=a.name;$.each(a.steps,function(f,d){var c=prebuild_node("tool",d.name,d.tool_id);c.init_field_data(d);if(d.position){c.element.css({top:d.position.top,left:d.position.left})}c.id=d.id;wf.nodes[c.id]=c;b=Math.max(b,parseInt(f))});wf.id_counter=b+1;$.each(a.steps,function(f,d){var c=wf.nodes[f];$.each(d.input_connections,function(h,g){if(g){var i=wf.nodes[g.id];var j=new Connector();j.connect(i.output_terminals[g.output_name],c.input_terminals[h]);j.redraw()}})})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_cha! nges_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(f! unction(p,o){return $(d[p].element).position().top-$(d[o].element).pos ition().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(functio! n(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='../images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<img src='../images/delete_icon.png' />").click(function(b){g.destroy()}).hover(function(){$(this).attr("src","../images/delete_icon_dark.png")},function(){$(this).attr("src","../ima! ges/delete_icon.png")}));i.appendTo("#canvas-container");var d=$("#can vas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o){var f=$(this).offsetParent().offset(),b=o.offsetX-f.left,p=o.offsetY-f.top;$(this).css({left:b,top:p});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{te! st:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=fu! nction(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b. cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(g){var h=$(this).offset();var f=b.cc.position();c=f.top-h.top;d=f.left-h.left}).bind("drag",function(f){a(f.offsetX+d,f.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k){var j=b.cc.width(),g=b.cc.height(),f=b.oc.width(),h=b.oc.height(),i=$(this).offsetParent().offset(),m=k.offsetX-i.left,l=k.offsetY-i.top;a(-(m/f*j),-(l/h*g))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g){var i=$(this).offsetParent();var h=i.offset();var f=Math.max(i.width()-(g.offsetX-h.left),i.height()-(g.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",fun! ction(f){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;$.each(workflow.nodes,function(t,q){var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}}); \ No newline at end of file +function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatype=b}OutputTerminal.prototype=new Terminal();function InputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1){for(var b in this.datatypes){if(a.datatype=="input"){return true}if(issubtype(a.datatype,this.datatypes[b])){return true}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a! ){this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;this.handle1.connect(this);this.handle2=a;this.handle2.connect(this)},destroy:function(){if(this.handle1){this.handle1.disconnect(this)}if(this.handle2){this.handle2.disconnect(this)}$(this.canvas).remove()},redraw:function(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};var h=n(this.handle1.element)+5;var g=i(this.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*! f;this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canv as.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(d,a,b){var c=this;$(d).each(function(){var f=this.terminal=new InputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dropstart",function(g){g.dragProxy.terminal.connectors[0].inner_color="#BBFFBB"}).bind("dropend",function(g){g.dragProxy.terminal.connectors[0].inner_color="#FFFFFF"}).bind("drop",function(g){(new Connector(g.dragTarget.terminal,g.dropTarget.terminal)).redraw()}).bind("hover",function(){if(f.connectors.length>0){var g=$("<div class='callou! t'></div>").css({display:"none"}).appendTo("body").append($("<div class='buttons'></div>").append($("<img src='../images/delete_icon.png' />").click(function(){$.each(f.connectors,function(i,h){h.destroy()});g.remove()}))).bind("mouseleave",function(){$(this).remove()});g.css({top:$(this).offset().top-2,left:$(this).offset().left-g.width(),"padding-right":$(this).width()}).show()}});c.input_terminals[a]=f})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j){var i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var k=new Connector();k.dragging=true;k.connect(this.terminal,i.terminal);$.dropManage({filter:function(h){return this.terminal.can_accept(f)}}).addClass("input-terminal-active");return i}).bind("drag",function(i){var h=function(){var k=$(i.dragProx! y).offsetParent().offset(),j=i.offsetX-k.left,l=i.offsetY-k.top;$(i.dr agProxy).css({left:j,top:l});i.dragProxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h){h.dragProxy.terminal.connectors[0].destroy();$(h.dragProxy).remove();$.dropManage().removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b.appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(g){var d=this.element;if(g.type){this.type=! g.type}this.name=g.name;this.form_html=g.form_html;this.tool_state=g.tool_state;this.tool_errors=g.tool_errors;this.tooltip=g.tooltip?g.tooltip:"";this.annotation=g.annotation;if(this.tool_errors){d.addClass("tool-node-error")}else{d.removeClass("tool-node-error")}var c=this;var a=d.find(".toolFormBody");a.find("div").remove();var h=$("<div class='inputs'></div>").appendTo(a);$.each(g.data_inputs,function(j,b){var f=$("<div class='terminal input-terminal'></div>");c.enable_input_terminal(f,b.name,b.extensions);h.append($("<div class='form-row dataRow input-data-row' name='"+b.name+"'>"+b.label+"</div>").prepend(f))});if((g.data_inputs.length>0)&&(g.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(g.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");c.enable_output_terminal(j,b.name,b.extension);var f=b.name;if(b.extension!="input"){f=f+" ("+b.extension+")"}a.append($("<div class='form-row dataRow'>"+f+"</div>").append(j)! )});workflow.node_changed(this)},update_field_data:function(f){var c=$ (this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;this.annotation=f.annotation;if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var g=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=g.find("div.input-data-row");$.each(f.data_inputs,function(k,h){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,h.name,h.extensions);g.find("div[name="+h.name+"]").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){j[0].terminal.connectors[0]=i;i.handle2=j[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+h.name+"'>"+h.label+"</div>").prepend(j))});g.replaceWith(b);g.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div")! .remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},to_simple:function(){var a={};$.each(this.nodes,function(b,d){var f={};$.each(d.input_terminals,function(g,h){f[h.name]=null;$.each(h.connectors,function(j,k){f[h.name]={id:k.handle1.node.id,output_name:k.handle1.name}})});var c={id:d.id,type:d.type,too! l_id:d.tool_id,tool_state:d.tool_state,tool_errors:d.tool_errors,input _connections:f,position:$(d.element).position(),annotation:d.annotation};a[d.id]=c});return{steps:a}},from_simple:function(a){wf=this;var b=0;wf.name=a.name;$.each(a.steps,function(f,d){var c=prebuild_node("tool",d.name,d.tool_id);c.init_field_data(d);if(d.position){c.element.css({top:d.position.top,left:d.position.left})}c.id=d.id;wf.nodes[c.id]=c;b=Math.max(b,parseInt(f))});wf.id_counter=b+1;$.each(a.steps,function(f,d){var c=wf.nodes[f];$.each(d.input_connections,function(h,g){if(g){var i=wf.nodes[g.id];var j=new Connector();j.connect(i.output_terminals[g.output_name],c.input_terminals[h]);j.redraw()}})})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.a! ctive_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();this.has_changes=true;var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by! _level,function(k,l){l.sort(function(p,o){return $(d[p].element).posit ion().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_cont! ainer.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='../images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<img src='../images/delete_icon.png' />").click(function(b){g.destroy()}).hover(function(){$(this).attr("src","../images/delete_icon_dark.png")},function! (){$(this).attr("src","../images/delete_icon.png")}));i.appendTo("#can vas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o){var f=$(this).offsetParent().offset(),b=o.offsetX-f.left,p=o.offsetY-f.top;$(this).css({left:b,top:p});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.ext! end(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:f! unction(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2); f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(g){var h=$(this).offset();var f=b.cc.position();c=f.top-h.top;d=f.left-h.left}).bind("drag",function(f){a(f.offsetX+d,f.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k){var j=b.cc.width(),g=b.cc.height(),f=b.oc.width(),h=b.oc.height(),i=$(this).offsetParent().offset(),m=k.offsetX-i.left,l=k.offsetY-i.top;a(-(m/f*j),-(l/h*g))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g){var i=$(this).offsetParent();var h=i.offset();var f=Math.max(i.width()-(g.offsetX-h.left),i.height()-(g.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview! -border div").bind("drag",function(f){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;$.each(workflow.nodes,function(t,q){var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_! viewport_overlay()}}); \ No newline at end of file diff -r 44f2713a7279 -r 0890f7bf9d49 static/scripts/packed/trackster.js --- a/static/scripts/packed/trackster.js Wed Mar 24 15:13:57 2010 -0400 +++ b/static/scripts/packed/trackster.js Thu Mar 25 15:21:57 2010 -0400 @@ -1,1 +1,1 @@ -var DEBUG=false;var DENSITY=1000,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=20,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_! img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;this.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tr! acks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a. track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.span/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/DENSITY)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.ma! x(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){this.tracks[c].draw()}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/$(document).width()*(this.high-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:function(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.con! tent_div=$("<div class='track-content'>");this.container_div=$("<div>< /div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");a.content_div.text(DATA_LOADING);a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d=="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d.length===0||d=="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d=="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");b(d);a.draw()}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.! high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(k);var l=this.content_div.width()/f;var h;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;var b=(g-i)*l;if(this.left_offset){b-=this.left_offset}c.css({left:b});k.append(c);this.max_height=Math.max(this.max_height,c.height())}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden! =true;this.container_div.addClass("label-track")};$.extend(LabelTrack. prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,a,d,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.indexer=d;this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:fu! nction(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,indexer:a.indexer,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(d){if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=d.min;a.prefs.max_value=d.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var c=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);c.css({position:"relative",top:"25px"});c.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;! if(!c.data_queue[e]){c.data_queue[e]=true;$.getJSON(data_url,{indexer: this.indexer,chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution,},function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()})}},draw_tile:function(n,p,c,e){if(this.vertical_range===undefined){return}var q=p*DENSITY*n,a=DENSITY*n,b=$("<canvas class='tile'></canvas>"),s=n+"_"+p;if(!this.data_cache.get(s)){this.get_data(n,p);return}var r=this.data_cache.get(s);b.css({position:"absolute",top:0,left:(q-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var m=b.get(0).getContext("2d"),j=false,k=this.prefs.min_value,f=this.prefs.max_value,l=this.vertical_range,d=this.height_px;m.beginPath();for(var o=0;o<r.length-1;o++){var h=r[o][0]-q;var g=r[o][1];if(isNaN(g)){j=false}else{h=h*e;if(g<=k){g=k}else{if(g>=f){g=f}}g=Math.round(d-(g-k)/l*d);if(j){m.lineTo(h,g)}else{m.moveTo(h,g);j=true}}}m.stroke();c.append(b);return b},gen_options:function(j){var a=$("<div></div>").addClass("form-row");var e="track_"+j+"_! minval",g="track_"+j+"_maxval",h=$("<label></label>").attr("for",e).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),i=$("<input></input>").attr("id",e).val(b),d=$("<label></label>").attr("for",g).text("Max value:"),f=(this.prefs.max_value===undefined?"":this.prefs.max_value),c=$("<input></input>").attr("id",g).val(f);return a.append(h).append(i).append(d).append(c)},update_options:function(c){var a=$("#track_"+c+"_minval").val(),b=$("#track_"+c+"_maxval").val();if(a!==this.prefs.min_value||b!==this.prefs.max_value){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(b);this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+c+"_minval").text(this.prefs.min_value);$("#linetrack_"+c+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,d,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.indexer=d;this.height_px! =100;this.container_div.addClass("feature-track");this.dataset_id=a;th is.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black"};if(b.block_color!==undefined){this.prefs.block_color=b.block_color}if(b.label_color!==undefined){this.prefs.label_color=b.label_color}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;this.init_each({indexer:a.indexer,low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom},function(b){a.values=b;a.calc_slots();a.slots=a.zo_slots})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{indexer:b.indexer,chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,include_blocks:true},! function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},calc_slots:function(){var b=[],a=this.content_div.width()/(this.view.high-this.view.low),d=this.view.max_low;for(var e=0,f=this.values.length;e<f;e++){var g,h,k=this.values[e];g=Math.floor((k.start-d)*a);h=Math.ceil((k.end-d)*a);var c=0;while(true){if(b[c]===undefined||b[c]<g){b[c]=h;this.zo_slots[k.uid]=c;break}c++}}this.height_px=b.length*this.vertical_nodetail_px+15;this.content_div.css("height",this.height_px+"px")},incremental_slots:function(a,f){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var h=this.inc_slots[a].w_scale,s=[],g=0,b=$("<canvas></canvas>").get(0).getContext("2d"),l=this.view.max_low;var c,e,u=[];for(var p=0,q=f.length;p<q;p++){var d=f[p];if(this.inc_slots[a][d.uid]!==undefined){g=Math.max(g,this.inc_slots[a][d.uid]);u.push(this.inc_slots[a][d.uid])}else{s.push(p)}}for(var p=0,q=s.length;p<q;p++){var d=f[s[p]];c=Math.floor((d.start-l)! *h);c-=b.measureText(d.name).width;e=Math.ceil((d.end-l)*h);var o=0;wh ile(true){var m=true;if(this.s_e_by_tile[a][o]!==undefined){for(var n=0,t=this.s_e_by_tile[a][o].length;n<t;n++){var r=this.s_e_by_tile[a][o][n];if(e>r[0]&&c<r[1]){m=false;break}}}if(m){if(this.s_e_by_tile[a][o]===undefined){this.s_e_by_tile[a][o]=[]}this.s_e_by_tile[a][o].push([c,e]);this.inc_slots[a][d.uid]=o;g=Math.max(g,o);break}o++}}return g},draw_tile:function(B,f,g,M){if(!this.values){return}var s=f*DENSITY*B,G=(f+1)*DENSITY*B,r=DENSITY*B;var K,L,m;if(M>this.show_labels_scale){if(!this.showing_details){this.showing_details=true}for(var H in this.data_cache.obj_cache){var C=H.split("_"),z=C[0],c=C[1];if(z<=s&&c>=G){K=this.data_cache.get(H);break}}if(!K){this.data_queue[[s,G]]=true;this.get_data(s,G);return}m=this.incremental_slots(this.view.zoom_res,K)*this.vertical_detail_px+15;L=this.inc_slots[this.view.zoom_res]}else{if(this.showing_details){this.showing_details=false}m=this.height_px;L=this.zo_slots;K=this.values}var a=Math.ceil(r*M),u=$("<canvas class='tile'></can! vas>"),D=this.prefs.label_color,e=this.prefs.block_color,x=this.left_offset,N=this.showing_details,O=(this.showing_details?this.vertical_detail_px:this.vertical_nodetail_px);u.css({position:"absolute",top:0,left:(s-this.view.low)*M-x});u.get(0).width=a+x;u.get(0).height=m;var p=u.get(0).getContext("2d");p.fillStyle=this.prefs.block_color;p.font=this.default_font;p.textAlign="right";var I=0;for(var J=0,o=K.length;J<o;J++){var v=K[J];if(v.start<=G&&v.end>=s){var A=Math.floor(Math.max(0,(v.start-s)*M)),q=Math.ceil(Math.min(a,(v.end-s)*M)),y=L[v.uid]*O;var n,E,t=null,P=null;if(v.thick_start&&v.thick_end){t=Math.floor(Math.max(0,(v.thick_start-s)*M));P=Math.ceil(Math.min(a,(v.thick_end-s)*M))}if(!N){p.fillRect(A+x,y+5,q-A,1)}else{if(v.start>s){p.fillStyle=D;p.fillText(v.name,A-1+x,y+8);p.fillStyle=e}var Q=v.blocks;if(Q){if(v.strand){if(v.strand=="+"){p.fillStyle=RIGHT_STRAND}else{if(v.strand=="-"){p.fillStyle=LEFT_STRAND}}p.fillRect(A+x,y,q-A,10);p.fillStyle=e}for(var H=0,d=Q.le! ngth;H<d;H++){var h=Q[H],b=Math.floor(Math.max(0,(h[0]-s)*M)),w=Math.c eil(Math.min(a,(h[1]-s)*M));if(b>w){continue}n=5;E=3;p.fillRect(b+x,y+E,w-b,n);if(t!==undefined&&!(b>P||w<t)){n=9;E=1;var F=Math.max(b,t),l=Math.min(w,P);p.fillRect(F+x,y+E,l-F,n)}}}else{n=9;E=1;p.fillRect(A+x,y+E,q-A,n);if(v.strand){if(v.strand=="+"){p.fillStyle=RIGHT_STRAND_INV}else{if(v.strand=="-"){p.fillStyle=LEFT_STRAND_INV}}p.fillRect(A+x,y,q-A,10);p.fillStyle=prefs.block_color}}}I++}}g.append(u);return u},gen_options:function(g){var a=$("<div></div>").addClass("form-row");var d="track_"+g+"_block_color",c=$("<label></label>").attr("for",d).text("Block color:"),b=$("<input></input>").attr("id",d).attr("name",d).val(this.prefs.block_color),f="track_"+g+"_label_color",h=$("<label></label>").attr("for",f).text("Label color:"),e=$("<input></input>").attr("id",f).attr("name",f).val(this.prefs.label_color);return a.append(c).append(b).append(h).append(e)},update_options:function(c){var a=$("#track_"+c+"_block_color").val(),b=$("#track_"+c+"_label_color").val();if(a!==this.p! refs.block_color||b!==this.prefs.label_color){this.prefs.block_color=a;this.prefs.label_color=b;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,d,b){this.track_type="ReadTrack";this.tile_cache=new Cache(CACHED_TILES_FEATURE);Track.call(this,c,$("#viewport"));TiledTrack.call(this);FeatureTrack.call(this,c,a,d,b)};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{draw_tile:function(v,z,m,n){if(!this.values){return}var A=z*DENSITY*v,e=(z+1)*DENSITY*v,q=DENSITY*v;var D,p,h;h=this.height_px;p=this.zo_slots;D=this.values;var t=Math.ceil(q*n),r=$("<canvas class='tile'></canvas>");r.css({position:"absolute",top:0,left:(A-this.view.low)*n-this.left_offset});r.get(0).width=t+this.left_offset;r.get(0).height=h;var u=r.get(0).getContext("2d");u.fillStyle=this.prefs.block_color;u.font=this.default_font;u.textAlign="right";var s=u.measureText("A").width;var w=0;for(var x=0,y=D.length;x<y;x++){var l=D[x];if(l.start<=e&&l.end>=A){var g=Math.floor(! Math.max(0,(l.start-A)*n)),k=Math.ceil(Math.min(t,(l.end-A)*n)),f=p[l. uid]*this.vertical_detail_px;var a,E,d=null,o=null;if(n>s){for(var B=0,b=l.name.length;B<b;B++){var C=Math.floor(Math.max(0,(l.start+B-A)*n));u.fillText(l.name[B],C+this.left_offset,f+8)}}else{u.fillRect(g+this.left_offset,f+4,k-g,3)}}}m.append(r);return r}}); \ No newline at end of file +var DEBUG=false;var DENSITY=1000,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=20,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv! ,"repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var Drawer=function(){};$.extend(Drawer.prototype,{intensity:function(b,a,c){},});drawer=new Drawer();var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_! high=0;this.center=(this.max_high-this.max_low)/2;this.zoom_factor=3;t his.zoom_level=0;this.track_id_counter=0};$.extend(View.prototype,{add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[a]},update_options:function(){var b=$("ul#sortable-ul").sortable("toArray");var d=[];var c=$("#viewport > div").sort(function(g,f){return b.indexOf($(g).attr("id"))>b.indexOf($(f).attr("id"))});$("#viewport > div").remove();$("#viewport").html(c);for(var e in view.tracks){var a=view.tracks[e];if(a.update_options){a.update_options(e)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.center=this.center=(this.max_high-this.max_low)/2;this.zoom_level=0;$(".yaxislabel").remove()},redraw:function(f){this.span=this.max_high-this.max_low;var d=this.sp! an/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(a,b){if(this.max_high===0||this.high-this.low<30){return}if(a){this.center=a/b.width()*(this.! high-this.low)+this.low}this.zoom_level+=1;this.redraw()},zoom_out:fun ction(){if(this.max_high===0){return}if(this.zoom_level<=0){this.zoom_level=0;return}this.zoom_level-=1;this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div></div>").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();a.content_div.css("height","30px");a.content_div.text(DATA_LOADING);a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if((d.da! ta&&d.data.length===0)||d==="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var k=$("<div style='position: relative;'></div>");this.content_div.children(":first").remove();this.content_div.append(k);var l=this.content_div.width()/f;var h;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+this.view.zoom_level+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;var b=(g-i)*l;if(this.left_offse! t){b-=this.left_offset}c.css({left:b});k.append(c);this.max_height=Mat h.max(this.max_height,c.height())}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.app! end(b)}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("line-track");this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode:"line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.max_value!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.pre! fs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.t otal_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div></div>").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(o,q,c,e){if(this.vertical_range===undefined){return}var r=q*DENSITY*o,a=DENSITY*o,b=$("<canvas class! ='tile'></canvas>"),u=o+"_"+q;if(!this.data_cache.get(u)){this.get_data(o,q);return}var t=this.data_cache.get(u);b.css({position:"absolute",top:0,left:(r-this.view.low)*e});b.get(0).width=Math.ceil(a*e);b.get(0).height=this.height_px;var n=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,m=this.vertical_range,s=this.total_frequency,d=this.height_px;n.beginPath();if(t.length>1){var f=Math.ceil((t[1][0]-t[0][0])*e)}else{var f=10}for(var p=0;p<t.length;p++){var j=t[p][0]-r;var h=t[p][1];if(this.prefs.mode=="intensity"){if(h===null){continue}j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/m*255);n.fillStyle="rgb("+h+","+h+","+h+")";n.fillRect(j,0,f,30)}else{if(h===null){k=false;continue}else{j=j*e;if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/m*d);if(k){n.lineTo(j,h)}else{n.moveTo(j,h);k=true}}}}n.stroke();c.append(b);return b},gen_options:function(n){var a=$("<div></div>").addClass("form-row");var h="track_"+n+"_minval",k="tra! ck_"+n+"_maxval",e="track_"+n+"_mode",l=$("<label></label>").attr("for ",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"line":this.prefs.mode),c=$('<select id="'+e+'"><option value="line" id="mode_line">Line</option><option value="intensity" id="mode_intensity">Intensity</option></select>');$("#"+e+" #mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!=this.prefs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertica! l_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.left_offset=200;this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:true};if(b.block_color!==undefined){this.prefs.block_color=b.block_color}if(b.label_color!==undefined){this.prefs.label_color=b.label_color}if(b.show_counts!==undefined){this.pre! fs.show_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTr ack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(c){a.data_cache.set(b,c);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,g,c){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.s_e_by_tile[a]={}}var m=this.inc_slots[a].w_scale,u=[],h=0,b=$("<canvas></canvas>").get(0).getContext("2d"),n=this.view.max_low;var d,f,w=[];for(var r=0,s=g.length;r<s;r++){var e=g[r],l=e[0];if(this.inc_slots[a][l]!==undefined){h=Math.max(h,this.inc_slots[a][l]);w.push(this.inc_slots[a][l])}else{u.push(r)}}for(var r=0,s=u.length;r<s;r++){var e=g[u[r]];! l=e[0],feature_start=e[1],feature_end=e[2],feature_name=e[3];d=Math.floor((feature_start-n)*m);if(!c){d-=b.measureText(feature_name).width}f=Math.ceil((feature_end-n)*m);var q=0;while(true){var o=true;if(this.s_e_by_tile[a][q]!==undefined){for(var p=0,v=this.s_e_by_tile[a][q].length;p<v;p++){var t=this.s_e_by_tile[a][q][p];if(f>t[0]&&d<t[1]){o=false;break}}}if(o){if(this.s_e_by_tile[a][q]===undefined){this.s_e_by_tile[a][q]=[]}this.s_e_by_tile[a][q].push([d,f]);this.inc_slots[a][l]=q;h=Math.max(h,q);break}q++}}return h},draw_tile:function(R,h,m,ae){var z=h*DENSITY*R,X=(h+1)*DENSITY*R,w=DENSITY*R;var ac,ad,p;var Y=z+"_"+X;var ac=this.data_cache.get(Y);if(!ac){this.data_queue[[z,X]]=true;this.get_data(z,X);return}if(ac.dataset_type=="array_tree"){p=30}else{var P=(ac.extra_info==="no_detail");var af=(P?this.vertical_nodetail_px:this.vertical_detail_px);p=this.incremental_slots(this.view.zoom_res,ac.data,P)*af+15;m.parent().css("height",Math.max(this.height_px,p)+"px");ad=this.! inc_slots[this.view.zoom_res]}var a=Math.ceil(w*ae),F=$("<canvas class ='tile'></canvas>"),T=this.prefs.label_color,f=this.prefs.block_color,J=this.left_offset;F.css({position:"absolute",top:0,left:(z-this.view.low)*ae-J});F.get(0).width=a+J;F.get(0).height=p;var t=F.get(0).getContext("2d");t.fillStyle=this.prefs.block_color;t.font=this.default_font;t.textAlign="right";var C=55,W=255-C,g=W*2/3;if(ac.dataset_type=="summary_tree"){var L=ac.data;var v=ac.max;var l=ac.avg;if(ac.data.length>2){var b=Math.ceil((L[1][0]-L[0][0])*ae)}else{var b=50}for(var aa=0,s=L.length;aa<s;aa++){var N=Math.ceil((L[aa][0]-z)*ae);var M=L[aa][1];if(!M){continue}var E=Math.floor(W-(M/v)*W);t.fillStyle="rgb("+E+","+E+","+E+")";t.fillRect(N+J,0,b,20);if(this.prefs.show_counts){if(E>g){t.fillStyle="black"}else{t.fillStyle="#ddd"}t.textAlign="center";t.fillText(L[aa][1],N+J+(b/2),12)}}m.append(F);return F}var ac=ac.data;var Z=0;for(var aa=0,s=ac.length;aa<s;aa++){var G=ac[aa],D=G[0],ab=G[1],O=G[2],A=G[3];if(ab<=X&&O>=z){var Q=Math.floor(Math.max(0,(ab-z)*ae)),u=Math.ceil(Ma! th.min(a,(O-z)*ae)),K=ad[D]*af;if(P){t.fillRect(Q+J,K+5,u-Q,1)}else{var r=G[4],I=G[5],S=G[6],e=G[7];var q,U,B=null,ag=null;if(I&&S){B=Math.floor(Math.max(0,(I-z)*ae));ag=Math.ceil(Math.min(a,(S-z)*ae))}if(ab>z){t.fillStyle=T;t.fillText(A,Q-1+J,K+8);t.fillStyle=f}if(e){if(r){if(r=="+"){t.fillStyle=RIGHT_STRAND}else{if(r=="-"){t.fillStyle=LEFT_STRAND}}t.fillRect(Q+J,K,u-Q,10);t.fillStyle=f}for(var Y=0,d=e.length;Y<d;Y++){var n=e[Y],c=Math.floor(Math.max(0,(n[0]-z)*ae)),H=Math.ceil(Math.min(a,(n[1]-z)*ae));if(c>H){continue}q=5;U=3;t.fillRect(c+J,K+U,H-c,q);if(B!==undefined&&!(c>ag||H<B)){q=9;U=1;var V=Math.max(c,B),o=Math.min(H,ag);t.fillRect(V+J,K+U,o-V,q)}}}else{q=9;U=1;t.fillRect(Q+J,K+U,u-Q,q);if(G.strand){if(G.strand=="+"){t.fillStyle=RIGHT_STRAND_INV}else{if(G.strand=="-"){t.fillStyle=LEFT_STRAND_INV}}t.fillRect(Q+J,K,u-Q,10);t.fillStyle=prefs.block_color}}}Z++}}m.append(F);return F},gen_options:function(h){var a=$("<div></div>").addClass("form-row");var d="track_"+h+"_b! lock_color",j=$("<label></label>").attr("for",d).text("Block color:"), k=$("<input></input>").attr("id",d).attr("name",d).val(this.prefs.block_color),i="track_"+h+"_label_color",f=$("<label></label>").attr("for",i).text("Text color:"),g=$("<input></input>").attr("id",i).attr("name",i).val(this.prefs.label_color),e="track_"+h+"_show_count",c=$("<label></label>").attr("for",e).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",e).attr("name",e).attr("checked",this.prefs.show_counts);return a.append(j).append(k).append(f).append(g).append(b).append(c)},update_options:function(d){var b=$("#track_"+d+"_block_color").val(),c=$("#track_"+d+"_label_color").val(),a=$("#track_"+d+"_show_count").attr("checked");if(b!==this.prefs.block_color||c!==this.prefs.label_color||a!=this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=c;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){this.track_type="ReadTrack";this.tile_cache=new Cache(CACHED_TILES_FEATUR! E);Track.call(this,c,$("#viewport"));TiledTrack.call(this);FeatureTrack.call(this,c,a,b)};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{draw_tile:function(v,z,m,n){if(!this.values){return}var A=z*DENSITY*v,e=(z+1)*DENSITY*v,q=DENSITY*v;var D,p,h;h=this.height_px;p=this.zo_slots;D=this.values;var t=Math.ceil(q*n),r=$("<canvas class='tile'></canvas>");r.css({position:"absolute",top:0,left:(A-this.view.low)*n-this.left_offset});r.get(0).width=t+this.left_offset;r.get(0).height=h;var u=r.get(0).getContext("2d");u.fillStyle=this.prefs.block_color;u.font=this.default_font;u.textAlign="right";var s=u.measureText("A").width;var w=0;for(var x=0,y=D.length;x<y;x++){var l=D[x];if(l.start<=e&&l.end>=A){var g=Math.floor(Math.max(0,(l.start-A)*n)),k=Math.ceil(Math.min(t,(l.end-A)*n)),f=p[l.uid]*this.vertical_detail_px;var a,E,d=null,o=null;if(n>s){for(var B=0,b=l.name.length;B<b;B++){var C=Math.floor(Math.max(0,(l.start+B-A)*n));u.fillText(l.name[B],C+this.lef! t_offset,f+8)}}else{u.fillRect(g+this.left_offset,f+4,k-g,3)}}}m.appen d(r);return r}}); \ No newline at end of file diff -r 44f2713a7279 -r 0890f7bf9d49 static/scripts/trackster.js --- a/static/scripts/trackster.js Wed Mar 24 15:13:57 2010 -0400 +++ b/static/scripts/trackster.js Thu Mar 25 15:21:57 2010 -0400 @@ -163,7 +163,7 @@ this.center = Math.round( this.low + (this.high - this.low) / 2 ); // 10^log10(range / DENSITY) Close approximation for browser window, assuming DENSITY = window width - this.resolution = Math.pow( 10, Math.ceil( Math.log( (this.high - this.low) / DENSITY ) / Math.LN10 ) ); + this.resolution = Math.pow( 10, Math.ceil( Math.log( (this.high - this.low) / 200 ) / Math.LN10 ) ); this.zoom_res = Math.pow( FEATURE_LEVELS, Math.max(0,Math.ceil( Math.log( this.resolution, FEATURE_LEVELS ) / Math.log(FEATURE_LEVELS) ))); // Overview @@ -355,9 +355,10 @@ this.dataset_id = dataset_id; this.data_cache = new Cache(CACHED_DATA); this.tile_cache = new Cache(CACHED_TILES_LINE); - this.prefs = { 'min_value': undefined, 'max_value': undefined }; + this.prefs = { 'min_value': undefined, 'max_value': undefined, 'mode': 'line' }; if (prefs.min_value !== undefined) { this.prefs.min_value = prefs.min_value; } if (prefs.max_value !== undefined) { this.prefs.max_value = prefs.max_value; } + if (prefs.max_value !== undefined) { this.prefs.mode = prefs.mode; } }; $.extend( LineTrack.prototype, TiledTrack.prototype, { init: function() { @@ -457,14 +458,17 @@ ctx.beginPath(); // for intensity, calculate delta x in pixels to for width of box - var delta_x_px = Math.ceil((data[1][0] - data[0][0]) * w_scale); - var mode = "line"; + if (data.length > 1) { + var delta_x_px = Math.ceil((data[1][0] - data[0][0]) * w_scale); + } else { + var delta_x_px = 10; + } for ( var i = 0; i < data.length; i++ ) { var x = data[i][0] - tile_low; var y = data[i][1]; - if ( mode == "intensity" ) { + if ( this.prefs.mode == "intensity" ) { // DRAW INTENSITY if (y === null) { continue; @@ -475,7 +479,7 @@ } else if (y >= max_value) { y = max_value; } - y = Math.floor( (y - min_value) / vertical_range * 255 ); + y = 255 - Math.floor( (y - min_value) / vertical_range * 255 ); ctx.fillStyle = "rgb(" +y+ "," +y+ "," +y+ ")"; ctx.fillRect(x, 0, delta_x_px, 30); } @@ -512,20 +516,27 @@ var minval = 'track_' + track_id + '_minval', maxval = 'track_' + track_id + '_maxval', + mode = 'track_' + track_id + '_mode', min_label = $('<label></label>').attr("for", minval).text("Min value:"), min_val = (this.prefs.min_value === undefined ? "" : this.prefs.min_value), min_input = $('<input></input>').attr("id", minval).val(min_val), max_label = $('<label></label>').attr("for", maxval).text("Max value:"), max_val = (this.prefs.max_value === undefined ? "" : this.prefs.max_value), - max_input = $('<input></input>').attr("id", maxval).val(max_val); + max_input = $('<input></input>').attr("id", maxval).val(max_val), + mode_label = $('<label></label>').attr("for", mode).text("Display mode:"), + mode_val = (this.prefs.mode === undefined ? "line" : this.prefs.mode), + mode_input = $('<select id="' +mode+ '"><option value="line" id="mode_line">Line</option><option value="intensity" id="mode_intensity">Intensity</option></select>'); + $("#" + mode + " #mode_"+mode_val).attr('selected', 'selected'); - return container.append(min_label).append(min_input).append(max_label).append(max_input); + return container.append(min_label).append(min_input).append(max_label).append(max_input).append(mode_label).append(mode_input); }, update_options: function(track_id) { var min_value = $('#track_' + track_id + '_minval').val(), - max_value = $('#track_' + track_id + '_maxval').val(); - if ( min_value !== this.prefs.min_value || max_value !== this.prefs.max_value) { + max_value = $('#track_' + track_id + '_maxval').val(), + mode = $('#track_' + track_id + '_mode option:selected').val(); + if ( min_value !== this.prefs.min_value || max_value !== this.prefs.max_value || mode != this.prefs.mode ) { this.prefs.min_value = parseFloat(min_value); this.prefs.max_value = parseFloat(max_value); + this.prefs.mode = mode; this.vertical_range = this.prefs.max_value - this.prefs.min_value; // Update the y-axis $('#linetrack_' + track_id + '_minval').text(this.prefs.min_value); @@ -557,9 +568,10 @@ this.tile_cache = new Cache(CACHED_TILES_FEATURE); this.data_cache = new Cache(20); - this.prefs = { 'block_color': 'black', 'label_color': 'black' }; + this.prefs = { 'block_color': 'black', 'label_color': 'black', 'show_counts': true }; if (prefs.block_color !== undefined) { this.prefs.block_color = prefs.block_color; } if (prefs.label_color !== undefined) { this.prefs.label_color = prefs.label_color; } + if (prefs.show_counts !== undefined) { this.prefs.show_counts = prefs.show_counts; } }; $.extend( FeatureTrack.prototype, TiledTrack.prototype, { @@ -584,7 +596,7 @@ track.data_queue[key] = true; $.getJSON( data_url, { chrom: track.view.chrom, low: low, high: high, dataset_id: track.dataset_id, - include_blocks: true, resolution: this.view.resolution }, function (result) { + resolution: this.view.resolution }, function (result) { track.data_cache.set(key, result); // console.log("datacache", track.data_cache.get(key)); delete track.data_queue[key]; @@ -592,31 +604,7 @@ }); } }, - calc_slots: function() { - var end_ary = [], - scale = this.content_div.width() / (this.view.high - this.view.low), - max_low = this.view.max_low; - // console.log(scale, this.view.high, this.view.low); - for (var i = 0, len = this.values.length; i < len; i++) { - var f_start, f_end, feature = this.values[i]; - f_start = Math.floor( (feature.start - max_low) * scale ); - f_end = Math.ceil( (feature.end - max_low) * scale ); - - // if (include_labels) { console.log(f_start, f_end); } - var j = 0; - while (true) { - if (end_ary[j] === undefined || end_ary[j] < f_start) { - end_ary[j] = f_end; - this.zo_slots[feature.uid] = j; - break; - } - j++; - } - } - this.height_px = end_ary.length * this.vertical_nodetail_px + 15; - this.content_div.css( "height", this.height_px + "px" ); - }, - incremental_slots: function( level, features ) { + incremental_slots: function( level, features, no_detail ) { if (!this.inc_slots[level]) { this.inc_slots[level] = {}; this.inc_slots[level].w_scale = 1 / level; @@ -636,10 +624,11 @@ // If feature already exists in slots (from previously seen tiles), use the same slot, // otherwise if not seen, add to "undone" list for slot calculation for (var i = 0, len = features.length; i < len; i++) { - var feature = features[i]; - if (this.inc_slots[level][feature.uid] !== undefined) { - highest_slot = Math.max(highest_slot, this.inc_slots[level][feature.uid]); - slotted.push(this.inc_slots[level][feature.uid]); + var feature = features[i], + feature_uid = feature[0]; + if (this.inc_slots[level][feature_uid] !== undefined) { + highest_slot = Math.max(highest_slot, this.inc_slots[level][feature_uid]); + slotted.push(this.inc_slots[level][feature_uid]); } else { undone.push(i); } @@ -648,9 +637,15 @@ // console.log("Slotted: ", features.length - undone.length, "/", features.length, slotted); for (var i = 0, len = undone.length; i < len; i++) { var feature = features[undone[i]]; - f_start = Math.floor( (feature.start - max_low) * w_scale ); - f_start -= dummy_canvas.measureText(feature.name).width; - f_end = Math.ceil( (feature.end - max_low) * w_scale ); + feature_uid = feature[0], + feature_start = feature[1], + feature_end = feature[2], + feature_name = feature[3]; + f_start = Math.floor( (feature_start - max_low) * w_scale ); + if (!no_detail) { + f_start -= dummy_canvas.measureText(feature_name).width; + } + f_end = Math.ceil( (feature_end - max_low) * w_scale ); var j = 0; // Try to fit the feature to the first slot that doesn't overlap any other features in that slot @@ -668,7 +663,7 @@ if (found) { if (this.s_e_by_tile[level][j] === undefined) { this.s_e_by_tile[level][j] = []; } this.s_e_by_tile[level][j].push([f_start, f_end]); - this.inc_slots[level][feature.uid] = j; + this.inc_slots[level][feature_uid] = j; highest_slot = Math.max(highest_slot, j); break; } @@ -707,9 +702,12 @@ required_height = 30; // Blah } else { - // Calculate new slots incrementally for this new chunk of data and update height if necessary - required_height = this.incremental_slots( this.view.zoom_res, data.data ) * this.vertical_detail_px + 15; - // console.log(required_height); + // Calculate new slots incrementally for this new chunk of data and update height if necessary + var no_detail = (data.extra_info === "no_detail"); + + var y_scale = ( no_detail ? this.vertical_nodetail_px : this.vertical_detail_px ); + required_height = this.incremental_slots( this.view.zoom_res, data.data, no_detail ) * y_scale + 15; + parent_element.parent().css("height", Math.max(this.height_px, required_height) + "px"); slots = this.inc_slots[this.view.zoom_res]; } @@ -718,9 +716,7 @@ new_canvas = $("<canvas class='tile'></canvas>"), label_color = this.prefs.label_color, block_color = this.prefs.block_color, - left_offset = this.left_offset, - // showing_details = this.showing_details, - y_scale = this.vertical_detail_px; + left_offset = this.left_offset; new_canvas.css({ position: "absolute", @@ -734,24 +730,39 @@ ctx.fillStyle = this.prefs.block_color; ctx.font = this.default_font; ctx.textAlign = "right"; - var min_color = 150; + var min_color = 55, + color_span = 255 - min_color, + color_cutoff = color_span*2/3; // Where text switches from black to white + - if (data.dataset_type == "array_tree") { + if (data.dataset_type == "summary_tree") { var points = data.data; - var sums = data.sums; - var avg_f = data.avg_f; - var delta_x_px = Math.ceil((points[1][0] - points[0][0]) * w_scale); - + var max = data.max; + var avg = data.avg; + if (data.data.length > 2) { + var delta_x_px = Math.ceil((points[1][0] - points[0][0]) * w_scale); + } else { + var delta_x_px = 50; // Arbitrary, fix + } + for ( var i = 0, len = points.length; i < len; i++ ) { var x = Math.ceil( (points[i][0] - tile_low) * w_scale ); var y = points[i][1]; - - if (!y) { - continue; + + if (!y) { continue; } + var color = Math.floor( color_span - (y / max) * color_span ); + ctx.fillStyle = "rgb(" +color+ "," +color+ "," +color+ ")"; + ctx.fillRect(x + left_offset, 0, delta_x_px, 20); + + if (this.prefs.show_counts) { + if (color > color_cutoff) { + ctx.fillStyle = "black"; + } else { + ctx.fillStyle = "#ddd"; + } + ctx.textAlign = "center"; + ctx.fillText(points[i][1], x + left_offset + (delta_x_px/2), 12); } - y = Math.floor( min_color + (y - avg_f)/sums * min_color ); - ctx.fillStyle = "rgb(" +y+ "," +y+ "," +y+ ")"; - ctx.fillRect(x + left_offset, 0, delta_x_px, 20); } parent_element.append( new_canvas ); return new_canvas; @@ -760,42 +771,51 @@ var data = data.data; var j = 0; for (var i = 0, len = data.length; i < len; i++) { - var feature = data[i]; - if (feature.start <= tile_high && feature.end >= tile_low) { - var f_start = Math.floor( Math.max(0, (feature.start - tile_low) * w_scale) ), - f_end = Math.ceil( Math.min(width, (feature.end - tile_low) * w_scale) ), - y_center = slots[feature.uid] * y_scale; + var feature = data[i], + feature_uid = feature[0], + feature_start = feature[1], + feature_end = feature[2], + feature_name = feature[3]; - var thickness, y_start, thick_start = null, thick_end = null; - if (feature.thick_start && feature.thick_end) { - thick_start = Math.floor( Math.max(0, (feature.thick_start - tile_low) * w_scale) ); - thick_end = Math.ceil( Math.min(width, (feature.thick_end - tile_low) * w_scale) ); - } - // if (!showing_details) { - // Non-detail levels - // ctx.fillRect(f_start + left_offset, y_center + 5, f_end - f_start, 1); - // } else { + if (feature_start <= tile_high && feature_end >= tile_low) { + var f_start = Math.floor( Math.max(0, (feature_start - tile_low) * w_scale) ), + f_end = Math.ceil( Math.min(width, (feature_end - tile_low) * w_scale) ), + y_center = slots[feature_uid] * y_scale; + + // console.log(feature_uid, feature_start, feature_end, f_start, f_end, y_center); + if (no_detail) { + ctx.fillRect(f_start + left_offset, y_center + 5, f_end - f_start, 1); + } else { // Showing labels, blocks, details - if (feature.start > tile_low) { + var feature_strand = feature[4], + feature_ts = feature[5], + feature_te = feature[6], + feature_blocks = feature[7]; + + var thickness, y_start, thick_start = null, thick_end = null; + if (feature_ts && feature_te) { + thick_start = Math.floor( Math.max(0, (feature_ts - tile_low) * w_scale) ); + thick_end = Math.ceil( Math.min(width, (feature_te - tile_low) * w_scale) ); + } + if (feature_start > tile_low) { ctx.fillStyle = label_color; - ctx.fillText(feature.name, f_start - 1 + left_offset, y_center + 8); + ctx.fillText(feature_name, f_start - 1 + left_offset, y_center + 8); ctx.fillStyle = block_color; } - var blocks = feature.blocks; - if (blocks) { + if (feature_blocks) { // Draw introns - if (feature.strand) { - if (feature.strand == "+") { + if (feature_strand) { + if (feature_strand == "+") { ctx.fillStyle = RIGHT_STRAND; - } else if (feature.strand == "-") { + } else if (feature_strand == "-") { ctx.fillStyle = LEFT_STRAND; } ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10); ctx.fillStyle = block_color; } - for (var k = 0, k_len = blocks.length; k < k_len; k++) { - var block = blocks[k], + for (var k = 0, k_len = feature_blocks.length; k < k_len; k++) { + var block = feature_blocks[k], block_start = Math.floor( Math.max(0, (block[0] - tile_low) * w_scale) ), block_end = Math.ceil( Math.min(width, (block[1] - tile_low) * w_scale) ); if (block_start > block_end) { continue; } @@ -829,7 +849,7 @@ ctx.fillStyle = prefs.block_color; } } - // } + } j++; } } @@ -843,15 +863,21 @@ block_color_label = $('<label></label>').attr("for", block_color).text("Block color:"), block_color_input = $('<input></input>').attr("id", block_color).attr("name", block_color).val(this.prefs.block_color), label_color = 'track_' + track_id + '_label_color', - label_color_label = $('<label></label>').attr("for", label_color).text("Label color:"), - label_color_input = $('<input></input>').attr("id", label_color).attr("name", label_color).val(this.prefs.label_color); - return container.append(block_color_label).append(block_color_input).append(label_color_label).append(label_color_input); + label_color_label = $('<label></label>').attr("for", label_color).text("Text color:"), + label_color_input = $('<input></input>').attr("id", label_color).attr("name", label_color).val(this.prefs.label_color), + show_count = 'track_' + track_id + '_show_count', + show_count_label = $('<label></label>').attr("for", show_count).text("Show summary counts"), + show_count_input = $('<input type="checkbox" style="float:left;"></input>').attr("id", show_count).attr("name", show_count).attr("checked", this.prefs.show_counts); + + return container.append(block_color_label).append(block_color_input).append(label_color_label).append(label_color_input).append(show_count_input).append(show_count_label); }, update_options: function(track_id) { var block_color = $('#track_' + track_id + '_block_color').val(), - label_color = $('#track_' + track_id + '_label_color').val(); - if (block_color !== this.prefs.block_color || label_color !== this.prefs.label_color) { + label_color = $('#track_' + track_id + '_label_color').val(), + show_counts = $('#track_' + track_id + '_show_count').attr("checked"); + if (block_color !== this.prefs.block_color || label_color !== this.prefs.label_color || show_counts != this.prefs.show_counts) { this.prefs.block_color = block_color; this.prefs.label_color = label_color; + this.prefs.show_counts = show_counts; this.tile_cache.clear(); this.draw(); } diff -r 44f2713a7279 -r 0890f7bf9d49 templates/tracks/browser.mako --- a/templates/tracks/browser.mako Wed Mar 24 15:13:57 2010 -0400 +++ b/templates/tracks/browser.mako Thu Mar 25 15:21:57 2010 -0400 @@ -170,7 +170,7 @@ $("#right-border").bind( "dragend", function(e) { refresh(e); } ); $(window).trigger( "resize" ); - $("#viewport").bind( "dragstart", function( e ) { + $("#viewport-container").bind( "dragstart", function( e ) { this.original_low = view.low; this.current_height = e.clientY; this.current_x = e.offsetX;