24 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/changeset/471c9a7d9ed2/ changeset: 471c9a7d9ed2 user: james_taylor date: 2011-10-30 23:12:22 summary: 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) affected #: 6 files diff -r 147f55e7161ceb4ea9fd3d08bd5dedd19fccbec0 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 static/june_2007_style/blue/panel_layout.css --- a/static/june_2007_style/blue/panel_layout.css +++ b/static/june_2007_style/blue/panel_layout.css @@ -23,9 +23,11 @@ .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 -r 147f55e7161ceb4ea9fd3d08bd5dedd19fccbec0 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 static/june_2007_style/blue/trackster.css --- 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 @@ .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 -r 147f55e7161ceb4ea9fd3d08bd5dedd19fccbec0 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 static/june_2007_style/panel_layout.css.tmpl --- a/static/june_2007_style/panel_layout.css.tmpl +++ b/static/june_2007_style/panel_layout.css.tmpl @@ -161,6 +161,10 @@ 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 @@ z-index: 80000; } +#overlay.modal .dialog-box .body { + min-width: 600px; +} + .dialog-box .body { padding: 5px; overflow: auto; diff -r 147f55e7161ceb4ea9fd3d08bd5dedd19fccbec0 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 static/june_2007_style/trackster.css.tmpl --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,8 @@ background-color: #CCC; padding: 1em; } -.feature-popup{ +.feature-popup { background-color: #DDD; position: absolute; - z-index: 1000 + z-index: 1000; } diff -r 147f55e7161ceb4ea9fd3d08bd5dedd19fccbec0 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 static/scripts/galaxy.panels.js --- a/static/scripts/galaxy.panels.js +++ b/static/scripts/galaxy.panels.js @@ -163,13 +163,23 @@ // 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 @@ $( ".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 -r 147f55e7161ceb4ea9fd3d08bd5dedd19fccbec0 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 templates/workflow/editor.mako --- 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(); https://bitbucket.org/galaxy/galaxy-central/changeset/f5961b41a0d4/ changeset: f5961b41a0d4 user: james_taylor date: 2011-10-30 23:59:05 summary: Trackster: fixes for bigwig data -- updated bx-python affected #: 5 files diff -r 471c9a7d9ed2eca4110bf59961ce21307344a982 -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ 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 @@ 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 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py --- 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 @@ st.write(out_fname) if __name__ == "__main__": - main() \ No newline at end of file + main() diff -r 471c9a7d9ed2eca4110bf59961ce21307344a982 -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 lib/galaxy/visualization/tracks/data_providers.py --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -434,40 +434,55 @@ # 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() + + result = [] + + if summary: + mean = summary.sum_data / summary.valid_count - pos = start - step_size = (end - start) / num_points - result = [] - if data: - for dat_dict in data: - result.append( (pos, float_nan(dat_dict['mean']) ) ) + 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 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -524,6 +524,7 @@ 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 @@ 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 -r 471c9a7d9ed2eca4110bf59961ce21307344a982 -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -699,7 +699,7 @@ 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 @@ 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 @@ w_scale = width / range, resolution = this.view.resolution, parent_element = $("<div style='position: relative;'></div>"); - + // For overview, adjust high, low, resolution, and w_scale. if (this.is_overview) { low = this.view.max_low; @@ -4759,4 +4759,4 @@ for ( key in modules.trackster ) { target[key] = modules.trackster[key]; } -})(window); \ No newline at end of file +})(window); https://bitbucket.org/galaxy/galaxy-central/changeset/aa241b7e0138/ changeset: aa241b7e0138 user: james_taylor date: 2011-10-31 23:06:30 summary: Trackster: for line tracks, compute default range using +/- two standard deviations around mean, for feature tracks, make feature popup look more like other tooltips affected #: 4 files diff -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 -r aa241b7e0138f5f107219666dc2e21daa76f3c87 lib/galaxy/visualization/tracks/data_providers.py --- 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 @@ # 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 @@ 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 -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 -r aa241b7e0138f5f107219666dc2e21daa76f3c87 static/june_2007_style/blue/trackster.css --- a/static/june_2007_style/blue/trackster.css +++ b/static/june_2007_style/blue/trackster.css @@ -87,4 +87,5 @@ .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 -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 -r aa241b7e0138f5f107219666dc2e21daa76f3c87 static/june_2007_style/trackster.css.tmpl --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,19 @@ 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 -r f5961b41a0d4a7c7b1f21dc8829644573c9e5448 -r aa241b7e0138f5f107219666dc2e21daa76f3c87 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2268,8 +2268,8 @@ // Build popup. var popup = $("<div/>").attr("id", feature_uid).addClass("feature-popup"), - key, value, - table = $("<table/>").appendTo(popup), row; + table = $("<table/>"), + key, value, row; for (key in feature_dict) { value = feature_dict[key]; row = $("<tr/>").appendTo(table); @@ -2277,6 +2277,7 @@ $("<td/>").attr("align", "left").appendTo(row) .text(typeof(value) == 'number' ? round(value, 2) : value); } + popup.append( $("<div class='feature-popup-inner'>").append( table ) ); popups[feature_uid] = popup; } @@ -2287,7 +2288,7 @@ // 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 @@ 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); } https://bitbucket.org/galaxy/galaxy-central/changeset/6db378a732b3/ changeset: 6db378a732b3 user: james_taylor date: 2011-10-31 23:06:39 summary: Automated merge with https://bitbucket.org/galaxy/galaxy-central affected #: 11 files diff -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ 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 @@ 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 -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py --- 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 @@ st.write(out_fname) if __name__ == "__main__": - main() \ No newline at end of file + main() diff -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 lib/galaxy/visualization/tracks/data_providers.py --- 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): @@ -434,40 +434,68 @@ # 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 values 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: + # 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) + + # 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() + + result = [] + + if summary: + mean = summary.sum_data / summary.valid_count - pos = start - step_size = (end - start) / num_points - result = [] - if data: - for dat_dict in data: - result.append( (pos, float_nan(dat_dict['mean']) ) ) + 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 -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -524,6 +524,7 @@ 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 @@ 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 -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 static/june_2007_style/blue/panel_layout.css --- a/static/june_2007_style/blue/panel_layout.css +++ b/static/june_2007_style/blue/panel_layout.css @@ -23,9 +23,11 @@ .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 -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 static/june_2007_style/blue/trackster.css --- 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,5 @@ .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 -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 static/june_2007_style/panel_layout.css.tmpl --- a/static/june_2007_style/panel_layout.css.tmpl +++ b/static/june_2007_style/panel_layout.css.tmpl @@ -161,6 +161,10 @@ 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 @@ z-index: 80000; } +#overlay.modal .dialog-box .body { + min-width: 600px; +} + .dialog-box .body { padding: 5px; overflow: auto; diff -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 static/june_2007_style/trackster.css.tmpl --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,19 @@ background-color: #CCC; padding: 1em; } -.feature-popup{ - background-color: #DDD; + +.feature-popup { position: absolute; - z-index: 1000 + 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 -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 static/scripts/galaxy.panels.js --- a/static/scripts/galaxy.panels.js +++ b/static/scripts/galaxy.panels.js @@ -163,13 +163,23 @@ // 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 @@ $( ".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 -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -699,7 +699,7 @@ 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 @@ 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(); }); @@ -2268,8 +2268,8 @@ // Build popup. var popup = $("<div/>").attr("id", feature_uid).addClass("feature-popup"), - key, value, - table = $("<table/>").appendTo(popup), row; + table = $("<table/>"), + key, value, row; for (key in feature_dict) { value = feature_dict[key]; row = $("<tr/>").appendTo(table); @@ -2277,6 +2277,7 @@ $("<td/>").attr("align", "left").appendTo(row) .text(typeof(value) == 'number' ? round(value, 2) : value); } + popup.append( $("<div class='feature-popup-inner'>").append( table ) ); popups[feature_uid] = popup; } @@ -2287,7 +2288,7 @@ // 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") } @@ -2699,7 +2700,7 @@ w_scale = width / range, resolution = this.view.resolution, parent_element = $("<div style='position: relative;'></div>"); - + // For overview, adjust high, low, resolution, and w_scale. if (this.is_overview) { low = this.view.max_low; @@ -3080,9 +3081,20 @@ 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); } @@ -4721,4 +4733,4 @@ for ( key in modules.trackster ) { target[key] = modules.trackster[key]; } -})(window); \ No newline at end of file +})(window); diff -r ef39a111210018fb50282a352182b298c23be8f7 -r 6db378a732b382143bf2c8992d298ab278ffc8f9 templates/workflow/editor.mako --- 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(); https://bitbucket.org/galaxy/galaxy-central/changeset/19fd9fccebb9/ changeset: 19fd9fccebb9 user: james_taylor date: 2011-10-31 23:31:15 summary: Trackster: faster x axis drag/wheel scrolling, seems to work across browsers, but hard to tell across platforms, report your experiences affected #: 1 file diff -r 6db378a732b382143bf2c8992d298ab278ffc8f9 -r 19fd9fccebb9af1150ab409da94f74bdf2246fb6 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -1024,6 +1024,7 @@ // 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 ); } https://bitbucket.org/galaxy/galaxy-central/changeset/89b2abe925ba/ changeset: 89b2abe925ba user: james_taylor date: 2011-11-01 19:54:11 summary: 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. affected #: 2 files diff -r 19fd9fccebb9af1150ab409da94f74bdf2246fb6 -r 89b2abe925baa41872656df183125d5185004111 lib/galaxy/visualization/tracks/data_providers.py --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -10,6 +10,7 @@ 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 @@ 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 -r 19fd9fccebb9af1150ab409da94f74bdf2246fb6 -r 89b2abe925baa41872656df183125d5185004111 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -3512,7 +3512,7 @@ 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 @@ this.feature_positions = {}; this.slot_height = slot_height; this.translation = 0; + this.y_translation = 0; }; /** @@ -4099,7 +4100,7 @@ */ 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 @@ 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 @@ } ctx.restore(); + feature_mapper.y_translation = this.get_top_padding(width); return feature_mapper; }, /** @@ -4200,6 +4210,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 @@ 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 @@ // 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 @@ 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 @@ } }); +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 }; https://bitbucket.org/galaxy/galaxy-central/changeset/0f5d6b2b13f7/ changeset: 0f5d6b2b13f7 user: james_taylor date: 2011-11-01 19:54:25 summary: Automated merge with https://bitbucket.org/galaxy/galaxy-central affected #: 11 files diff -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ 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 @@ 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 -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py --- 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 @@ st.write(out_fname) if __name__ == "__main__": - main() \ No newline at end of file + main() diff -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc lib/galaxy/visualization/tracks/data_providers.py --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -2,14 +2,15 @@ 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): 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 @@ -720,40 +721,73 @@ # 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 values 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: + # 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) + + # 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() + + result = [] + + 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 - result = [] - if data: - for dat_dict in data: - result.append( (pos, float_nan(dat_dict['mean']) ) ) + pos = start + step_size = (end - start) / num_points + + for i in range( num_points ): + result.append( (pos, float_nan( mean[i] ) ) ) pos += step_size return { 'data': result } diff -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -528,6 +528,7 @@ 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 } @@ -1041,4 +1042,4 @@ 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 -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc static/june_2007_style/blue/panel_layout.css --- a/static/june_2007_style/blue/panel_layout.css +++ b/static/june_2007_style/blue/panel_layout.css @@ -23,9 +23,11 @@ .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 -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc static/june_2007_style/blue/trackster.css --- 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,5 @@ .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 -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc static/june_2007_style/panel_layout.css.tmpl --- a/static/june_2007_style/panel_layout.css.tmpl +++ b/static/june_2007_style/panel_layout.css.tmpl @@ -161,6 +161,10 @@ 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 @@ z-index: 80000; } +#overlay.modal .dialog-box .body { + min-width: 600px; +} + .dialog-box .body { padding: 5px; overflow: auto; diff -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc static/june_2007_style/trackster.css.tmpl --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,19 @@ background-color: #CCC; padding: 1em; } -.feature-popup{ - background-color: #DDD; + +.feature-popup { position: absolute; - z-index: 1000 + 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 -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc static/scripts/galaxy.panels.js --- a/static/scripts/galaxy.panels.js +++ b/static/scripts/galaxy.panels.js @@ -163,13 +163,23 @@ // 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 @@ $( ".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 -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -699,7 +699,7 @@ 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(); @@ -1024,6 +1024,7 @@ // 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 ); } @@ -1253,7 +1254,7 @@ 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(); }); @@ -2268,8 +2269,8 @@ // Build popup. var popup = $("<div/>").attr("id", feature_uid).addClass("feature-popup"), - key, value, - table = $("<table/>").appendTo(popup), row; + table = $("<table/>"), + key, value, row; for (key in feature_dict) { value = feature_dict[key]; row = $("<tr/>").appendTo(table); @@ -2277,6 +2278,7 @@ $("<td/>").attr("align", "left").appendTo(row) .text(typeof(value) == 'number' ? round(value, 2) : value); } + popup.append( $("<div class='feature-popup-inner'>").append( table ) ); popups[feature_uid] = popup; } @@ -2287,7 +2289,7 @@ // 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") } @@ -2695,7 +2697,7 @@ w_scale = width / range, resolution = this.view.resolution, parent_element = $("<div style='position: relative;'></div>"); - + // For overview, adjust high, low, resolution, and w_scale. if (this.is_overview) { low = this.view.max_low; @@ -3109,9 +3111,20 @@ 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); } @@ -3528,7 +3541,7 @@ 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; @@ -4054,6 +4067,7 @@ this.feature_positions = {}; this.slot_height = slot_height; this.translation = 0; + this.y_translation = 0; }; /** @@ -4075,7 +4089,7 @@ */ 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. @@ -4105,15 +4119,23 @@ 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 @@ -4150,6 +4172,7 @@ } ctx.restore(); + feature_mapper.y_translation = this.get_top_padding(width); return feature_mapper; }, /** @@ -4176,6 +4199,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, { @@ -4213,7 +4240,7 @@ 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, @@ -4276,38 +4303,51 @@ // 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; } @@ -4338,6 +4378,12 @@ 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. @@ -4675,11 +4721,54 @@ } }); +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 }; @@ -4710,4 +4799,4 @@ for ( key in modules.trackster ) { target[key] = modules.trackster[key]; } -})(window); \ No newline at end of file +})(window); diff -r 33e2ae31dddd0f494841aaa7f408aac9312b031b -r 0f5d6b2b13f78e164550420d620e569e681ed8bc templates/workflow/editor.mako --- 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(); https://bitbucket.org/galaxy/galaxy-central/changeset/ebbf0f5e259c/ changeset: ebbf0f5e259c user: james_taylor date: 2011-11-01 23:56:34 summary: Make default height for LineTracks smaller, 32px seems more reasonable. Possibly it should be larger if there is both positive and negative data? affected #: 1 file diff -r 0f5d6b2b13f78e164550420d620e569e681ed8bc -r ebbf0f5e259cae099fcdcd6ac92697869b3b4bca static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -3031,7 +3031,8 @@ 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; https://bitbucket.org/galaxy/galaxy-central/changeset/ee0b898a2604/ changeset: ee0b898a2604 user: james_taylor date: 2011-11-02 18:29:26 summary: Trackster: don't import all of numpy in data provider (problem with numpy's log un summary tree handling) affected #: 1 file diff -r ebbf0f5e259cae099fcdcd6ac92697869b3b4bca -r ee0b898a2604ce4c1896059fb56147be3ea2a674 lib/galaxy/visualization/tracks/data_providers.py --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -10,7 +10,7 @@ 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 @@ 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 ) ) https://bitbucket.org/galaxy/galaxy-central/changeset/5d3b2955f12e/ changeset: 5d3b2955f12e user: james_taylor date: 2011-11-02 18:37:24 summary: Trackster: allow configuration elements to have help text affected #: 3 files diff -r ee0b898a2604ce4c1896059fb56147be3ea2a674 -r 5d3b2955f12e08067d9a1ecd3f110777c7a83caf static/june_2007_style/base.css.tmpl --- a/static/june_2007_style/base.css.tmpl +++ b/static/june_2007_style/base.css.tmpl @@ -231,6 +231,10 @@ padding: 3px 0 0 1em; } +.form-row .help { + color: #666; +} + select, input, textarea { font: inherit; } diff -r ee0b898a2604ce4c1896059fb56147be3ea2a674 -r 5d3b2955f12e08067d9a1ecd3f110777c7a83caf static/june_2007_style/blue/base.css --- a/static/june_2007_style/blue/base.css +++ b/static/june_2007_style/blue/base.css @@ -43,6 +43,7 @@ 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 -r ee0b898a2604ce4c1896059fb56147be3ea2a674 -r 5d3b2955f12e08067d9a1ecd3f110777c7a83caf static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2149,6 +2149,10 @@ else { row.append( $('<input />').attr("id", id ).attr("name", id ).val( value ) ); } + // Help text + if ( param.help ) { + row.append( $("<div class='help'/>").text( param.help ) ); + } } }); return container; @@ -3194,7 +3198,8 @@ { 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, https://bitbucket.org/galaxy/galaxy-central/changeset/5027d6f9ca30/ changeset: 5027d6f9ca30 user: james_taylor date: 2011-11-02 22:53:00 summary: 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. affected #: 1 file diff -r 5d3b2955f12e08067d9a1ecd3f110777c7a83caf -r 5027d6f9ca30432d0dc09d8e7598c1679d75aaed static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2335,6 +2335,8 @@ 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 @@ this.icons_div = $("<div/>").css("float", "left").appendTo(this.header_div).hide(); // Track icons. + this.toggle_icon = $("<a/>").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 = $("<a/>").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 @@ // 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() { @@ -2559,6 +2579,26 @@ 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. */ predraw_init: function() {} @@ -2689,6 +2729,11 @@ */ _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 @@ var drag_control = $( "<div class='track-resize'>" ) // 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(); } https://bitbucket.org/galaxy/galaxy-central/changeset/b3900db4f77c/ changeset: b3900db4f77c user: james_taylor date: 2011-11-02 23:07:28 summary: Trackster: show color pickers to left of input rather than underneath affected #: 1 file diff -r 5027d6f9ca30432d0dc09d8e7598c1679d75aaed -r b3900db4f77c46687ae8aa2eb81a7aeaf7bfa751 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2126,7 +2126,7 @@ } else if ( param.type === 'color' ) { var input = $('<input />').attr("id", id ).attr("name", id ).val( value ); // Color picker in tool tip style float - var tip = $( "<div class='tipsy tipsy-north' style='position: absolute;' />" ).hide(); + var tip = $( "<div class='tipsy tipsy-west' style='position: absolute;' />" ).hide(); // Inner div for padding purposes var tip_inner = $("<div style='background-color: black; padding: 10px;'></div>").appendTo(tip); var farb_container = $("<div/>") @@ -2136,8 +2136,10 @@ // Outer div container input and tip for hover to work $("<div />").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(); https://bitbucket.org/galaxy/galaxy-central/changeset/62b7482496c2/ changeset: 62b7482496c2 user: james_taylor date: 2011-11-05 19:55:11 summary: Trackster: making config form generation for parameters a function so it can be used recursively (for conditional configuration, not yet implemented) affected #: 1 file diff -r b3900db4f77c46687ae8aa2eb81a7aeaf7bfa751 -r 62b7482496c231f3ba0ea6f5682692ccc242fa14 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2113,8 +2113,14 @@ build_form: function() { var track_config = this; var container = $("<div />"); - $.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 = $("<div class='form-row' />").appendTo( container ); @@ -2132,7 +2138,6 @@ var farb_container = $("<div/>") .appendTo(tip_inner) .farbtastic( { width: 100, height: 100, callback: input, color: value }); - // Outer div container input and tip for hover to work $("<div />").append( input ).append( tip ).appendTo( row ).bind( "click", function ( e ) { tip.css( { @@ -2156,7 +2161,10 @@ row.append( $("<div class='help'/>").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 ) { https://bitbucket.org/galaxy/galaxy-central/changeset/4db2b2230f7e/ changeset: 4db2b2230f7e user: james_taylor date: 2011-11-07 16:21:17 summary: Trackster: use hover event for feature track popups affected #: 3 files diff -r 62b7482496c231f3ba0ea6f5682692ccc242fa14 -r 4db2b2230f7e1dc075f696a4c6083956da02a4c4 eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ 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 @@ 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 -r 62b7482496c231f3ba0ea6f5682692ccc242fa14 -r 4db2b2230f7e1dc075f696a4c6083956da02a4c4 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2242,7 +2242,16 @@ // 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 -r 62b7482496c231f3ba0ea6f5682692ccc242fa14 -r 4db2b2230f7e1dc075f696a4c6083956da02a4c4 templates/tracks/browser.mako --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -42,7 +42,7 @@ <script type='text/javascript' src="${h.url_for('/static/scripts/excanvas.js')}"></script><![endif]--> -${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.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" )} <script type="text/javascript"> // https://bitbucket.org/galaxy/galaxy-central/changeset/4e9db728d9ea/ changeset: 4e9db728d9ea user: james_taylor date: 2011-11-07 16:27:50 summary: merge affected #: 14 files diff -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py --- 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 @@ st.write(out_fname) if __name__ == "__main__": - main() \ No newline at end of file + main() diff -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a lib/galaxy/visualization/tracks/data_providers.py --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -2,14 +2,15 @@ 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): pkg_resources.require( "ctypes" ) pkg_resources.require( "pysam" ) pkg_resources.require( "numpy" ) +import numpy from galaxy.datatypes.util.gff_util import * from galaxy.util.json import from_json_string from bx.interval_index_file import Indexes @@ -740,40 +741,73 @@ # 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 values 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: + # 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 = numpy.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) + + # 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() + + result = [] + + 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 - result = [] - if data: - for dat_dict in data: - result.append( (pos, float_nan(dat_dict['mean']) ) ) + pos = start + step_size = (end - start) / num_points + + for i in range( num_points ): + result.append( (pos, float_nan( mean[i] ) ) ) pos += step_size return { 'data': result } diff -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -528,6 +528,7 @@ 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 } @@ -1041,4 +1042,4 @@ 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 -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a static/june_2007_style/base.css.tmpl --- a/static/june_2007_style/base.css.tmpl +++ b/static/june_2007_style/base.css.tmpl @@ -231,6 +231,10 @@ padding: 3px 0 0 1em; } +.form-row .help { + color: #666; +} + select, input, textarea { font: inherit; } diff -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a static/june_2007_style/blue/base.css --- a/static/june_2007_style/blue/base.css +++ b/static/june_2007_style/blue/base.css @@ -43,6 +43,7 @@ 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 -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a static/june_2007_style/blue/panel_layout.css --- a/static/june_2007_style/blue/panel_layout.css +++ b/static/june_2007_style/blue/panel_layout.css @@ -23,9 +23,11 @@ .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 -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a static/june_2007_style/blue/trackster.css --- 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,5 @@ .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 -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a static/june_2007_style/panel_layout.css.tmpl --- a/static/june_2007_style/panel_layout.css.tmpl +++ b/static/june_2007_style/panel_layout.css.tmpl @@ -161,6 +161,10 @@ 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 @@ z-index: 80000; } +#overlay.modal .dialog-box .body { + min-width: 600px; +} + .dialog-box .body { padding: 5px; overflow: auto; diff -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a static/june_2007_style/trackster.css.tmpl --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,19 @@ background-color: #CCC; padding: 1em; } -.feature-popup{ - background-color: #DDD; + +.feature-popup { position: absolute; - z-index: 1000 + 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 -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a static/scripts/galaxy.panels.js --- a/static/scripts/galaxy.panels.js +++ b/static/scripts/galaxy.panels.js @@ -163,13 +163,23 @@ // 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 @@ $( ".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 -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -701,7 +701,7 @@ 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(); @@ -1027,6 +1027,7 @@ // 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 ); } @@ -1255,7 +1256,7 @@ 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(); }); @@ -2114,8 +2115,14 @@ build_form: function() { var track_config = this; var container = $("<div />"); - $.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 = $("<div class='form-row' />").appendTo( container ); @@ -2127,18 +2134,19 @@ } else if ( param.type === 'color' ) { var input = $('<input />').attr("id", id ).attr("name", id ).val( value ); // Color picker in tool tip style float - var tip = $( "<div class='tipsy tipsy-north' style='position: absolute;' />" ).hide(); + var tip = $( "<div class='tipsy tipsy-west' style='position: absolute;' />" ).hide(); // Inner div for padding purposes var tip_inner = $("<div style='background-color: black; padding: 10px;'></div>").appendTo(tip); var farb_container = $("<div/>") .appendTo(tip_inner) .farbtastic( { width: 100, height: 100, callback: input, color: value }); - // Outer div container input and tip for hover to work $("<div />").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(); @@ -2150,8 +2158,15 @@ else { row.append( $('<input />').attr("id", id ).attr("name", id ).val( value ) ); } + // Help text + if ( param.help ) { + row.append( $("<div class='help'/>").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 ) { @@ -2229,7 +2244,16 @@ // 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(), @@ -2270,8 +2294,8 @@ // Build popup. var popup = $("<div/>").attr("id", feature_uid).addClass("feature-popup"), - key, value, - table = $("<table/>").appendTo(popup), row; + table = $("<table/>"), + key, value, row; for (key in feature_dict) { value = feature_dict[key]; row = $("<tr/>").appendTo(table); @@ -2279,6 +2303,7 @@ $("<td/>").attr("align", "left").appendTo(row) .text(typeof(value) == 'number' ? round(value, 2) : value); } + popup.append( $("<div class='feature-popup-inner'>").append( table ) ); popups[feature_uid] = popup; } @@ -2289,7 +2314,7 @@ // 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") } @@ -2331,6 +2356,12 @@ 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++; // // Create HTML element structure for track. @@ -2346,6 +2377,9 @@ this.icons_div = $("<div/>").css("float", "left").appendTo(this.header_div).hide(); // Track icons. + this.toggle_icon = $("<a/>").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 = $("<a/>").attr("href", "javascript:void(0);").attr("title", "Edit settings") .addClass("icon-button settings-icon").tipsy( {gravity: 's'} ) .appendTo(this.icons_div); @@ -2365,6 +2399,21 @@ // 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() { @@ -2552,6 +2601,26 @@ this.update_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. */ predraw_init: function() {} @@ -2682,6 +2751,11 @@ */ _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; } @@ -2694,7 +2768,7 @@ w_scale = width / range, resolution = this.view.resolution, parent_element = $("<div style='position: relative;'></div>"); - + // For overview, adjust high, low, resolution, and w_scale. if (this.is_overview) { low = this.view.max_low; @@ -3028,7 +3102,8 @@ 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; @@ -3076,8 +3151,10 @@ var drag_control = $( "<div class='track-resize'>" ) // 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(); } @@ -3108,9 +3185,20 @@ 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); } @@ -3179,7 +3267,8 @@ { 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, @@ -3527,7 +3616,7 @@ 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; @@ -4057,6 +4146,7 @@ this.feature_positions = {}; this.slot_height = slot_height; this.translation = 0; + this.y_translation = 0; }; /** @@ -4078,7 +4168,7 @@ */ 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. @@ -4108,15 +4198,23 @@ 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 @@ -4153,6 +4251,7 @@ } ctx.restore(); + feature_mapper.y_translation = this.get_top_padding(width); return feature_mapper; }, /** @@ -4179,6 +4278,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, { @@ -4216,7 +4319,7 @@ 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, @@ -4279,38 +4382,51 @@ // 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; } @@ -4341,6 +4457,12 @@ 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. @@ -4673,11 +4795,54 @@ } }); +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 }; @@ -4708,4 +4873,4 @@ for ( key in modules.trackster ) { target[key] = modules.trackster[key]; } -})(window); \ No newline at end of file +})(window); diff -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a templates/tracks/browser.mako --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -42,7 +42,7 @@ <script type='text/javascript' src="${h.url_for('/static/scripts/excanvas.js')}"></script><![endif]--> -${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.mousewheel", "jquery.autocomplete", "trackster", "trackster_ui", "jquery.ui.sortable.slider", "farbtastic", "jquery.tipsy" )} +${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.event.hover","jquery.mousewheel", "jquery.autocomplete", "trackster", "trackster_ui", "jquery.ui.sortable.slider", "farbtastic", "jquery.tipsy" )} <script type="text/javascript"> // diff -r 545288e40ee10ed48d422eb772b734ace1422451 -r 4e9db728d9ea2553f387b53e6d25230b69a2789a templates/workflow/editor.mako --- 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(); https://bitbucket.org/galaxy/galaxy-central/changeset/9c80e9985d71/ changeset: 9c80e9985d71 user: james_taylor date: 2011-11-07 16:32:18 summary: Move bx-python version to 0.7.1 (again) affected #: 1 file diff -r 4db2b2230f7e1dc075f696a4c6083956da02a4c4 -r 9c80e9985d71939dfcda96a1c21c9be85983620f eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ 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 @@ 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 https://bitbucket.org/galaxy/galaxy-central/changeset/7e45efd017bd/ changeset: 7e45efd017bd user: james_taylor date: 2011-11-07 16:32:53 summary: Automated merge with ssh://bitbucket.org/james_taylor/galaxy-central affected #: 1 file diff -r 4e9db728d9ea2553f387b53e6d25230b69a2789a -r 7e45efd017bdb4d6ea0af86e80b3ae9d2a029c67 eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ 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 @@ 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 https://bitbucket.org/galaxy/galaxy-central/changeset/54f19d6736c0/ changeset: 54f19d6736c0 user: james_taylor date: 2011-11-07 21:31:32 summary: Eggs: add revision string for new bx-python affected #: 1 file diff -r 7e45efd017bdb4d6ea0af86e80b3ae9d2a029c67 -r 54f19d6736c0d5f55a841b76f4a72e32e490df38 eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -67,7 +67,7 @@ psycopg2 = _8.4.2_static pysqlite = _3.6.17_static MySQL_python = _5.1.41_static -; bx_python = _494c2d1d68b3_rebuild1 +bx_python = _7b95ff194725 GeneTrack = _dev_48da9e998f0caf01c5be731e926f4b0481f658f0 SQLAlchemy = _dev_r6498 pysam = _kanwei_b10f6e722e9a https://bitbucket.org/galaxy/galaxy-central/changeset/de594b3589e5/ changeset: de594b3589e5 user: james_taylor date: 2011-11-07 21:31:41 summary: Automated merge with https://bitbucket.org/galaxy/galaxy-central affected #: 14 files diff -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ 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 @@ psycopg2 = _8.4.2_static pysqlite = _3.6.17_static MySQL_python = _5.1.41_static -bx_python = _494c2d1d68b3_rebuild1 +bx_python = _7b95ff194725 GeneTrack = _dev_48da9e998f0caf01c5be731e926f4b0481f658f0 SQLAlchemy = _dev_r6498 pysam = _kanwei_b10f6e722e9a diff -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py --- 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 @@ st.write(out_fname) if __name__ == "__main__": - main() \ No newline at end of file + main() diff -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd lib/galaxy/visualization/tracks/data_providers.py --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -2,14 +2,15 @@ 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): pkg_resources.require( "ctypes" ) pkg_resources.require( "pysam" ) pkg_resources.require( "numpy" ) +import numpy from galaxy.datatypes.util.gff_util import * from galaxy.util.json import from_json_string from bx.interval_index_file import Indexes @@ -740,40 +741,73 @@ # 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 values 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: + # 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 = numpy.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) + + # 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() + + result = [] + + 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 - result = [] - if data: - for dat_dict in data: - result.append( (pos, float_nan(dat_dict['mean']) ) ) + pos = start + step_size = (end - start) / num_points + + for i in range( num_points ): + result.append( (pos, float_nan( mean[i] ) ) ) pos += step_size return { 'data': result } diff -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -528,6 +528,7 @@ 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 } @@ -1041,4 +1042,4 @@ 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 -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd static/june_2007_style/base.css.tmpl --- a/static/june_2007_style/base.css.tmpl +++ b/static/june_2007_style/base.css.tmpl @@ -231,6 +231,10 @@ padding: 3px 0 0 1em; } +.form-row .help { + color: #666; +} + select, input, textarea { font: inherit; } diff -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd static/june_2007_style/blue/base.css --- a/static/june_2007_style/blue/base.css +++ b/static/june_2007_style/blue/base.css @@ -43,6 +43,7 @@ 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 -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd static/june_2007_style/blue/panel_layout.css --- a/static/june_2007_style/blue/panel_layout.css +++ b/static/june_2007_style/blue/panel_layout.css @@ -23,9 +23,11 @@ .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 -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd static/june_2007_style/blue/trackster.css --- 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,5 @@ .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 -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd static/june_2007_style/panel_layout.css.tmpl --- a/static/june_2007_style/panel_layout.css.tmpl +++ b/static/june_2007_style/panel_layout.css.tmpl @@ -161,6 +161,10 @@ 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 @@ z-index: 80000; } +#overlay.modal .dialog-box .body { + min-width: 600px; +} + .dialog-box .body { padding: 5px; overflow: auto; diff -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd static/june_2007_style/trackster.css.tmpl --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,19 @@ background-color: #CCC; padding: 1em; } -.feature-popup{ - background-color: #DDD; + +.feature-popup { position: absolute; - z-index: 1000 + 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 -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd static/scripts/galaxy.panels.js --- a/static/scripts/galaxy.panels.js +++ b/static/scripts/galaxy.panels.js @@ -163,13 +163,23 @@ // 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 @@ $( ".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 -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -701,7 +701,7 @@ 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(); @@ -1027,6 +1027,7 @@ // 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 ); } @@ -1255,7 +1256,7 @@ 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(); }); @@ -2114,8 +2115,14 @@ build_form: function() { var track_config = this; var container = $("<div />"); - $.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 = $("<div class='form-row' />").appendTo( container ); @@ -2127,18 +2134,19 @@ } else if ( param.type === 'color' ) { var input = $('<input />').attr("id", id ).attr("name", id ).val( value ); // Color picker in tool tip style float - var tip = $( "<div class='tipsy tipsy-north' style='position: absolute;' />" ).hide(); + var tip = $( "<div class='tipsy tipsy-west' style='position: absolute;' />" ).hide(); // Inner div for padding purposes var tip_inner = $("<div style='background-color: black; padding: 10px;'></div>").appendTo(tip); var farb_container = $("<div/>") .appendTo(tip_inner) .farbtastic( { width: 100, height: 100, callback: input, color: value }); - // Outer div container input and tip for hover to work $("<div />").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(); @@ -2150,8 +2158,15 @@ else { row.append( $('<input />').attr("id", id ).attr("name", id ).val( value ) ); } + // Help text + if ( param.help ) { + row.append( $("<div class='help'/>").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 ) { @@ -2229,7 +2244,16 @@ // 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(), @@ -2270,8 +2294,8 @@ // Build popup. var popup = $("<div/>").attr("id", feature_uid).addClass("feature-popup"), - key, value, - table = $("<table/>").appendTo(popup), row; + table = $("<table/>"), + key, value, row; for (key in feature_dict) { value = feature_dict[key]; row = $("<tr/>").appendTo(table); @@ -2279,6 +2303,7 @@ $("<td/>").attr("align", "left").appendTo(row) .text(typeof(value) == 'number' ? round(value, 2) : value); } + popup.append( $("<div class='feature-popup-inner'>").append( table ) ); popups[feature_uid] = popup; } @@ -2289,7 +2314,7 @@ // 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") } @@ -2331,6 +2356,12 @@ 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++; // // Create HTML element structure for track. @@ -2346,6 +2377,9 @@ this.icons_div = $("<div/>").css("float", "left").appendTo(this.header_div).hide(); // Track icons. + this.toggle_icon = $("<a/>").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 = $("<a/>").attr("href", "javascript:void(0);").attr("title", "Edit settings") .addClass("icon-button settings-icon").tipsy( {gravity: 's'} ) .appendTo(this.icons_div); @@ -2365,6 +2399,21 @@ // 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() { @@ -2552,6 +2601,26 @@ this.update_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. */ predraw_init: function() {} @@ -2682,6 +2751,11 @@ */ _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; } @@ -2694,7 +2768,7 @@ w_scale = width / range, resolution = this.view.resolution, parent_element = $("<div style='position: relative;'></div>"); - + // For overview, adjust high, low, resolution, and w_scale. if (this.is_overview) { low = this.view.max_low; @@ -3028,7 +3102,8 @@ 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; @@ -3076,8 +3151,10 @@ var drag_control = $( "<div class='track-resize'>" ) // 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(); } @@ -3108,9 +3185,20 @@ 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); } @@ -3179,7 +3267,8 @@ { 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, @@ -3527,7 +3616,7 @@ 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; @@ -4057,6 +4146,7 @@ this.feature_positions = {}; this.slot_height = slot_height; this.translation = 0; + this.y_translation = 0; }; /** @@ -4078,7 +4168,7 @@ */ 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. @@ -4108,15 +4198,23 @@ 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 @@ -4153,6 +4251,7 @@ } ctx.restore(); + feature_mapper.y_translation = this.get_top_padding(width); return feature_mapper; }, /** @@ -4179,6 +4278,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, { @@ -4216,7 +4319,7 @@ 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, @@ -4279,38 +4382,51 @@ // 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; } @@ -4341,6 +4457,12 @@ 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. @@ -4673,11 +4795,54 @@ } }); +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 }; @@ -4708,4 +4873,4 @@ for ( key in modules.trackster ) { target[key] = modules.trackster[key]; } -})(window); \ No newline at end of file +})(window); diff -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd templates/tracks/browser.mako --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -42,7 +42,7 @@ <script type='text/javascript' src="${h.url_for('/static/scripts/excanvas.js')}"></script><![endif]--> -${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.mousewheel", "jquery.autocomplete", "trackster", "trackster_ui", "jquery.ui.sortable.slider", "farbtastic", "jquery.tipsy" )} +${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.event.hover","jquery.mousewheel", "jquery.autocomplete", "trackster", "trackster_ui", "jquery.ui.sortable.slider", "farbtastic", "jquery.tipsy" )} <script type="text/javascript"> // diff -r ba739e96c1a14d649c37655cecfc5b835ab343d2 -r de594b3589e575e3347c75a2d3fad33b01db35dd templates/workflow/editor.mako --- 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(); https://bitbucket.org/galaxy/galaxy-central/changeset/a9f7d94aedb5/ changeset: a9f7d94aedb5 user: james_taylor date: 2011-11-07 18:34:23 summary: Trackster: allow selecting the arc connector style for feature tracks, add support for select config parameter type affected #: 1 file diff -r de594b3589e575e3347c75a2d3fad33b01db35dd -r a9f7d94aedb5ec5a7fe6e9f832b98c45a4c473d3 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -2127,10 +2127,21 @@ var value = track_config.values[ param.key ]; var row = $("<div class='form-row' />").appendTo( container ); row.append( $('<label />').attr("for", id ).text( param.label + ":" ) ); + // Draw parameter as checkbox if ( param.type === 'bool' ) { row.append( $('<input type="checkbox" />').attr("id", id ).attr("name", id ).attr( 'checked', value ) ); + // Draw parameter as textbox } else if ( param.type === 'text' ) { row.append( $('<input type="text"/>').attr("id", id ).val(value).click( function() { $(this).select() })); + // Draw paramter as select area + } else if ( param.type == 'select' ) { + var select = $('<select />').attr("id", id); + for ( var i = 0; i < param.options.length; i++ ) { + $("<option/>").text( param.options[i].label ).attr( "value", param.options[i].value ).appendTo( select ); + } + select.val( value ); + row.append( select ); + // Draw parameter as color picker } else if ( param.type === 'color' ) { var input = $('<input />').attr("id", id ).attr("name", id ).val( value ); // Color picker in tool tip style float @@ -3269,12 +3280,15 @@ { key: 'label_color', label: 'Label color', type: 'color', default_value: 'black' }, { key: 'show_counts', label: 'Show summary counts', type: 'bool', default_value: true, help: 'Show the number of items in each bin when drawing summary histogram' }, + { key: 'connector_style', label: 'Connector style', type: 'select', default_value: 'fishbones', + options: [ { label: 'Line with arrows', value: 'fishbone' }, { label: 'Arcs', value: 'arcs' } ] }, { key: 'mode', type: 'string', default_value: this.mode, hidden: true }, ], saved_values: prefs, onchange: function() { track.set_name(track.prefs.name); track.tile_cache.clear(); + track.set_painter_from_config(); track.request_draw(); } }); @@ -3297,6 +3311,14 @@ this.painter = painters.LinkedFeaturePainter; }; extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, { + set_painter_from_config: function() { + console.log( this, this.config ); + if ( this.config.values['connector_style'] == 'arcs' ) { + this.painter = painters.ArcLinkedFeaturePainter; + } else { + this.painter = painters.LinkedFeaturePainter; + } + }, /** * Actions to be taken after draw has been completed. Draw is completed when all tiles have been * drawn/fetched and shown. https://bitbucket.org/galaxy/galaxy-central/changeset/0f5ab41da4bd/ changeset: 0f5ab41da4bd user: james_taylor date: 2011-11-07 21:36:46 summary: Trackster: fix loading of track connector style when initializing from saved affected #: 1 file diff -r a9f7d94aedb5ec5a7fe6e9f832b98c45a4c473d3 -r 0f5ab41da4bd2e605b9f0785a1e35a7a0d5983d6 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -3308,7 +3308,8 @@ this.data_manager = new DataManager(20, this); this.left_offset = 200; - this.painter = painters.LinkedFeaturePainter; + // this.painter = painters.LinkedFeaturePainter; + this.set_painter_from_config(); }; extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, { set_painter_from_config: function() { https://bitbucket.org/galaxy/galaxy-central/changeset/0c65c410ccc4/ changeset: 0c65c410ccc4 user: james_taylor date: 2011-11-07 22:59:15 summary: Trackster: code for adding multiple bookmarks from a dataset. Currently the button is commented out until this can be made more usable affected #: 7 files diff -r 0f5ab41da4bd2e605b9f0785a1e35a7a0d5983d6 -r 0c65c410ccc4b15c72fcb50a5c3c3e982f0b446b lib/galaxy/visualization/tracks/data_providers.py --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -363,14 +363,18 @@ for large datasets. """ - def get_iterator( self, chrom, start, end ): + def get_iterator( self, chrom=None, start=None, end=None ): def line_filter_iter(): for line in open( self.original_dataset.file_name ): + if line.startswith( "track" ) or line.startswith( "browser" ): + continue feature = line.split() feature_chrom = feature[0] feature_start = int( feature[1] ) feature_end = int( feature[2] ) - if feature_chrom != chrom or feature_start > int( end ) or feature_end < int( start ): + if ( chrom is not None and feature_chrom != chrom ) \ + or ( start is not None and feature_start > int( end ) ) \ + or ( end is not None and feature_end < int( start ) ): continue yield line return line_filter_iter() diff -r 0f5ab41da4bd2e605b9f0785a1e35a7a0d5983d6 -r 0c65c410ccc4b15c72fcb50a5c3c3e982f0b446b lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -13,7 +13,7 @@ from galaxy.web.framework import simplejson from galaxy.web.framework.helpers import time_ago, grids from galaxy.util.bunch import Bunch -from galaxy.datatypes.interval import Gff +from galaxy.datatypes.interval import Gff, Bed from galaxy.model import NoConverterException, ConverterDependencyException from galaxy.visualization.tracks.data_providers import * from galaxy.visualization.tracks.visual_analytics import get_tool_def, get_dataset_job @@ -246,6 +246,27 @@ "tool": get_tool_def( trans, dataset ) } return track + + @web.json + def bookmarks_from_dataset( self, trans, hda_id=None, ldda_id=None ): + if hda_id: + hda_ldda = "hda" + dataset = self.get_dataset( trans, hda_id, check_ownership=False, check_accessible=True ) + elif ldda_id: + hda_ldda = "ldda" + dataset = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( ldda_id ) ) + rows = [] + if isinstance( dataset.datatype, Bed ): + data = RawBedDataProvider( original_dataset=dataset ).get_iterator() + for i, line in enumerate( data ): + if ( i > 500 ): break + fields = line.split() + location = name = "%s:%s-%s" % ( fields[0], fields[1], fields[2] ) + if len( fields ) > 3: + name = fields[4] + rows.append( [location, name] ) + return { 'data': rows } + @web.expose @web.require_login() diff -r 0f5ab41da4bd2e605b9f0785a1e35a7a0d5983d6 -r 0c65c410ccc4b15c72fcb50a5c3c3e982f0b446b static/june_2007_style/base.css.tmpl --- a/static/june_2007_style/base.css.tmpl +++ b/static/june_2007_style/base.css.tmpl @@ -909,6 +909,14 @@ -sprite-group: fugue; -sprite-image: fugue/plus-circle.png; } +.icon-button.plus-button { + -sprite-group: fugue; + -sprite-image: fugue/plus-button.png; +} +.icon-button.gear { + -sprite-group: fugue; + -sprite-image: fugue/gear.png; +} .tipsy { padding: 5px; diff -r 0f5ab41da4bd2e605b9f0785a1e35a7a0d5983d6 -r 0c65c410ccc4b15c72fcb50a5c3c3e982f0b446b static/june_2007_style/blue/base.css --- a/static/june_2007_style/blue/base.css +++ b/static/june_2007_style/blue/base.css @@ -163,6 +163,8 @@ .icon-button.vis-chart{background:url(fugue.png) no-repeat 0px -286px;} .icon-button.go-to-full-screen{background:url(fugue.png) no-repeat 0px -312px;} .icon-button.import{background:url(fugue.png) no-repeat 0px -338px;} +.icon-button.plus-button{background:url(fugue.png) no-repeat 0px -364px;} +.icon-button.gear{background:url(fugue.png) no-repeat 0px -390px;} .tipsy{padding:5px;font-size:10px;filter:alpha(opacity=80);background-repeat:no-repeat;background-image:url(../images/tipsy.gif);} .tipsy-inner{padding:5px 8px 4px 8px;background-color:black;color:white;max-width:200px;text-align:center;} .tipsy-north{background-position:top center;} @@ -171,7 +173,7 @@ .tipsy-west{background-position:left center;} .editable-text{cursor:pointer;} .editable-text:hover{cursor:text;border:dotted #999999 1px;} -.text-and-autocomplete-select{background:url(fugue.png) no-repeat right -364px;} +.text-and-autocomplete-select{background:url(fugue.png) no-repeat right -416px;} .icon-button.multiinput{background:url(../images/documents-stack.png) no-repeat;cursor:pointer;float:none;display:inline-block;margin-left:10px;} .icon-button.multiinput.disabled{background:url(../images/documents-stack-faded.png) no-repeat;cursor:auto;} .workflow-invocation-complete{border:solid 1px #6A6;border-left-width:5px;margin:10px 0;padding-left:5px;} diff -r 0f5ab41da4bd2e605b9f0785a1e35a7a0d5983d6 -r 0c65c410ccc4b15c72fcb50a5c3c3e982f0b446b static/june_2007_style/blue/fugue.png Binary file static/june_2007_style/blue/fugue.png has changed diff -r 0f5ab41da4bd2e605b9f0785a1e35a7a0d5983d6 -r 0c65c410ccc4b15c72fcb50a5c3c3e982f0b446b static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -3313,7 +3313,6 @@ }; extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, { set_painter_from_config: function() { - console.log( this, this.config ); if ( this.config.values['connector_style'] == 'arcs' ) { this.painter = painters.ArcLinkedFeaturePainter; } else { diff -r 0f5ab41da4bd2e605b9f0785a1e35a7a0d5983d6 -r 0c65c410ccc4b15c72fcb50a5c3c3e982f0b446b templates/tracks/browser.mako --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -110,6 +110,52 @@ } }); }; + + /** + * Use a popup grid to bookmarks from a dataset. + */ + var add_bookmarks = function() { + show_modal( "Select dataset for new bookmarks", "progress" ); + $.ajax({ + url: "${h.url_for( action='list_histories' )}", + data: { "f-dbkey": view.dbkey }, + error: function() { alert( "Grid failed" ); }, + success: function(table_html) { + show_modal( + "Select dataset for new bookmarks", + table_html, { + "Cancel": function() { + hide_modal(); + }, + "Insert": function() { + // Just use the first selected + $('input[name=id]:checked,input[name=ldda_ids]:checked').first().each(function(){ + var data, id = $(this).val(); + if ($(this).attr("name") === "id") { + data = { hda_id: id }; + } else { + data = { ldda_id: id}; + } + + $.ajax({ + url: "${h.url_for( action='bookmarks_from_dataset' )}", + data: data, + dataType: "json", + }).then( function(data) { + for( i = 0; i < data.data.length; i++ ) { + var row = data.data[i]; + console.log( row[0], row[1] ); + add_bookmark( row[0], row[1] ); + } + }); + }); + hide_modal(); + } + } + ); + } + }); + }; $(function() { // Manual tipsy config because default gravity is S and cannot be changed. @@ -240,7 +286,13 @@ annotation = "Bookmark description"; return add_bookmark(position, annotation); }); - + + // make_popupmenu( $("#bookmarks-more-button"), { + // "Add from BED dataset": function() { + // add_bookmarks(); + // } + // }); + init_keyboard_nav(view); }; @@ -271,14 +323,15 @@ <div class="unified-panel-header" unselectable="on"><div class="unified-panel-header-inner"> + <div style="float: right"> + <a id="add-bookmark-button" class='icon-button menu-button plus-button' href="javascript:void(0);" title="Add bookmark"></a> + ## <a id="bookmarks-more-button" class='icon-button menu-button gear popup' href="javascript:void(0);" title="More actions"></a> + </div> Bookmarks </div></div><div class="unified-panel-body" style="overflow: auto;"><div id="bookmarks-container"></div> - <div> - <a class="icon-button import" style="margin-left: .5em; width: 100%" original-title="Add Bookmark" id="add-bookmark-button" href="javascript:void(0);">Add Bookmark</a> - </div></div></%def> https://bitbucket.org/galaxy/galaxy-central/changeset/8b91dd6872df/ changeset: 8b91dd6872df user: james_taylor date: 2011-11-08 00:22:47 summary: Trackster: fix strand drawing for single blocks with no thin/thick part affected #: 1 file diff -r 0c65c410ccc4b15c72fcb50a5c3c3e982f0b446b -r 8b91dd6872df54ef7d479b8c814241e2aabf12f4 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -4385,17 +4385,17 @@ // Draw feature/feature blocks + connectors. if (!feature_blocks) { // If there are no blocks, treat the feature as one big exon. - if ( feature.strand ) { - if (feature.strand === "+") { + ctx.fillStyle = block_color; + ctx.fillRect(f_start, y_center + 1, f_end - f_start, thick_height); + // If strand is specified, draw arrows over feature + if ( feature_strand ) { + if (feature_strand === "+") { ctx.fillStyle = ctx.canvas.manager.get_pattern( 'right_strand_inv' ); - } else if (feature.strand === "-") { + } else if (feature_strand === "-") { ctx.fillStyle = ctx.canvas.manager.get_pattern( 'left_strand_inv' ); } + ctx.fillRect(f_start, y_center + 1, f_end - f_start, thick_height); } - else { // No strand. - ctx.fillStyle = block_color; - } - ctx.fillRect(f_start, y_center, f_end - f_start, thick_height); } else { // // There are feature blocks and mode is either Squish or Pack. https://bitbucket.org/galaxy/galaxy-central/changeset/132798922d53/ changeset: 132798922d53 user: james_taylor date: 2011-11-08 17:18:24 summary: Automated merge with ssh://bitbucket.org/galaxy/galaxy-central affected #: 15 files diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -12,7 +12,7 @@ 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 @@ psycopg2 = _8.4.2_static pysqlite = _3.6.17_static MySQL_python = _5.1.41_static -bx_python = _494c2d1d68b3_rebuild1 +bx_python = _7b95ff194725 GeneTrack = _dev_48da9e998f0caf01c5be731e926f4b0481f658f0 SQLAlchemy = _dev_r6498 pysam = _kanwei_b10f6e722e9a diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 lib/galaxy/datatypes/converters/interval_to_summary_tree_converter.py --- 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 @@ st.write(out_fname) if __name__ == "__main__": - main() \ No newline at end of file + main() diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 lib/galaxy/visualization/tracks/data_providers.py --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -2,14 +2,15 @@ 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): pkg_resources.require( "ctypes" ) pkg_resources.require( "pysam" ) pkg_resources.require( "numpy" ) +import numpy from galaxy.datatypes.util.gff_util import * from galaxy.util.json import from_json_string from bx.interval_index_file import Indexes @@ -362,14 +363,18 @@ for large datasets. """ - def get_iterator( self, chrom, start, end ): + def get_iterator( self, chrom=None, start=None, end=None ): def line_filter_iter(): for line in open( self.original_dataset.file_name ): + if line.startswith( "track" ) or line.startswith( "browser" ): + continue feature = line.split() feature_chrom = feature[0] feature_start = int( feature[1] ) feature_end = int( feature[2] ) - if feature_chrom != chrom or feature_start > int( end ) or feature_end < int( start ): + if ( chrom is not None and feature_chrom != chrom ) \ + or ( start is not None and feature_start > int( end ) ) \ + or ( end is not None and feature_end < int( start ) ): continue yield line return line_filter_iter() @@ -740,40 +745,73 @@ # 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 values 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: + # 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 = numpy.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) + + # 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() + + result = [] + + 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 - result = [] - if data: - for dat_dict in data: - result.append( (pos, float_nan(dat_dict['mean']) ) ) + pos = start + step_size = (end - start) / num_points + + for i in range( num_points ): + result.append( (pos, float_nan( mean[i] ) ) ) pos += step_size return { 'data': result } diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -13,7 +13,7 @@ from galaxy.web.framework import simplejson from galaxy.web.framework.helpers import time_ago, grids from galaxy.util.bunch import Bunch -from galaxy.datatypes.interval import Gff +from galaxy.datatypes.interval import Gff, Bed from galaxy.model import NoConverterException, ConverterDependencyException from galaxy.visualization.tracks.data_providers import * from galaxy.visualization.tracks.visual_analytics import get_tool_def, get_dataset_job @@ -246,6 +246,27 @@ "tool": get_tool_def( trans, dataset ) } return track + + @web.json + def bookmarks_from_dataset( self, trans, hda_id=None, ldda_id=None ): + if hda_id: + hda_ldda = "hda" + dataset = self.get_dataset( trans, hda_id, check_ownership=False, check_accessible=True ) + elif ldda_id: + hda_ldda = "ldda" + dataset = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( ldda_id ) ) + rows = [] + if isinstance( dataset.datatype, Bed ): + data = RawBedDataProvider( original_dataset=dataset ).get_iterator() + for i, line in enumerate( data ): + if ( i > 500 ): break + fields = line.split() + location = name = "%s:%s-%s" % ( fields[0], fields[1], fields[2] ) + if len( fields ) > 3: + name = fields[4] + rows.append( [location, name] ) + return { 'data': rows } + @web.expose @web.require_login() @@ -528,6 +549,7 @@ 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 } @@ -1041,4 +1063,4 @@ 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 -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/june_2007_style/base.css.tmpl --- a/static/june_2007_style/base.css.tmpl +++ b/static/june_2007_style/base.css.tmpl @@ -231,6 +231,10 @@ padding: 3px 0 0 1em; } +.form-row .help { + color: #666; +} + select, input, textarea { font: inherit; } @@ -905,6 +909,14 @@ -sprite-group: fugue; -sprite-image: fugue/plus-circle.png; } +.icon-button.plus-button { + -sprite-group: fugue; + -sprite-image: fugue/plus-button.png; +} +.icon-button.gear { + -sprite-group: fugue; + -sprite-image: fugue/gear.png; +} .tipsy { padding: 5px; diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/june_2007_style/blue/base.css --- a/static/june_2007_style/blue/base.css +++ b/static/june_2007_style/blue/base.css @@ -43,6 +43,7 @@ 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;} @@ -162,6 +163,8 @@ .icon-button.vis-chart{background:url(fugue.png) no-repeat 0px -286px;} .icon-button.go-to-full-screen{background:url(fugue.png) no-repeat 0px -312px;} .icon-button.import{background:url(fugue.png) no-repeat 0px -338px;} +.icon-button.plus-button{background:url(fugue.png) no-repeat 0px -364px;} +.icon-button.gear{background:url(fugue.png) no-repeat 0px -390px;} .tipsy{padding:5px;font-size:10px;filter:alpha(opacity=80);background-repeat:no-repeat;background-image:url(../images/tipsy.gif);} .tipsy-inner{padding:5px 8px 4px 8px;background-color:black;color:white;max-width:200px;text-align:center;} .tipsy-north{background-position:top center;} @@ -170,7 +173,7 @@ .tipsy-west{background-position:left center;} .editable-text{cursor:pointer;} .editable-text:hover{cursor:text;border:dotted #999999 1px;} -.text-and-autocomplete-select{background:url(fugue.png) no-repeat right -364px;} +.text-and-autocomplete-select{background:url(fugue.png) no-repeat right -416px;} .icon-button.multiinput{background:url(../images/documents-stack.png) no-repeat;cursor:pointer;float:none;display:inline-block;margin-left:10px;} .icon-button.multiinput.disabled{background:url(../images/documents-stack-faded.png) no-repeat;cursor:auto;} .workflow-invocation-complete{border:solid 1px #6A6;border-left-width:5px;margin:10px 0;padding-left:5px;} diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/june_2007_style/blue/fugue.png Binary file static/june_2007_style/blue/fugue.png has changed diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/june_2007_style/blue/panel_layout.css --- a/static/june_2007_style/blue/panel_layout.css +++ b/static/june_2007_style/blue/panel_layout.css @@ -23,9 +23,11 @@ .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 -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/june_2007_style/blue/trackster.css --- 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,5 @@ .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 -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/june_2007_style/panel_layout.css.tmpl --- a/static/june_2007_style/panel_layout.css.tmpl +++ b/static/june_2007_style/panel_layout.css.tmpl @@ -161,6 +161,10 @@ 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 @@ z-index: 80000; } +#overlay.modal .dialog-box .body { + min-width: 600px; +} + .dialog-box .body { padding: 5px; overflow: auto; diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/june_2007_style/trackster.css.tmpl --- a/static/june_2007_style/trackster.css.tmpl +++ b/static/june_2007_style/trackster.css.tmpl @@ -405,8 +405,19 @@ background-color: #CCC; padding: 1em; } -.feature-popup{ - background-color: #DDD; + +.feature-popup { position: absolute; - z-index: 1000 + 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 -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/scripts/galaxy.panels.js --- a/static/scripts/galaxy.panels.js +++ b/static/scripts/galaxy.panels.js @@ -163,13 +163,23 @@ // 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 @@ $( ".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 -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 static/scripts/trackster.js --- a/static/scripts/trackster.js +++ b/static/scripts/trackster.js @@ -701,7 +701,7 @@ 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(); @@ -1027,6 +1027,7 @@ // 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 ); } @@ -1255,7 +1256,7 @@ 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(); }); @@ -2114,31 +2115,49 @@ build_form: function() { var track_config = this; var container = $("<div />"); - $.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 = $("<div class='form-row' />").appendTo( container ); row.append( $('<label />').attr("for", id ).text( param.label + ":" ) ); + // Draw parameter as checkbox if ( param.type === 'bool' ) { row.append( $('<input type="checkbox" />').attr("id", id ).attr("name", id ).attr( 'checked', value ) ); + // Draw parameter as textbox } else if ( param.type === 'text' ) { row.append( $('<input type="text"/>').attr("id", id ).val(value).click( function() { $(this).select() })); + // Draw paramter as select area + } else if ( param.type == 'select' ) { + var select = $('<select />').attr("id", id); + for ( var i = 0; i < param.options.length; i++ ) { + $("<option/>").text( param.options[i].label ).attr( "value", param.options[i].value ).appendTo( select ); + } + select.val( value ); + row.append( select ); + // Draw parameter as color picker } else if ( param.type === 'color' ) { var input = $('<input />').attr("id", id ).attr("name", id ).val( value ); // Color picker in tool tip style float - var tip = $( "<div class='tipsy tipsy-north' style='position: absolute;' />" ).hide(); + var tip = $( "<div class='tipsy tipsy-west' style='position: absolute;' />" ).hide(); // Inner div for padding purposes var tip_inner = $("<div style='background-color: black; padding: 10px;'></div>").appendTo(tip); var farb_container = $("<div/>") .appendTo(tip_inner) .farbtastic( { width: 100, height: 100, callback: input, color: value }); - // Outer div container input and tip for hover to work $("<div />").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(); @@ -2150,8 +2169,15 @@ else { row.append( $('<input />').attr("id", id ).attr("name", id ).val( value ) ); } + // Help text + if ( param.help ) { + row.append( $("<div class='help'/>").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 ) { @@ -2229,7 +2255,16 @@ // 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(), @@ -2270,8 +2305,8 @@ // Build popup. var popup = $("<div/>").attr("id", feature_uid).addClass("feature-popup"), - key, value, - table = $("<table/>").appendTo(popup), row; + table = $("<table/>"), + key, value, row; for (key in feature_dict) { value = feature_dict[key]; row = $("<tr/>").appendTo(table); @@ -2279,6 +2314,7 @@ $("<td/>").attr("align", "left").appendTo(row) .text(typeof(value) == 'number' ? round(value, 2) : value); } + popup.append( $("<div class='feature-popup-inner'>").append( table ) ); popups[feature_uid] = popup; } @@ -2289,7 +2325,7 @@ // 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") } @@ -2331,6 +2367,12 @@ 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++; // // Create HTML element structure for track. @@ -2346,6 +2388,9 @@ this.icons_div = $("<div/>").css("float", "left").appendTo(this.header_div).hide(); // Track icons. + this.toggle_icon = $("<a/>").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 = $("<a/>").attr("href", "javascript:void(0);").attr("title", "Edit settings") .addClass("icon-button settings-icon").tipsy( {gravity: 's'} ) .appendTo(this.icons_div); @@ -2365,6 +2410,21 @@ // 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() { @@ -2552,6 +2612,26 @@ this.update_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. */ predraw_init: function() {} @@ -2682,6 +2762,11 @@ */ _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; } @@ -2694,7 +2779,7 @@ w_scale = width / range, resolution = this.view.resolution, parent_element = $("<div style='position: relative;'></div>"); - + // For overview, adjust high, low, resolution, and w_scale. if (this.is_overview) { low = this.view.max_low; @@ -3028,7 +3113,8 @@ 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; @@ -3076,8 +3162,10 @@ var drag_control = $( "<div class='track-resize'>" ) // 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(); } @@ -3108,9 +3196,20 @@ 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); } @@ -3179,13 +3278,17 @@ { 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: 'connector_style', label: 'Connector style', type: 'select', default_value: 'fishbones', + options: [ { label: 'Line with arrows', value: 'fishbone' }, { label: 'Arcs', value: 'arcs' } ] }, { key: 'mode', type: 'string', default_value: this.mode, hidden: true }, ], saved_values: prefs, onchange: function() { track.set_name(track.prefs.name); track.tile_cache.clear(); + track.set_painter_from_config(); track.request_draw(); } }); @@ -3205,9 +3308,17 @@ this.data_manager = new DataManager(20, this); this.left_offset = 200; - this.painter = painters.LinkedFeaturePainter; + // this.painter = painters.LinkedFeaturePainter; + this.set_painter_from_config(); }; extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, { + set_painter_from_config: function() { + if ( this.config.values['connector_style'] == 'arcs' ) { + this.painter = painters.ArcLinkedFeaturePainter; + } else { + this.painter = painters.LinkedFeaturePainter; + } + }, /** * Actions to be taken after draw has been completed. Draw is completed when all tiles have been * drawn/fetched and shown. @@ -3527,7 +3638,7 @@ 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; @@ -4057,6 +4168,7 @@ this.feature_positions = {}; this.slot_height = slot_height; this.translation = 0; + this.y_translation = 0; }; /** @@ -4078,7 +4190,7 @@ */ 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. @@ -4108,15 +4220,23 @@ 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 @@ -4153,6 +4273,7 @@ } ctx.restore(); + feature_mapper.y_translation = this.get_top_padding(width); return feature_mapper; }, /** @@ -4179,6 +4300,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, { @@ -4216,7 +4341,7 @@ 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, @@ -4260,17 +4385,17 @@ // Draw feature/feature blocks + connectors. if (!feature_blocks) { // If there are no blocks, treat the feature as one big exon. - if ( feature.strand ) { - if (feature.strand === "+") { + ctx.fillStyle = block_color; + ctx.fillRect(f_start, y_center + 1, f_end - f_start, thick_height); + // If strand is specified, draw arrows over feature + if ( feature_strand ) { + if (feature_strand === "+") { ctx.fillStyle = ctx.canvas.manager.get_pattern( 'right_strand_inv' ); - } else if (feature.strand === "-") { + } else if (feature_strand === "-") { ctx.fillStyle = ctx.canvas.manager.get_pattern( 'left_strand_inv' ); } + ctx.fillRect(f_start, y_center + 1, f_end - f_start, thick_height); } - else { // No strand. - ctx.fillStyle = block_color; - } - ctx.fillRect(f_start, y_center, f_end - f_start, thick_height); } else { // // There are feature blocks and mode is either Squish or Pack. @@ -4279,38 +4404,51 @@ // 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; } @@ -4341,6 +4479,12 @@ 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. @@ -4673,11 +4817,54 @@ } }); +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 }; @@ -4708,4 +4895,4 @@ for ( key in modules.trackster ) { target[key] = modules.trackster[key]; } -})(window); \ No newline at end of file +})(window); diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 templates/tracks/browser.mako --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -42,7 +42,7 @@ <script type='text/javascript' src="${h.url_for('/static/scripts/excanvas.js')}"></script><![endif]--> -${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.mousewheel", "jquery.autocomplete", "trackster", "trackster_ui", "jquery.ui.sortable.slider", "farbtastic", "jquery.tipsy" )} +${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.event.drag", "jquery.event.hover","jquery.mousewheel", "jquery.autocomplete", "trackster", "trackster_ui", "jquery.ui.sortable.slider", "farbtastic", "jquery.tipsy" )} <script type="text/javascript"> // @@ -110,6 +110,52 @@ } }); }; + + /** + * Use a popup grid to bookmarks from a dataset. + */ + var add_bookmarks = function() { + show_modal( "Select dataset for new bookmarks", "progress" ); + $.ajax({ + url: "${h.url_for( action='list_histories' )}", + data: { "f-dbkey": view.dbkey }, + error: function() { alert( "Grid failed" ); }, + success: function(table_html) { + show_modal( + "Select dataset for new bookmarks", + table_html, { + "Cancel": function() { + hide_modal(); + }, + "Insert": function() { + // Just use the first selected + $('input[name=id]:checked,input[name=ldda_ids]:checked').first().each(function(){ + var data, id = $(this).val(); + if ($(this).attr("name") === "id") { + data = { hda_id: id }; + } else { + data = { ldda_id: id}; + } + + $.ajax({ + url: "${h.url_for( action='bookmarks_from_dataset' )}", + data: data, + dataType: "json", + }).then( function(data) { + for( i = 0; i < data.data.length; i++ ) { + var row = data.data[i]; + console.log( row[0], row[1] ); + add_bookmark( row[0], row[1] ); + } + }); + }); + hide_modal(); + } + } + ); + } + }); + }; $(function() { // Manual tipsy config because default gravity is S and cannot be changed. @@ -240,7 +286,13 @@ annotation = "Bookmark description"; return add_bookmark(position, annotation); }); - + + // make_popupmenu( $("#bookmarks-more-button"), { + // "Add from BED dataset": function() { + // add_bookmarks(); + // } + // }); + init_keyboard_nav(view); }; @@ -271,14 +323,15 @@ <div class="unified-panel-header" unselectable="on"><div class="unified-panel-header-inner"> + <div style="float: right"> + <a id="add-bookmark-button" class='icon-button menu-button plus-button' href="javascript:void(0);" title="Add bookmark"></a> + ## <a id="bookmarks-more-button" class='icon-button menu-button gear popup' href="javascript:void(0);" title="More actions"></a> + </div> Bookmarks </div></div><div class="unified-panel-body" style="overflow: auto;"><div id="bookmarks-container"></div> - <div> - <a class="icon-button import" style="margin-left: .5em; width: 100%" original-title="Add Bookmark" id="add-bookmark-button" href="javascript:void(0);">Add Bookmark</a> - </div></div></%def> diff -r d161bb98374369c0cc3aa77eed4e162273e64bb3 -r 132798922d53213d6ca4e1596619f254bdd582d0 templates/workflow/editor.mako --- 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(); Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.