From c98ed7820638f3e5a66accc73317997b10deb00b Mon Sep 17 00:00:00 2001 From: James Taylor Date: Sun, 30 Oct 2011 18:12:22 -0400 Subject: [PATCH 01/14] Modal dialogs now dim the entire page, show_message can be used for the old behavior (but should only be used for messages that do not require interaction) --- static/june_2007_style/blue/panel_layout.css | 2 + static/june_2007_style/blue/trackster.css | 52 ++++++++++---------- static/june_2007_style/panel_layout.css.tmpl | 8 +++ static/june_2007_style/trackster.css.tmpl | 4 +- static/scripts/galaxy.panels.js | 16 ++++-- templates/workflow/editor.mako | 4 +- 6 files changed, 54 insertions(+), 32 deletions(-) diff --git a/static/june_2007_style/blue/panel_layout.css b/static/june_2007_style/blue/panel_layout.css index 298ee40af41..5a3e2cf51d7 100644 --- a/static/june_2007_style/blue/panel_layout.css +++ b/static/june_2007_style/blue/panel_layout.css @@ -23,9 +23,11 @@ div.unified-panel-body{position:absolute;top:2em;bottom:0;width:100%;margin-top: .panel-header-button:hover{color:black;background-color:#ccc;} .panel-header-button:active{color:white;background-color:#aaaaaa;} #overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:20000;} +#overlay.modal #overlay-background{background:rgba(0,0,0,0.5);} .dialog-box-container{position:relative;margin-top:80px;margin-right:auto;margin-left:auto;} .dialog-box-wrapper{position:relative;padding:1em;background-color:rgba(0,0,0,0.5);-moz-border-radius:1em;-webkit-border-radius:1em;} .dialog-box{border:solid #999 1px;background:white;z-index:80000;} +#overlay.modal .dialog-box .body{min-width:600px;} .dialog-box .body{padding:5px;overflow:auto;max-height:500px;min-width:300px;} .dialog-box .buttons{padding:5px;} .panel-error-message,.panel-warning-message,.panel-done-message,.panel-info-message{height:24px;line-height:24px;color:#303030;padding:0px;padding-left:26px;background-color:#FFCCCC;background-image:url(error_small.png);background-repeat:no-repeat;background-position:6px 50%;} diff --git a/static/june_2007_style/blue/trackster.css b/static/june_2007_style/blue/trackster.css index e59b26677a9..e4f2aad9f4b 100644 --- a/static/june_2007_style/blue/trackster.css +++ b/static/june_2007_style/blue/trackster.css @@ -3,30 +3,9 @@ .content{font:10px verdana;} .nav-controls{text-align:center;padding:1px 0;} .nav-controls input{margin:0 5px;} -.menu-button{padding: 0px 4px 0px 4px;} #zoom-in,#zoom-out{display:inline-block;height:16px;width:16px;margin-bottom:-3px;cursor:pointer;} #zoom-out{background:transparent url(../images/fugue/magnifier-zoom-out.png) center center no-repeat;} #zoom-in{margin-left:10px;background:transparent url(../images/fugue/magnifier-zoom.png) center center no-repeat;} -.overview-icon{background:transparent url(../images/fugue/application-dock-270-bw.png) no-repeat;} -.overview-icon:hover{background:transparent url(../images/fugue/application-dock-270.png) no-repeat;} -.settings-icon{background:transparent url(../images/fugue/gear-bw.png) no-repeat;} -.settings-icon:hover{background:transparent url(../images/fugue/gear.png) no-repeat;} -.tools-icon{background:transparent url(../images/fugue/toolbox-bw.png) no-repeat;} -.tools-icon:hover{background:transparent url(../images/fugue/toolbox.png) no-repeat;} -.filters-icon{background:transparent url(../images/fugue/ui-slider-050-bw.png) no-repeat;} -.filters-icon:hover{background:transparent url(../images/fugue/ui-slider-050.png) no-repeat;} -.remove-icon,.overview-close{background:transparent url(../images/fugue/cross-small-bw.png) no-repeat;} -.remove-icon:hover,.overview-close:hover{background:transparent url(../images/fugue/cross-circle.png) no-repeat;} -#close-icon{background:transparent url(../images/fugue/cross-circle-bw.png) no-repeat;margin-right:0px;} -#close-icon:hover{background:transparent url(../images/fugue/cross-circle.png) no-repeat;} -#add-tracks-icon{background:transparent url(../images/fugue/plus-button-bw.png) no-repeat;} -#add-tracks-icon:hover{background:transparent url(../images/fugue/plus-button.png) no-repeat;} -#add-group-icon{background:transparent url(../images/fugue/block--plus-bw.png) no-repeat;} -#add-group-icon:hover{background:transparent url(../images/fugue/block--plus.png) no-repeat;} -#bookmarks-icon{background:transparent url(../images/fugue/bookmarks-bw.png) no-repeat;} -#bookmarks-icon:hover{background:transparent url(../images/fugue/bookmarks.png) no-repeat;} -#save-icon{background:transparent url(../images/fugue/disk--arrow-bw.png) no-repeat;} -#save-icon:hover{background:transparent url(../images/fugue/disk--arrow.png) no-repeat;} .nav-input{font-size:12px;width:30em;z-index:1000;} .location{display:inline-block;width:15em;margin:0 10px;} .draghandle{margin-top:2px;cursor:move;float:left;background:transparent url(../images/visualization/draggable_horizontal.png) center center no-repeat;width:10px;height:12px;} @@ -65,16 +44,39 @@ input{font:10px verdana;} .dynamic-tool,.filters{margin-left:0.25em;padding-bottom:0.5em;} .dynamic-tool{width:410px;} -.filters>.sliders,.display-controls{float:left;margin:1em;} +.filters > .sliders,.display-controls{float:left;margin:1em;} .sliders{width:410px;} -.display-controls{border-left:solid 2px #DDDDDD;padding-left:1em} -.filter-row{margin-top:0.4em;} +.display-controls{border-left:solid 2px #DDDDDD;padding-left:1em +} +.filter-row { + margin-top:0.4em;} .slider-row{margin-left:1em;} .elt-label{float:left;font-weight:bold;margin-right:1em;} .slider{float:right;width:200px;position:relative;} .tool-name{font-size:110%;font-weight:bold;} .param-row{margin-top:0.2em;margin-left:1em;} .param-label{float:left;font-weight:bold;padding-top:0.2em;} +.menu-button{padding:0px 4px 0px 4px;} +.settings-icon{background:transparent url(../images/fugue/gear-bw.png) no-repeat;} +.settings-icon:hover{background:transparent url(../images/fugue/gear.png) no-repeat;} +.overview-icon{background:transparent url(../images/fugue/application-dock-270-bw.png) no-repeat;} +.overview-icon:hover{background:transparent url(../images/fugue/application-dock-270.png) no-repeat;} +.tools-icon{background:transparent url(../images/fugue/toolbox-bw.png) no-repeat;} +.tools-icon:hover{background:transparent url(../images/fugue/toolbox.png) no-repeat;} +.filters-icon{background:transparent url(../images/fugue/ui-slider-050-bw.png) no-repeat;} +.filters-icon:hover{background:transparent url(../images/fugue/ui-slider-050.png) no-repeat;} +.remove-icon,.overview-close{background:transparent url(../images/fugue/cross-small-bw.png) no-repeat;} +.remove-icon:hover,.overview-close:hover{background:transparent url(../images/fugue/cross-circle.png) no-repeat;} +#close-icon{background:transparent url(../images/fugue/cross-circle-bw.png) no-repeat;margin-right:0px;} +#close-icon:hover{background:transparent url(../images/fugue/cross-circle.png) no-repeat;} +#add-tracks-icon{background:transparent url(../images/fugue/plus-button-bw.png) no-repeat;} +#add-tracks-icon:hover{background:transparent url(../images/fugue/plus-button.png) no-repeat;} +#add-group-icon{background:transparent url(../images/fugue/block--plus-bw.png) no-repeat;} +#add-group-icon:hover{background:transparent url(../images/fugue/block--plus.png) no-repeat;} +#bookmarks-icon{background:transparent url(../images/fugue/bookmarks-bw.png) no-repeat;} +#bookmarks-icon:hover{background:transparent url(../images/fugue/bookmarks.png) no-repeat;} +#save-icon{background:transparent url(../images/fugue/disk--arrow-bw.png) no-repeat;} +#save-icon:hover{background:transparent url(../images/fugue/disk--arrow.png) no-repeat;} .child-track-icon{background:url('../images/fugue/arrow-000-small-bw.png') no-repeat;width:30px;cursor:move;} .track-resize{background:white url('../images/visualization/draggable_vertical.png') no-repeat top center;position:absolute;right:3px;bottom:-4px;width:14px;height:7px;border:solid #999 1px;z-index:100;} .bookmark{background:white;border:solid #999 1px;border-right:none;margin:0.5em;margin-right:0;padding:0.5em;} @@ -85,4 +87,4 @@ input{font:10px verdana;} .icon.more-across{background:url('../images/fugue/arrow-transition-bw.png') no-repeat 0px 0px;} .intro{padding:1em;} .intro > .action-button{background-color:#CCC;padding:1em;} -.feature-popup{background-color:#DDD;position:absolute;z-index:1000} +.feature-popup{background-color:#DDD;position:absolute;z-index:1000;} diff --git a/static/june_2007_style/panel_layout.css.tmpl b/static/june_2007_style/panel_layout.css.tmpl index 614079a91a4..292501dda9c 100644 --- a/static/june_2007_style/panel_layout.css.tmpl +++ b/static/june_2007_style/panel_layout.css.tmpl @@ -161,6 +161,10 @@ div.unified-panel-body { z-index: 20000; } +#overlay.modal #overlay-background { + background: rgba(0,0,0,0.5); +} + .dialog-box-container { position: relative; margin-top: 80px; @@ -182,6 +186,10 @@ div.unified-panel-body { z-index: 80000; } +#overlay.modal .dialog-box .body { + min-width: 600px; +} + .dialog-box .body { padding: 5px; overflow: auto; diff --git a/static/june_2007_style/trackster.css.tmpl b/static/june_2007_style/trackster.css.tmpl index 16e92729a7c..2d051579f83 100644 --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,8 @@ input { background-color: #CCC; padding: 1em; } -.feature-popup{ +.feature-popup { background-color: #DDD; position: absolute; - z-index: 1000 + z-index: 1000; } diff --git a/static/scripts/galaxy.panels.js b/static/scripts/galaxy.panels.js index be187d8fb57..37adce6b7e2 100644 --- a/static/scripts/galaxy.panels.js +++ b/static/scripts/galaxy.panels.js @@ -163,13 +163,23 @@ function make_right_panel( panel_el, center_el, border_el ) { // Modal dialog boxes function hide_modal() { - $(".dialog-box-container" ).fadeOut( function() { + $(".dialog-box-container" ).hide( 0, function() { $("#overlay").hide(); + $("#overlay").removeClass( "modal" ); $( ".dialog-box" ).find( ".body" ).children().remove(); } ); }; -function show_modal( title, body, buttons, extra_buttons, init_fn ) { +function show_modal() { + $("#overlay").addClass( "modal" ); + _show_modal.apply( this, arguments ); +} + +function show_message() { + _show_modal.apply( this, arguments ); +} + +function _show_modal( title, body, buttons, extra_buttons, init_fn ) { if ( title ) { $( ".dialog-box" ).find( ".title" ).html( title ); $( ".dialog-box" ).find( ".unified-panel-header" ).show(); @@ -205,7 +215,7 @@ function show_modal( title, body, buttons, extra_buttons, init_fn ) { $( ".dialog-box" ).find( ".body" ).html( body ); if ( ! $(".dialog-box-container").is( ":visible" ) ) { $("#overlay").show(); - $(".dialog-box-container").fadeIn(); + $(".dialog-box-container").show(); } // Fix min-width so that modal cannot shrink considerably if // new content is loaded. diff --git a/templates/workflow/editor.mako b/templates/workflow/editor.mako index 27ff852b8dc..af01aecc946 100644 --- a/templates/workflow/editor.mako +++ b/templates/workflow/editor.mako @@ -213,7 +213,7 @@ show_workflow_parameters(); }, beforeSubmit: function( data ) { - show_modal( "Loading workflow", "progress" ); + show_message( "Loading workflow", "progress" ); } }); } @@ -650,7 +650,7 @@ }; var save_current_workflow = function ( eventObj, success_callback ) { - show_modal( "Saving workflow", "progress" ); + show_message( "Saving workflow", "progress" ); workflow.check_changes_in_active_form(); if (!workflow.has_changes) { hide_modal(); From 37b973097f230cf540cdd830854ee1dfc13d4eb7 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Sun, 30 Oct 2011 18:59:05 -0400 Subject: [PATCH 02/14] Trackster: fixes for bigwig data -- updated bx-python --- eggs.ini | 4 +- .../interval_to_summary_tree_converter.py | 2 +- .../visualization/tracks/data_providers.py | 59 ++++++++++++------- lib/galaxy/web/controllers/tracks.py | 3 +- static/scripts/trackster.js | 8 +-- 5 files changed, 46 insertions(+), 30 deletions(-) diff --git a/eggs.ini b/eggs.ini index 7bec53d8b6f..82c12aad70b 100644 --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ repository = http://eggs.g2.bx.psu.edu no_auto = pbs_python DRMAA_python [eggs:platform] -bx_python = 0.7.0 +bx_python = 0.7.1 Cheetah = 2.2.2 ctypes = 1.0.2 DRMAA_python = 0.2 @@ -67,7 +67,7 @@ Whoosh = 0.3.18 psycopg2 = _8.4.2_static pysqlite = _3.6.17_static MySQL_python = _5.1.41_static -bx_python = _494c2d1d68b3_rebuild1 +; bx_python = _494c2d1d68b3_rebuild1 GeneTrack = _dev_48da9e998f0caf01c5be731e926f4b0481f658f0 SQLAlchemy = _dev_r6498 pysam = _kanwei_b10f6e722e9a diff --git a/lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py b/lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py index 2fb73a6e18d..f7f89dbf407 100644 --- a/lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py +++ b/lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py @@ -50,4 +50,4 @@ def main(): st.write(out_fname) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/lib/galaxy/visualization/tracks/data_providers.py b/lib/galaxy/visualization/tracks/data_providers.py index eb561b62f74..3c09a8dc7d3 100644 --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -434,40 +434,55 @@ class BBIDataProvider( TracksDataProvider ): # Bigwig has the possibility of it being a standalone bigwig file, in which case we use # original_dataset, or coming from wig->bigwig conversion in which we use converted_dataset f, bbi = self._get_dataset() - + + # If the stats kwarg was provide, we compute overall summary data for the entire chromosome, + # but no reduced data -- currently only providing min/max which is used by trackster to + # determine the default range if 'stats' in kwargs: - all_dat = bbi.query(chrom, 0, 2147483647, 1) + # FIXME: use actual chromosome size + summary = bbi.summarize( chrom, 0, 214783647, 1 ) f.close() - if all_dat is None: + if summary is None: return None - - all_dat = all_dat[0] # only 1 summary - return { 'data' : { 'max': float( all_dat['max'] ), \ - 'min': float( all_dat['min'] ), \ - 'total_frequency': float( all_dat['coverage'] ) } \ - } - + else: + return dict( data=dict( min=summary.min_val[0], max=summary.max_val[0] ) ) + start = int(start) end = int(end) + + # The following seems not to work very well, for example it will only return one + # data point if the tile is 1280px wide. Not sure what the intent is. + # The first zoom level for BBI files is 640. If too much is requested, it will look at each block instead # of summaries. The calculation done is: zoom <> (end-start)/num_points/2. # Thus, the optimal number of points is (end-start)/num_points/2 = 640 # num_points = (end-start) / 1280 - num_points = (end-start) / 1280 - if num_points < 1: - num_points = end - start - else: - num_points = min(num_points, 500) + #num_points = (end-start) / 1280 + #if num_points < 1: + # num_points = end - start + #else: + # num_points = min(num_points, 500) - data = bbi.query(chrom, start, end, num_points) + # For now, we'll do 1000 data points by default However, the summaries + # don't seem to work when a summary pixel corresponds to less than one + # datapoint, so we prevent that. + # FIXME: need to switch over to using the full data at high levels of + # detail. + num_points = min( 1000, end - start ) + + summary = bbi.summarize( chrom, start, end, num_points ) f.close() - - pos = start - step_size = (end - start) / num_points + result = [] - if data: - for dat_dict in data: - result.append( (pos, float_nan(dat_dict['mean']) ) ) + + if summary: + mean = summary.sum_data / summary.valid_count + + pos = start + step_size = (end - start) / num_points + + for value in mean: + result.append( (pos, float_nan(value) ) ) pos += step_size return { 'data': result } diff --git a/lib/galaxy/web/controllers/tracks.py b/lib/galaxy/web/controllers/tracks.py index a1527438a1f..2fcc1916e3c 100644 --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -524,6 +524,7 @@ class TracksController( BaseUIController, UsesVisualization, UsesHistoryDatasetA if not standalone_provider.has_data( chrom ): return messages.NO_DATA valid_chroms = standalone_provider.valid_chroms() + # Have data if we get here return { "status": messages.DATA, "valid_chroms": valid_chroms } @@ -990,4 +991,4 @@ def _get_highest_priority_msg( message_list ): return_message = message elif return_message == None and message == messages.PENDING: return_message = message - return return_message \ No newline at end of file + return return_message diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index ce87c95f0f3..0657c8a8018 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -699,7 +699,7 @@ extend(Drawable.prototype, { remove: function() { this.container.remove_drawable(this); - this.container_div.fadeOut('slow', function() { + this.container_div.hide(0, function() { $(this).remove(); // HACK: is there a better way to update the view? view.update_intro_div(); @@ -1253,7 +1253,7 @@ extend( View.prototype, DrawableCollection.prototype, { DrawableCollection.prototype.remove_drawable.call(this, drawable); if (hide) { var view = this; - drawable.container_div.fadeOut('slow', function() { + drawable.container_div.hide(0, function() { $(this).remove(); view.update_intro_div(); }); @@ -2699,7 +2699,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, { w_scale = width / range, resolution = this.view.resolution, parent_element = $("
"); - + // For overview, adjust high, low, resolution, and w_scale. if (this.is_overview) { low = this.view.max_low; @@ -4759,4 +4759,4 @@ exports.VariantPainter = VariantPainter; for ( key in modules.trackster ) { target[key] = modules.trackster[key]; } -})(window); \ No newline at end of file +})(window); From 725f613e30783f6c0f31fa370a1d5d909a5ce0fb Mon Sep 17 00:00:00 2001 From: James Taylor Date: Mon, 31 Oct 2011 18:06:30 -0400 Subject: [PATCH 03/14] Trackster: for line tracks, compute default range using +/- two standard deviations around mean, for feature tracks, make feature popup look more like other tooltips --- .../visualization/tracks/data_providers.py | 25 ++++++++++++++----- static/june_2007_style/blue/trackster.css | 3 ++- static/june_2007_style/trackster.css.tmpl | 15 +++++++++-- static/scripts/trackster.js | 22 ++++++++++++---- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/lib/galaxy/visualization/tracks/data_providers.py b/lib/galaxy/visualization/tracks/data_providers.py index 3c09a8dc7d3..30bcb726e36 100644 --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -2,8 +2,8 @@ Data providers for tracks visualizations. """ -import sys -from math import ceil, log +import sys, time +from math import ceil, log, sqrt import pkg_resources pkg_resources.require( "bx-python" ) if sys.version_info[:2] == (2, 4): @@ -435,9 +435,9 @@ class BBIDataProvider( TracksDataProvider ): # original_dataset, or coming from wig->bigwig conversion in which we use converted_dataset f, bbi = self._get_dataset() - # If the stats kwarg was provide, we compute overall summary data for the entire chromosome, - # but no reduced data -- currently only providing min/max which is used by trackster to - # determine the default range + # If the stats kwarg was provide, we compute overall summary data for + # the entire chromosome, but no reduced data -- currently only + # providing values used by trackster to determine the default range if 'stats' in kwargs: # FIXME: use actual chromosome size summary = bbi.summarize( chrom, 0, 214783647, 1 ) @@ -445,7 +445,20 @@ class BBIDataProvider( TracksDataProvider ): if summary is None: return None else: - return dict( data=dict( min=summary.min_val[0], max=summary.max_val[0] ) ) + # Does the summary contain any defined values? + valid_count = summary.valid_count[0] + if summary.valid_count < 1: + return None + + # Compute $\mu \pm 2\sigma$ to provide an estimate for upper and lower + # bounds that contain ~95% of the data. + mean = summary.sum_data[0] / valid_count + var = summary.sum_squares[0] - mean + if valid_count > 1: + var /= valid_count - 1 + sd = sqrt( var ) + + return dict( data=dict( min=summary.min_val[0], max=summary.max_val[0], mean=mean, sd=sd ) ) start = int(start) end = int(end) diff --git a/static/june_2007_style/blue/trackster.css b/static/june_2007_style/blue/trackster.css index e4f2aad9f4b..a9ba82ae395 100644 --- a/static/june_2007_style/blue/trackster.css +++ b/static/june_2007_style/blue/trackster.css @@ -87,4 +87,5 @@ input{font:10px verdana;} .icon.more-across{background:url('../images/fugue/arrow-transition-bw.png') no-repeat 0px 0px;} .intro{padding:1em;} .intro > .action-button{background-color:#CCC;padding:1em;} -.feature-popup{background-color:#DDD;position:absolute;z-index:1000;} +.feature-popup{position:absolute;z-index:1000;padding:5px;font-size:10px;filter:alpha(opacity=80);background-repeat:no-repeat;background-image:url(../images/tipsy.gif);background-position:top center;} +.feature-popup-inner{padding:5px 8px 4px 8px;background-color:black;color:white;} diff --git a/static/june_2007_style/trackster.css.tmpl b/static/june_2007_style/trackster.css.tmpl index 2d051579f83..a902ec965d0 100644 --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,19 @@ input { background-color: #CCC; padding: 1em; } -.feature-popup { - background-color: #DDD; + +.feature-popup { position: absolute; z-index: 1000; + padding: 5px; + font-size: 10px; + filter: alpha(opacity=80); + background-repeat: no-repeat; + background-image: url(../images/tipsy.gif); + background-position: top center; +} +.feature-popup-inner { + padding: 5px 8px 4px 8px; + background-color: black; + color: white; } diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index 0657c8a8018..49006daba95 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2268,8 +2268,8 @@ FeatureTrackTile.prototype.predisplay_actions = function() { // Build popup. var popup = $("
").attr("id", feature_uid).addClass("feature-popup"), - key, value, - table = $("").appendTo(popup), row; + table = $("
"), + key, value, row; for (key in feature_dict) { value = feature_dict[key]; row = $("").appendTo(table); @@ -2277,6 +2277,7 @@ FeatureTrackTile.prototype.predisplay_actions = function() { $("
").attr("align", "left").appendTo(row) .text(typeof(value) == 'number' ? round(value, 2) : value); } + popup.append( $("
").append( table ) ); popups[feature_uid] = popup; } @@ -2287,7 +2288,7 @@ FeatureTrackTile.prototype.predisplay_actions = function() { // parseInt strips "px" from left, top measurements. +7 so that mouse pointer does not // overlap popup. var - popupX = offsetX + parseInt( tile.canvas.css("left") ) + 7, + popupX = offsetX + parseInt( tile.canvas.css("left") ) - popup.width() / 2, popupY = offsetY + parseInt( tile.canvas.css("top") ) + 7; popup.css("left", popupX + "px").css("top", popupY + "px") } @@ -3080,9 +3081,20 @@ extend(LineTrack.prototype, Drawable.prototype, TiledTrack.prototype, { track.container_div.addClass( "line-track" ); var data = result.data; if ( isNaN(parseFloat(track.prefs.min_value)) || isNaN(parseFloat(track.prefs.max_value)) ) { - track.prefs.min_value = data.min; - track.prefs.max_value = data.max; + // Compute default minimum and maximum values + var min_value = data.min + var max_value = data.max + // If mean and sd are present, use them to compute a ~95% window + // but only if it would shrink the range on one side + min_value = Math.floor( Math.min( 0, Math.max( min_value, data.mean - 2 * data.sd ) ) ) + max_value = Math.ceil( Math.max( 0, Math.min( max_value, data.mean + 2 * data.sd ) ) ) + // Update the prefs + track.prefs.min_value = min_value; + track.prefs.max_value = max_value; // Update the config + // FIXME: we should probably only save this when the user explicately sets it + // since we lose the ability to compute it on the fly (when changing + // chromosomes for example). $('#track_' + track.dataset_id + '_minval').val(track.prefs.min_value); $('#track_' + track.dataset_id + '_maxval').val(track.prefs.max_value); } From 97eee3d2ea0edc0440440828fb4f83b1d91a20d0 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Mon, 31 Oct 2011 18:31:15 -0400 Subject: [PATCH 04/14] Trackster: faster x axis drag/wheel scrolling, seems to work across browsers, but hard to tell across platforms, report your experiences --- static/scripts/trackster.js | 1 + 1 file changed, 1 insertion(+) diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index ddf5b8c6eda..8c94fde7fc3 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -1024,6 +1024,7 @@ extend( View.prototype, DrawableCollection.prototype, { // Only act on x axis scrolling if we see if, y will be i // handled by the browser when the event bubbles up if ( dx ) { + dx *= 50; var delta_chrom = Math.round( - dx / view.viewport_container.width() * (view.high - view.low) ); view.move_delta( delta_chrom ); } From 501bd68cd16039355ba9876b79a42de39953f3f8 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Tue, 1 Nov 2011 14:54:11 -0400 Subject: [PATCH 05/14] Trackster: set min/max values for wiggle tracks using standard deviation. Add a new FeaturePainter that uses Arcs to connect blocks. Allow FeaturePainters to provide (computed) top and bottom padding around the rows where features are drawn. --- .../visualization/tracks/data_providers.py | 10 +- static/scripts/trackster.js | 112 +++++++++++++++--- 2 files changed, 102 insertions(+), 20 deletions(-) diff --git a/lib/galaxy/visualization/tracks/data_providers.py b/lib/galaxy/visualization/tracks/data_providers.py index 8b3a705203a..9f83e698cab 100644 --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -10,6 +10,7 @@ if sys.version_info[:2] == (2, 4): pkg_resources.require( "ctypes" ) pkg_resources.require( "pysam" ) pkg_resources.require( "numpy" ) +from numpy import * from galaxy.datatypes.util.gff_util import * from galaxy.util.json import from_json_string from bx.interval_index_file import Indexes @@ -490,12 +491,17 @@ class BBIDataProvider( TracksDataProvider ): if summary: mean = summary.sum_data / summary.valid_count + + ## Standard deviation by bin, not yet used + ## var = summary.sum_squares - mean + ## var /= minimum( valid_count - 1, 1 ) + ## sd = sqrt( var ) pos = start step_size = (end - start) / num_points - for value in mean: - result.append( (pos, float_nan(value) ) ) + for i in range( num_points ): + result.append( (pos, float_nan( mean[i] ) ) ) pos += step_size return { 'data': result } diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index 8c94fde7fc3..b6c190781ca 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -3512,7 +3512,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, { var filter_height_scaler = (this.filters_manager.height_filter ? new FilterScaler(this.filters_manager.height_filter) : null); // HACK: ref_seq will only be defined for ReadTracks, and only the ReadPainter accepts that argument var painter = new (this.painter)(filtered, tile_low, tile_high, this.prefs, mode, filter_alpha_scaler, filter_height_scaler, ref_seq); - var required_height = Math.max(MIN_TRACK_HEIGHT, painter.get_required_height(slots_required)); + var required_height = Math.max(MIN_TRACK_HEIGHT, painter.get_required_height(slots_required,width)); var canvas = this.view.canvas_manager.new_canvas(); var feature_mapper = null; @@ -4078,6 +4078,7 @@ var FeaturePositionMapper = function(slot_height) { this.feature_positions = {}; this.slot_height = slot_height; this.translation = 0; + this.y_translation = 0; }; /** @@ -4099,7 +4100,7 @@ FeaturePositionMapper.prototype.map_feature_data = function(feature_data, slot, */ FeaturePositionMapper.prototype.get_feature_data = function(x, y) { // Find slot using Y. - var slot = Math.floor( y/this.slot_height ), + var slot = Math.floor( (y-this.y_translation)/this.slot_height ), feature_dict; // May not be over a slot due to padding, margin, etc. @@ -4129,15 +4130,23 @@ var FeaturePainter = function(data, view_start, view_end, prefs, mode, alpha_sca FeaturePainter.prototype.default_prefs = { block_color: "#FFF", connector_color: "#FFF" }; extend(FeaturePainter.prototype, { - get_required_height: function(rows_required) { + get_required_height: function(rows_required, width) { // y_scale is the height per row var required_height = y_scale = this.get_row_height(), mode = this.mode; // If using a packing mode, need to multiply by the number of slots used if (mode === "no_detail" || mode === "Squish" || mode === "Pack") { required_height = rows_required * y_scale; } + return required_height + this.get_top_padding(width) + this.get_bottom_padding(width); + }, + /** Extra padding before first row of features */ + get_top_padding: function(width) { + return 0; + }, + /** Extra padding after last row of features */ + get_bottom_padding: function(width) { // Pad bottom by half a row, at least 5 px - return required_height + Math.max( Math.round( y_scale / 2 ), 5 ); + return Math.max( Math.round( this.get_row_height() / 2 ), 5 ) }, /** * Draw data on ctx using slots and within the rectangle defined by width and height. Returns @@ -4174,6 +4183,7 @@ extend(FeaturePainter.prototype, { } ctx.restore(); + feature_mapper.y_translation = this.get_top_padding(width); return feature_mapper; }, /** @@ -4200,6 +4210,10 @@ var DENSE_TRACK_HEIGHT = 10, var LinkedFeaturePainter = function(data, view_start, view_end, prefs, mode, alpha_scaler, height_scaler) { FeaturePainter.call(this, data, view_start, view_end, prefs, mode, alpha_scaler, height_scaler); + // Whether to draw a single connector in the background that spans the entire feature (the intron fishbone) + this.draw_background_connector = true; + // Whether to call draw_connector for every pair of blocks + this.draw_individual_connectors = false; }; extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { @@ -4237,7 +4251,7 @@ extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { f_end = Math.ceil( Math.min(width, Math.max(0, (feature_end - tile_low) * w_scale)) ), draw_start = f_start, draw_end = f_end, - y_center = (mode === "Dense" ? 0 : (0 + slot)) * y_scale, + y_center = (mode === "Dense" ? 0 : (0 + slot)) * y_scale + this.get_top_padding(width), thickness, y_start, thick_start = null, thick_end = null, // TODO: is there any reason why block, label color cannot be set at the Painter level? block_color = this.prefs.block_color, @@ -4300,38 +4314,51 @@ extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { // needed. This ensures that whole feature, regardless of whether it starts with // a block, is visible. // - - // Draw whole feature as connector/intron. + + // Compute y axis center position and height var cur_y_center, cur_height; if (mode === "Squish" || mode === "Dense") { - ctx.fillStyle = CONNECTOR_COLOR; cur_y_center = y_center + Math.floor(SQUISH_FEATURE_HEIGHT/2) + 1; cur_height = 1; } else { // mode === "Pack" if (feature_strand) { - var cur_y_center = y_center; - var cur_height = thick_height; - if (feature_strand === "+") { - ctx.fillStyle = ctx.canvas.manager.get_pattern( 'right_strand' ); - } else if (feature_strand === "-") { - ctx.fillStyle = ctx.canvas.manager.get_pattern( 'left_strand' ); - } + cur_y_center = y_center; + cur_height = thick_height; } else { - ctx.fillStyle = CONNECTOR_COLOR; cur_y_center += (SQUISH_FEATURE_HEIGHT/2) + 1; cur_height = 1; } } - ctx.fillRect(f_start, cur_y_center, f_end - f_start, cur_height); + + // Draw whole feature as connector/intron. + if ( this.draw_background_connector ) { + if (mode === "Squish" || mode === "Dense") { + ctx.fillStyle = CONNECTOR_COLOR; + } + else { // mode === "Pack" + if (feature_strand) { + if (feature_strand === "+") { + ctx.fillStyle = ctx.canvas.manager.get_pattern( 'right_strand' ); + } else if (feature_strand === "-") { + ctx.fillStyle = ctx.canvas.manager.get_pattern( 'left_strand' ); + } + } + else { + ctx.fillStyle = CONNECTOR_COLOR; + } + } + ctx.fillRect(f_start, cur_y_center, f_end - f_start, cur_height); + } // Draw blocks. var start_and_height; 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, Math.max((block[1] - tile_low) * w_scale)) ); + block_end = Math.ceil( Math.min(width, Math.max((block[1] - tile_low) * w_scale)) ), + last_block_start, last_block_end; // Skip drawing if block not on tile. if (block_start > block_end) { continue; } @@ -4362,6 +4389,12 @@ extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { ctx.fillRect(block_thick_start, y_center + 1, block_thick_end - block_thick_start, thick_height ); } } + // Draw individual connectors if required + if ( this.draw_individual_connectors && last_block_start ) { + this.draw_connector( ctx, last_block_start, last_block_end, block_start, block_end, y_center ); + } + last_block_start = block_start; + last_block_end = block_end; } // FIXME: Height scaling only works in Pack mode right now. @@ -4699,11 +4732,54 @@ extend(ReadPainter.prototype, FeaturePainter.prototype, { } }); +var ArcLinkedFeaturePainter = function(data, view_start, view_end, prefs, mode, alpha_scaler, height_scaler) { + LinkedFeaturePainter.call(this, data, view_start, view_end, prefs, mode, alpha_scaler, height_scaler); + // Need to know the longest feature length for adding spacing + this.longest_feature_length = this.calculate_longest_feature_length(); + this.draw_background_connector = false; + this.draw_individual_connectors = true; +}; + +extend(ArcLinkedFeaturePainter.prototype, FeaturePainter.prototype, LinkedFeaturePainter.prototype, { + + calculate_longest_feature_length: function () { + var longest_feature_length = 0; + for (var i = 0, len = this.data.length; i < len; i++) { + var feature = this.data[i], feature_start = feature[1], feature_end = feature[2]; + longest_feature_length = Math.max( longest_feature_length, feature_end - feature_start ); + } + return longest_feature_length; + }, + + get_top_padding: function( width ) { + var view_range = this.view_end - this.view_start, + w_scale = width / view_range; + return Math.min( 128, Math.ceil( ( this.longest_feature_length / 2 ) * w_scale ) ); + }, + + draw_connector: function( ctx, block1_start, block1_end, block2_start, block2_end, y_center ) { + // Arc drawing -- from closest endpoints + var x_center = ( block1_end + block2_start ) / 2, + radius = block2_start - x_center; + // For full half circles + var angle1 = Math.PI, angle2 = 0; + if ( radius > 0 ) { + ctx.beginPath(); + ctx.arc( x_center, y_center, block2_start - x_center, Math.PI, 0 ); + ctx.stroke(); + } + } +}); + + + + exports.Scaler = Scaler; exports.SummaryTreePainter = SummaryTreePainter; exports.LinePainter = LinePainter; exports.LinkedFeaturePainter = LinkedFeaturePainter; exports.ReadPainter = ReadPainter; +exports.ArcLinkedFeaturePainter = ArcLinkedFeaturePainter; // End painters_module encapsulation }; From 4464b804e25471edf621980abde77a9fb36b7748 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Tue, 1 Nov 2011 18:56:34 -0400 Subject: [PATCH 06/14] Make default height for LineTracks smaller, 32px seems more reasonable. Possibly it should be larger if there is both positive and negative data? --- static/scripts/trackster.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index ac36d65d95c..daf3b11dd92 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -3031,7 +3031,8 @@ var LineTrack = function (name, view, container, hda_ldda, dataset_id, prefs) { this.min_height_px = 16; this.max_height_px = 400; - this.height_px = 80; + // Default height for new tracks, should be a defined constant? + this.height_px = 32; this.hda_ldda = hda_ldda; this.dataset_id = dataset_id; this.original_dataset_id = dataset_id; From dc24de99a32e1eb018b6c73682901ca9a533d5c1 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Wed, 2 Nov 2011 13:29:26 -0400 Subject: [PATCH 07/14] Trackster: don't import all of numpy in data provider (problem with numpy's log un summary tree handling) --- lib/galaxy/visualization/tracks/data_providers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/visualization/tracks/data_providers.py b/lib/galaxy/visualization/tracks/data_providers.py index ec4a63723a6..612809e569a 100644 --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -10,7 +10,7 @@ if sys.version_info[:2] == (2, 4): pkg_resources.require( "ctypes" ) pkg_resources.require( "pysam" ) pkg_resources.require( "numpy" ) -from numpy import * +import numpy from galaxy.datatypes.util.gff_util import * from galaxy.util.json import from_json_string from bx.interval_index_file import Indexes @@ -743,7 +743,7 @@ class BBIDataProvider( TracksDataProvider ): var = summary.sum_squares[0] - mean if valid_count > 1: var /= valid_count - 1 - sd = sqrt( var ) + sd = numpy.sqrt( var ) return dict( data=dict( min=summary.min_val[0], max=summary.max_val[0], mean=mean, sd=sd ) ) From 2ca322c0e2208a6997876fbb921e809ba730625f Mon Sep 17 00:00:00 2001 From: James Taylor Date: Wed, 2 Nov 2011 13:37:24 -0400 Subject: [PATCH 08/14] Trackster: allow configuration elements to have help text --- static/june_2007_style/base.css.tmpl | 4 ++++ static/june_2007_style/blue/base.css | 1 + static/scripts/trackster.js | 7 ++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/static/june_2007_style/base.css.tmpl b/static/june_2007_style/base.css.tmpl index 998165bb0d7..d53be3086af 100644 --- a/static/june_2007_style/base.css.tmpl +++ b/static/june_2007_style/base.css.tmpl @@ -231,6 +231,10 @@ div.form-row-error-message { padding: 3px 0 0 1em; } +.form-row .help { + color: #666; +} + select, input, textarea { font: inherit; } diff --git a/static/june_2007_style/blue/base.css b/static/june_2007_style/blue/base.css index a6caed06955..70bc7fede74 100644 --- a/static/june_2007_style/blue/base.css +++ b/static/june_2007_style/blue/base.css @@ -43,6 +43,7 @@ div.form-row label.inline{display:inline;} div.form-row-input{float:left;} div.form-row-input label{font-weight:normal;display:inline;} div.form-row-error-message{width:300px;float:left;color:red;font-weight:bold;padding:3px 0 0 1em;} +.form-row .help{color:#666;} select,input,textarea{font:inherit;} select,textarea,input[type="text"],input[type="file"],input[type="password"]{-webkit-box-sizing:border-box;max-width:300px;} .errormessagelarge,.warningmessagelarge,.donemessagelarge,.infomessagelarge{padding:10px;padding-left:52px;min-height:32px;border:1px solid #AA6666;background-color:#FFCCCC;background-image:url(error_message_icon.png);background-repeat:no-repeat;background-position:10px 10px;} diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index daf3b11dd92..ce186cbd681 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2149,6 +2149,10 @@ extend(DrawableConfig.prototype, { else { row.append( $('').attr("id", id ).attr("name", id ).val( value ) ); } + // Help text + if ( param.help ) { + row.append( $("
").text( param.help ) ); + } } }); return container; @@ -3194,7 +3198,8 @@ var FeatureTrack = function(name, view, container, hda_ldda, dataset_id, prefs, { key: 'name', label: 'Name', type: 'text', default_value: name }, { key: 'block_color', label: 'Block color', type: 'color', default_value: get_random_color() }, { key: 'label_color', label: 'Label color', type: 'color', default_value: 'black' }, - { key: 'show_counts', label: 'Show summary counts', type: 'bool', default_value: true }, + { key: 'show_counts', label: 'Show summary counts', type: 'bool', default_value: true, + help: 'Show the number of items in each bin when drawing summary histogram' }, { key: 'mode', type: 'string', default_value: this.mode, hidden: true }, ], saved_values: prefs, From 96cfea923d9f7552d5364afabc8f017ea70d1a7e Mon Sep 17 00:00:00 2001 From: James Taylor Date: Wed, 2 Nov 2011 17:53:00 -0400 Subject: [PATCH 09/14] Trackster: allow track contents to be hidden without removing track. Hidden tracks are condensed and not drawn. TODO: whether a track is hidden is not currently saved. No easy way to inherit prefs right now. --- static/scripts/trackster.js | 51 +++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index ce186cbd681..98c52c4cde8 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2335,6 +2335,8 @@ var Track = function(name, view, container, show_header, prefs, data_url, data_q this.data_url_extra_params = {} this.data_query_wait = (data_query_wait ? data_query_wait : DEFAULT_DATA_QUERY_WAIT); this.dataset_check_url = converted_datasets_state_url; + // FIXME: this should be a saved setting + this.content_visible = true; if (!Track.id_counter) { Track.id_counter = 0; } this.id = Track.id_counter++; @@ -2353,6 +2355,9 @@ var Track = function(name, view, container, show_header, prefs, data_url, data_q this.icons_div = $("
").css("float", "left").appendTo(this.header_div).hide(); // Track icons. + this.toggle_icon = $("").attr("href", "javascript:void(0);").attr("title", "Hide/show track content") + .addClass("icon-button toggle").tipsy( {gravity: 's'} ) + .appendTo(this.icons_div); this.settings_icon = $("").attr("href", "javascript:void(0);").attr("title", "Edit settings") .addClass("icon-button settings-icon").tipsy( {gravity: 's'} ) .appendTo(this.icons_div); @@ -2372,6 +2377,21 @@ var Track = function(name, view, container, show_header, prefs, data_url, data_q // Suppress double clicks in header so that they do not impact viz. this.header_div.dblclick( function(e) { e.stopPropagation(); } ); + + // Toggle icon hides or shows the track content + this.toggle_icon.click( function() { + if ( track.content_visible ) { + track.toggle_icon.addClass("toggle-expand").removeClass("toggle"); + track.hide_contents(); + track.mode_div.hide(); + track.content_visible = false; + } else { + track.toggle_icon.addClass("toggle").removeClass("toggle-expand"); + track.content_visible = true; + track.mode_div.show(); + track.show_contents(); + } + }); // Clicking on settings icon opens track config. this.settings_icon.click( function() { @@ -2558,6 +2578,26 @@ extend(Track.prototype, Drawable.prototype, { this.update_track_icons(); }, + /** + * Hide any elements that are part of the tracks contents area. Should + * remove as approprite, the track will be redrawn by show_contents. + */ + hide_contents : function () { + // Clear contents by removing any elements that are contained in + // the tracks content_div + this.content_div.children().remove(); + // Hide the content div + this.content_div.hide(); + // And any y axis labels (common to several track types) + this.container_div.find(".yaxislabel, .track-resize").hide() + }, + show_contents : function() { + // Show the contents div and labels (if present) + this.content_div.show(); + this.container_div.find(".yaxislabel, .track-resize").show() + // Request a redraw of the content + this.request_draw(); + }, /** * Additional initialization required before drawing track for the first time. */ @@ -2689,6 +2729,11 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, { */ _draw: function(force, clear_after) { if (!this.enabled) { return; } + + // TODO: There should probably be a general way to disable content drawing + // for all drawables. However the button to toggle this is currently + // only present for Track instances. + if (!this.content_visible) { return; } // HACK: ReferenceTrack can draw without dataset ID, but other tracks cannot. if ( !(this instanceof ReferenceTrack) && (!this.dataset_id) ) { return; } @@ -3084,8 +3129,10 @@ extend(LineTrack.prototype, Drawable.prototype, TiledTrack.prototype, { var drag_control = $( "
" ) // Control shows on hover over track, stays while dragging $(track.container_div).hover( function() { - in_handle = true; - drag_control.show(); + if ( track.content_visible ) { + in_handle = true; + drag_control.show(); + } }, function() { in_handle = false; if ( ! in_drag ) { drag_control.hide(); } From 5ea4590857f077c7e88d2c29135bfc828276ec90 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Wed, 2 Nov 2011 18:07:28 -0400 Subject: [PATCH 10/14] Trackster: show color pickers to left of input rather than underneath --- static/scripts/trackster.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index 98c52c4cde8..77c5a61d71a 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2126,7 +2126,7 @@ extend(DrawableConfig.prototype, { } else if ( param.type === 'color' ) { var input = $('').attr("id", id ).attr("name", id ).val( value ); // Color picker in tool tip style float - var tip = $( "
" ).hide(); + var tip = $( "
" ).hide(); // Inner div for padding purposes var tip_inner = $("
").appendTo(tip); var farb_container = $("
") @@ -2136,8 +2136,10 @@ extend(DrawableConfig.prototype, { // Outer div container input and tip for hover to work $("
").append( input ).append( tip ).appendTo( row ).bind( "click", function ( e ) { tip.css( { - left: $(this).position().left + ( $(input).width() / 2 ) - 60, - top: $(this).position().top + $(this.height) + // left: $(this).position().left + ( $(input).width() / 2 ) - 60, + // top: $(this).position().top + $(this.height) + left: $(this).position().left + $(input).width() + 5, + top: $(this).position().top - ( $(tip).height() / 2 ) + ( $(input).height() / 2 ) } ).show(); $(document).bind( "click.color-picker", function() { tip.hide(); From e669a876aa910a8022d9424c7db47d9646d46763 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Sat, 5 Nov 2011 14:55:11 -0400 Subject: [PATCH 11/14] Trackster: making config form generation for parameters a function so it can be used recursively (for conditional configuration, not yet implemented) --- static/scripts/trackster.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index 77c5a61d71a..89a13f06f71 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2113,8 +2113,14 @@ extend(DrawableConfig.prototype, { build_form: function() { var track_config = this; var container = $("
"); - $.each( this.params, function( index, param ) { - if ( ! param.hidden ) { + var param; + // Function to process parameters recursively + function handle_params( params, container ) { + for ( var index = 0; index < params.length; index++ ) { + param = params[index]; + // Hidden params have no representation in the form + if ( param.hidden ) { continue; } + // Build row for param var id = 'param_' + index; var value = track_config.values[ param.key ]; var row = $("
").appendTo( container ); @@ -2132,7 +2138,6 @@ extend(DrawableConfig.prototype, { var farb_container = $("
") .appendTo(tip_inner) .farbtastic( { width: 100, height: 100, callback: input, color: value }); - // Outer div container input and tip for hover to work $("
").append( input ).append( tip ).appendTo( row ).bind( "click", function ( e ) { tip.css( { @@ -2156,7 +2161,10 @@ extend(DrawableConfig.prototype, { row.append( $("
").text( param.help ) ); } } - }); + } + // Handle top level parameters in order + handle_params( this.params, container ); + // Return element containing constructed form return container; }, update_from_form: function( container ) { From 43ec3def31bc341eb3c673ca2655e5fdf40168d4 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Mon, 7 Nov 2011 10:21:17 -0500 Subject: [PATCH 12/14] Trackster: use hover event for feature track popups --- eggs.ini | 4 ++-- static/scripts/trackster.js | 11 ++++++++++- templates/tracks/browser.mako | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/eggs.ini b/eggs.ini index 82c12aad70b..7bec53d8b6f 100644 --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ repository = http://eggs.g2.bx.psu.edu no_auto = pbs_python DRMAA_python [eggs:platform] -bx_python = 0.7.1 +bx_python = 0.7.0 Cheetah = 2.2.2 ctypes = 1.0.2 DRMAA_python = 0.2 @@ -67,7 +67,7 @@ Whoosh = 0.3.18 psycopg2 = _8.4.2_static pysqlite = _3.6.17_static MySQL_python = _5.1.41_static -; bx_python = _494c2d1d68b3_rebuild1 +bx_python = _494c2d1d68b3_rebuild1 GeneTrack = _dev_48da9e998f0caf01c5be731e926f4b0481f658f0 SQLAlchemy = _dev_r6498 pysam = _kanwei_b10f6e722e9a diff --git a/static/scripts/trackster.js b/static/scripts/trackster.js index 89a13f06f71..71185763fa1 100644 --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2242,7 +2242,16 @@ FeatureTrackTile.prototype.predisplay_actions = function() { // Only show popups in Pack mode. if (tile.mode !== "Pack") { return; } - $(this.canvas).mousemove(function (e) { + $(this.canvas).hover( function() { + this.hovered = true; + $(this).mousemove(); + }, function() { + this.hovered = false; + // Clear popup if it is still hanging around (this is probably not needed) + $(this).siblings(".feature-popup").remove(); + } ).mousemove(function (e) { + // Use the hover plugin to get a delay before showing popup + if ( !this.hovered ) { return; } // Get feature data for position. var this_offset = $(this).offset(), diff --git a/templates/tracks/browser.mako b/templates/tracks/browser.mako index 1799b085f00..fa7cc535a01 100644 --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -42,7 +42,7 @@ ${parent.javascripts()} -${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.mousewheel", "jquery.autocomplete", "trackster", "trackster_ui", "jquery.ui.sortable.slider", "jquery.scrollTo", "farbtastic", "jquery.tipsy" )} +${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.event.hover", "jquery.event.hover", "jquery.mousewheel", "jquery.autocomplete", "trackster", "trackster_ui", "jquery.ui.sortable.slider", "jquery.scrollTo", "farbtastic", "jquery.tipsy" )}