diff --git a/client/galaxy/scripts/utils/config.js b/client/galaxy/scripts/utils/config.js index db3c4052ede..50bec9ccb9e 100644 --- a/client/galaxy/scripts/utils/config.js +++ b/client/galaxy/scripts/utils/config.js @@ -4,7 +4,7 @@ define(['libs/underscore', 'viz/trackster/util', 'utils/config'], function(_, ut * A configuration setting. Currently key is used as id. */ var ConfigSetting = Backbone.Model.extend({ - + initialize: function(options) { // Use key as id for now. var key = this.get('key'); @@ -16,8 +16,8 @@ var ConfigSetting = Backbone.Model.extend({ this.set(_.extend({}, defaults, options)); } - if (this.get('value') === undefined) { - // Use default to set value. + if (this.get('value') === undefined && this.get('default_value') !== undefined) { + // Use default to set value (if present). this.set_value(this.get('default_value')); // If no default value for color config, set random color. @@ -134,7 +134,7 @@ var ConfigSettingCollection = Backbone.Collection.extend({ return _.extend({}, m, { value: saved_values[m.key] }); }); } - + return new ConfigSettingCollection(models); } }); @@ -150,7 +150,7 @@ var ConfigSettingCollectionView = Backbone.View.extend({ */ render: function() { var container = this.$el; - + this.collection.each(function(param, index) { // Hidden params have no representation in the form if (param.get('hidden')) { return; } @@ -165,7 +165,7 @@ var ConfigSettingCollectionView = Backbone.View.extend({ if ( type === 'bool' ) { row.append( $('').attr("id", id ).attr("name", id ).attr( 'checked', value ) ); } - // Draw parameter as textbox + // Draw parameter as textbox else if ( type === 'text' ) { row.append( $('').attr("id", id ).val(value).click( function() { $(this).select(); })); } @@ -177,39 +177,39 @@ var ConfigSettingCollectionView = Backbone.View.extend({ }); select.val( value ); row.append( select ); - + } - // Draw parameter as color picker + // Draw parameter as color picker else if ( type === 'color' ) { - var + var container_div = $("
").appendTo(row), - input = $('').attr("id", id ).attr("name", id ).val( value ).css("float", "left") + input = $('').attr("id", id ).attr("name", id ).val( value ).css("float", "left") .appendTo(container_div).click(function(e) { // Hide other pickers. $(".tooltip").removeClass( "in" ); - + // Show input's color picker. var tip = $(this).siblings(".tooltip").addClass( "in" ); - tip.css( { + tip.css( { // left: $(this).position().left + ( $(input).width() / 2 ) - 60, - // top: $(this).position().top + $(this.height) + // top: $(this).position().top + $(this.height) left: $(this).position().left + $(this).width() + 5, top: $(this).position().top - ( $(tip).height() / 2 ) + ( $(this).height() / 2 ) } ).show(); - - // Click management: - + + // Click management: + // Keep showing tip if clicking in tip. tip.click(function(e) { e.stopPropagation(); }); - + // Hide tip if clicking outside of tip. $(document).bind( "click.color-picker", function() { tip.hide(); $(document).unbind( "click.color-picker" ); }); - + // No propagation to avoid triggering document click (and tip hiding) above. e.stopPropagation(); }), @@ -222,20 +222,20 @@ var ConfigSettingCollectionView = Backbone.View.extend({ tip_inner = $("").appendTo(tip), tip_arrow = $("").appendTo(tip), farb_obj = $.farbtastic(tip_inner, { width: 100, height: 100, callback: input, color: value }); - + // Clear floating. container_div.append( $("").css("clear", "both")); - + // Use function to fix farb_obj value. (function(fixed_farb_obj) { new_color_icon.click(function() { fixed_farb_obj.setColor(util_mod.get_random_color()); - }); + }); })(farb_obj); - - } + + } else { - row.append( $('').attr("id", id ).attr("name", id ).val( value ) ); + row.append( $('').attr("id", id ).attr("name", id ).val( value ) ); } // Help text if ( param.help ) { diff --git a/client/galaxy/scripts/viz/trackster/painters.js b/client/galaxy/scripts/viz/trackster/painters.js index f17cb050067..f88aabf3d69 100644 --- a/client/galaxy/scripts/viz/trackster/painters.js +++ b/client/galaxy/scripts/viz/trackster/painters.js @@ -8,7 +8,7 @@ define( ["libs/underscore"], function( _ ) { */ var BEFORE = 1001, CONTAINS = 1002, OVERLAP_START = 1003, OVERLAP_END = 1004, CONTAINED_BY = 1005, AFTER = 1006; var compute_overlap = function(first_region, second_region) { - var + var first_start = first_region[0], first_end = first_region[1], second_start = second_region[0], second_end = second_region[1], overlap; @@ -34,7 +34,7 @@ var compute_overlap = function(first_region, second_region) { overlap = OVERLAP_END; } } - + return overlap; }; @@ -60,7 +60,7 @@ var dashedLine = function(ctx, x1, y1, x2, y2, dashLen) { var dashX = dX / dashes; var dashY = dY / dashes; var q; - + for (q = 0; q < dashes; q++, x1 += dashX, y1 += dashY) { if (q % 2 !== 0) { continue; @@ -74,11 +74,11 @@ var dashedLine = function(ctx, x1, y1, x2, y2, dashLen) { */ var drawDownwardEquilateralTriangle = function(ctx, down_vertex_x, down_vertex_y, side_len) { // Compute other two points of triangle. - var + var x1 = down_vertex_x - side_len/2, x2 = down_vertex_x + side_len/2, y = down_vertex_y - Math.sqrt( side_len*3/2 ); - + // Draw and fill. ctx.beginPath(); ctx.moveTo(x1, y); @@ -198,16 +198,16 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) { ctx.fillStyle = "#aaa"; ctx.fillRect( 0, y_zero, width, 1 ); } - + ctx.beginPath(); var x_scaled, y, delta_x_px; if (data.length > 1) { delta_x_px = Math.ceil((data[1][0] - data[0][0]) * w_scale); - } + } else { delta_x_px = 10; } - + // Painter color can be in either block_color (FeatureTrack) or color pref (LineTrack). var painter_color = this.prefs.block_color || this.prefs.color, // Extract RGB from preference color. @@ -215,10 +215,10 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) { pref_r = (pref_color & 0xff0000) >> 16, pref_g = (pref_color & 0x00ff00) >> 8, pref_b = pref_color & 0x0000ff, - top_overflow = false, + top_overflow = false, bot_overflow = false; - - + + // Paint track. for (var i = 0, len = data.length; i < len; i++) { // Reset attributes for next point. @@ -241,20 +241,20 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) { if (y < min_value) { bot_overflow = true; y = min_value; - } + } else if (y > max_value) { top_overflow = true; y = max_value; } - + // Draw point. if (mode === "Histogram") { // y becomes the bar height in pixels, which is the negated for canvas coords y = Math.round( y / vertical_range * height_px ); ctx.fillRect(x_scaled, y_zero, delta_x_px, - y ); - } + } else if (mode === "Intensity") { - var + var saturation = (y - min_value) / vertical_range, // Range is [pref_color, 255] where saturation = 0 --> 255 and saturation = 1 --> pref color new_r = Math.round( pref_r + (255 - pref_r) * (1 - saturation) ), @@ -262,23 +262,23 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) { new_b = Math.round( pref_b + (255 - pref_b) * (1 - saturation) ); ctx.fillStyle = "rgb(" + new_r + "," + new_g + "," + new_b + ")"; ctx.fillRect(x_scaled, 0, delta_x_px, height_px); - } + } else { // mode is Coverage/Line or Filled. // Scale Y value. y = Math.round( height_px - (y - min_value) / vertical_range * height_px ); if (in_path) { ctx.lineTo(x_scaled, y); - } + } else { in_path = true; if (mode === "Filled") { ctx.moveTo(x_scaled, height_px); ctx.lineTo(x_scaled, y); - } + } else { ctx.moveTo(x_scaled, y); - // Use this approach (note: same as for filled) to draw line from 0 to + // Use this approach (note: same as for filled) to draw line from 0 to // first data point. //ctx.moveTo(x_scaled, height_px); //ctx.lineTo(x_scaled, y); @@ -292,7 +292,7 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) { var overflow_x; if (mode === "Histogram" || mode === "Intensity") { overflow_x = delta_x_px; - } + } else { // Line and Filled, which are points x_scaled -= 2; // Move it over to the left so it's centered on the point overflow_x = 4; @@ -312,11 +312,11 @@ LinePainter.prototype.draw = function(ctx, width, height, w_scale) { ctx.lineTo( 0, y_zero ); } ctx.fill(); - } + } else { ctx.stroke(); } - + ctx.restore(); }; @@ -356,7 +356,7 @@ FeaturePositionMapper.prototype.get_feature_data = function(x, y) { if (!this.feature_positions[slot]) { return null; } - + // Find feature using X. x += this.translation; for (var i = 0; i < this.feature_positions[slot].length; i++) { @@ -404,8 +404,8 @@ _.extend(FeaturePainter.prototype, { * a FeaturePositionMapper object with information about where features were drawn. */ draw: function(ctx, width, height, w_scale, slots) { - var data = this.data, - view_start = this.view_start, + var data = this.data, + view_start = this.view_start, view_end = this.view_end; ctx.save(); @@ -423,11 +423,11 @@ _.extend(FeaturePainter.prototype, { feature_uid = feature[0], feature_start = feature[1], feature_end = feature[2], - // Slot valid only if features are slotted and this feature is slotted; + // Slot valid only if features are slotted and this feature is slotted; // feature may not be due to lack of space. slot = (slots && slots[feature_uid] !== undefined ? slots[feature_uid].slot : null); - - // Draw feature if (a) mode is dense or feature is slotted (as it must be for all non-dense modes) and + + // Draw feature if (a) mode is dense or feature is slotted (as it must be for all non-dense modes) and // (b) there's overlap between the feature and drawing region. if ( (this.mode === "Dense" || slot !== null) && ( feature_start < view_end && feature_end > view_start ) ) { x_draw_coords = this.draw_element(ctx, this.mode, feature, slot, view_start, view_end, w_scale, y_scale, width); @@ -449,7 +449,7 @@ _.extend(FeaturePainter.prototype, { }); }, - /** + /** * Abstract function for drawing an individual feature. */ draw_element: function(ctx, mode, feature, slot, tile_low, tile_high, w_scale, y_scale, width ) { @@ -486,7 +486,7 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { get_row_height: function() { var mode = this.mode, height; if (mode === "Dense") { - height = DENSE_TRACK_HEIGHT; + height = DENSE_TRACK_HEIGHT; } else if (mode === "no_detail") { height = NO_DETAIL_TRACK_HEIGHT; @@ -520,20 +520,20 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { // For now, assume '.' === '+' block_color = (!feature_strand || feature_strand === "+" || feature_strand === "." ? this.prefs.block_color : this.prefs.reverse_strand_color); label_color = this.prefs.label_color; - + // Set global alpha. ctx.globalAlpha = this.alpha_scaler.gen_val(feature); - + // In dense mode, put all data in top slot. if (mode === "Dense") { slot = 1; } - + if (mode === "no_detail") { // No details for feature, so only one way to display. ctx.fillStyle = block_color; ctx.fillRect(f_start, y_start + 5, f_end - f_start, NO_DETAIL_FEATURE_HEIGHT); - } + } else { // Mode is either Squish or Pack: // Feature details. var feature_ts = feature[5], @@ -541,12 +541,12 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { feature_blocks = feature[7], // Whether we are drawing full height or squished features full_height = true; - + if (feature_ts && feature_te) { thick_start = Math.floor( Math.max(0, (feature_ts - tile_low) * w_scale) ); thick_end = Math.ceil( Math.min(width, Math.max(0, (feature_te - tile_low) * w_scale)) ); } - + // Set vars that depend on mode. var thin_height, thick_height; if (mode === "Squish" ) { @@ -560,7 +560,7 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { thin_height = 5; thick_height = PACK_FEATURE_HEIGHT; } - + // Draw feature/feature blocks + connectors. if (!feature_blocks) { // If there are no blocks, treat the feature as one big exon. @@ -575,15 +575,15 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { } ctx.fillRect(f_start, y_start + 1, f_end - f_start, thick_height); } - } else { + } else { // // There are feature blocks and mode is either Squish or Pack. // - // Approach: (a) draw whole feature as connector/intron and (b) draw blocks as + // Approach: (a) draw whole feature as connector/intron and (b) draw blocks as // needed. This ensures that whole feature, regardless of whether it starts with // a block, is visible. // - + // Compute y axis start position and height var cur_y_start, cur_height; if (mode === "Squish" || mode === "Dense") { @@ -620,7 +620,7 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { } ctx.fillRect(f_start, cur_y_start, 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++) { @@ -630,7 +630,7 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { block_end = Math.ceil( Math.min(width, Math.max((block[1] - tile_low - 0.5) * w_scale)) ), last_block_start, last_block_end; - // Skip drawing if block not on tile. + // Skip drawing if block not on tile. if (block_start > block_end) { continue; } // Draw thin block. @@ -666,15 +666,15 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { last_block_start = block_start; last_block_end = block_end; } - + // FIXME: Height scaling only works in Pack mode right now. if (mode === "Pack") { // Reset alpha so height scaling is not impacted by alpha scaling. ctx.globalAlpha = 1; - + // Height scaling: draw white lines to reduce height according to height scale factor. ctx.fillStyle = "white"; // TODO: set this to background color. - var + var hscale_factor = this.height_scaler.gen_val(feature), // Ceil ensures that min height is >= 1. new_height = Math.ceil(thick_height * hscale_factor), @@ -682,13 +682,13 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { if (hscale_factor !== 1) { ctx.fillRect(f_start, cur_y_start + 1, f_end - f_start, ws_height); ctx.fillRect(f_start, cur_y_start + thick_height - ws_height + 1, f_end - f_start, ws_height); - } - } + } + } } - + // Reset alpha so that label is not transparent. ctx.globalAlpha = 1; - + // Draw label for Pack mode. if (feature_name && mode === "Pack" && feature_start > tile_low) { ctx.fillStyle = label_color; @@ -705,10 +705,10 @@ _.extend(LinkedFeaturePainter.prototype, FeaturePainter.prototype, { //ctx.fillStyle = block_color; } } - + // Reset global alpha. ctx.globalAlpha = 1; - + return [draw_start, draw_end]; } }); @@ -726,7 +726,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { get_row_height: function() { var height, mode = this.mode; if (mode === "Dense") { - height = DENSE_TRACK_HEIGHT; + height = DENSE_TRACK_HEIGHT; } else if (mode === "Squish") { height = SQUISH_TRACK_HEIGHT; @@ -821,7 +821,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { drawing_blocks = [], s_start, s_end; - + // Keep list of items that need to be drawn on top of initial drawing layer. var draw_last = []; @@ -837,7 +837,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { if (is_overlap([feature_start + block[0], feature_start + block[1]], tile_region)) { s_start = get_draw_coord(feature_start + block[0]); s_end = get_draw_coord(feature_start + block[1]); - + // Make sure that block is drawn even if it too small to be rendered officially; in this case, // read is drawn at 1px. // TODO: need to ensure that s_start, s_end are calculated the same for both slotting @@ -861,7 +861,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { var seq_start = feature_start + base_offset; s_start = get_draw_coord(seq_start); s_end = get_draw_coord(seq_start + cig_len); - + // Skip feature if it's not in tile. if (!is_overlap([seq_start, seq_start + cig_len], tile_region)) { // Update offsets. @@ -869,7 +869,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { seq_offset = update_seq_offset(seq_offset, cig_op, cig_len); continue; } - + // Make sure that read is drawn even if it too small to be rendered officially; in this case, // read is drawn at 1px. // TODO: need to ensure that s_start, s_end are calculated the same for both slotting @@ -877,8 +877,8 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { if (s_start === s_end) { s_end += 1; } - - // Draw read feature. + + // Draw read feature. switch (cig_op) { case "H": // Hard clipping. case "S": // Soft clipping. @@ -932,7 +932,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { // Move forward in sequence only if sequence used to get mismatches. if (cig_op === 'X') { seq_offset += cig_len; } base_offset += cig_len; - + break; case "N": // Skipped bases. ctx.fillStyle = CONNECTOR_COLOR; @@ -948,7 +948,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { // Check to see if sequence should be drawn at all by looking at the overlap between // the sequence region and the tile region. var insert_x_coord = s_start - gap; - + if (is_overlap([seq_start, seq_start + cig_len], tile_region)) { var seq = read_seq.slice(seq_offset, seq_offset + cig_len); // Insertion point is between the sequence start and the previous base: (-gap) moves @@ -992,7 +992,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { // Draw block. ctx.fillStyle = "yellow"; // TODO: This is a pretty hack-ish way to fill rectangle based on mode. - ctx.fillRect(x_center, y_start + (this.mode !== "Dense" ? 2 : 5), + ctx.fillRect(x_center, y_start + (this.mode !== "Dense" ? 2 : 5), s_end - s_start, (mode !== "Dense" ? SQUISH_FEATURE_HEIGHT : DENSE_FEATURE_HEIGHT)); } } @@ -1011,7 +1011,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { break; } } - + // // Draw last items. // @@ -1032,7 +1032,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { } } }, - + /** * Draw a complete read pair */ @@ -1048,7 +1048,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { y_start = (mode === "Dense" ? 0 : (0 + slot)) * y_scale, draw_height = (mode === 'Pack' ? PACK_FEATURE_HEIGHT : SQUISH_FEATURE_HEIGHT), label_color = this.prefs.label_color; - + // Draw read. if (feature[5] instanceof Array) { // Read is paired. @@ -1097,7 +1097,7 @@ _.extend(ReadPainter.prototype, FeaturePainter.prototype, { ctx.fillText(feature_name, f_start - LABEL_SPACING, y_start + 9, this.max_label_length); } } - + // FIXME: provide actual coordinates for drawn read. return [0,0]; } @@ -1122,7 +1122,7 @@ _.extend(ArcLinkedFeaturePainter.prototype, FeaturePainter.prototype, LinkedFeat return longest_feature_length; }, - get_top_padding: function( width ) { + 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 ) ); @@ -1131,7 +1131,7 @@ _.extend(ArcLinkedFeaturePainter.prototype, FeaturePainter.prototype, LinkedFeat draw_connector: function( ctx, block1_start, block1_end, block2_start, block2_end, y_start ) { // Arc drawing -- from closest endpoints var x_center = ( block1_end + block2_start ) / 2, - radius = block2_start - x_center; + radius = block2_start - x_center; // For full half circles var angle1 = Math.PI, angle2 = 0; if ( radius > 0 ) { @@ -1290,32 +1290,33 @@ SplitRamp.prototype.map_value = function( value ) { var DiagonalHeatmapPainter = function(data, view_start, view_end, prefs, mode) { Painter.call( this, data, view_start, view_end, prefs, mode ); var i, len; + if ( this.prefs.min_value === undefined ) { var min_value = Infinity; for (i = 0, len = this.data.length; i < len; i++) { - min_value = Math.min( min_value, this.data[i][5] ); + min_value = Math.min( min_value, this.data[i][6] ); } this.prefs.min_value = min_value; } if ( this.prefs.max_value === undefined ) { var max_value = -Infinity; for (i = 0, len = this.data.length; i < len; i++) { - max_value = Math.max( max_value, this.data[i][5] ); + max_value = Math.max( max_value, this.data[i][6] ); } this.prefs.max_value = max_value; } }; -DiagonalHeatmapPainter.prototype.default_prefs = { - min_value: undefined, - max_value: undefined, - mode: "Heatmap", +DiagonalHeatmapPainter.prototype.default_prefs = { + min_value: undefined, + max_value: undefined, + mode: "Heatmap", pos_color: "#FF8C00", - neg_color: "#4169E1" + neg_color: "#4169E1" }; DiagonalHeatmapPainter.prototype.draw = function(ctx, width, height, w_scale) { - var + var min_value = this.prefs.min_value, max_value = this.prefs.max_value, value_range = max_value - min_value, @@ -1336,15 +1337,11 @@ DiagonalHeatmapPainter.prototype.draw = function(ctx, width, height, w_scale) { // Draw into triangle, then rotate and scale ctx.rotate(-45 * Math.PI / 180); ctx.scale( invsqrt2, invsqrt2 ); - + // Paint track. for (var i = 0, len = data.length; i < len; i++) { - d = data[i]; - // Ensure the cell is visible - // if ( ) - s1 = scale( d[1] ); e1 = scale( d[2] ); s2 = scale( d[4] ); @@ -1352,10 +1349,9 @@ DiagonalHeatmapPainter.prototype.draw = function(ctx, width, height, w_scale) { value = d[6]; ctx.fillStyle = ( ramp.map_value( value ) ); - ctx.fillRect( s1, s2, ( e1 - s1 ), ( e2 - s2 ) ); } - + ctx.restore(); }; @@ -1372,7 +1368,7 @@ var ReadPainterUtils = function(ctx, row_height, px_per_base, mode) { _.extend(ReadPainterUtils.prototype, { /** - * Draw deletion of base(s). + * Draw deletion of base(s). * @param draw_detail if true, drawing in detail and deletion is drawn more subtly */ draw_deletion: function(x, y, len) { @@ -1399,7 +1395,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, { get_row_height: function() { var mode = this.mode, height; if (mode === "Dense") { - height = DENSE_TRACK_HEIGHT; + height = DENSE_TRACK_HEIGHT; } else if (mode === "Squish") { height = SQUISH_TRACK_HEIGHT; @@ -1431,8 +1427,8 @@ _.extend(VariantPainter.prototype, Painter.prototype, { draw: function(ctx, width, height, w_scale) { ctx.save(); - var - /** + var + /** * Returns dictionary of information about an indel; returns empty if there no indel. Assumes indel is left-aligned. * Dict attributes: * -type: 'insertion' or 'deletion' @@ -1485,7 +1481,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, { row_height = (this.mode === 'Squish' ? SQUISH_TRACK_HEIGHT : PACK_TRACK_HEIGHT), // If zoomed out, fill the whole row with feature to make it easier to read; // when zoomed in, use feature height so that there are gaps in sample rows. - feature_height = (w_scale < 0.1 ? + feature_height = (w_scale < 0.1 ? row_height : (this.mode === 'Squish' ? SQUISH_FEATURE_HEIGHT : PACK_FEATURE_HEIGHT) ), @@ -1495,7 +1491,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, { // If there's a single sample, update drawing variables. if (num_samples === 1) { - row_height = feature_height = + row_height = feature_height = (w_scale < ctx.canvas.manager.char_width_px ? this.prefs.summary_height : row_height); paint_utils.row_height = row_height; // No summary when there's a single sample. @@ -1519,7 +1515,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, { alt = [ locus_data[4].split(',') ]; sample_gts = locus_data[7].split(','); allele_counts = locus_data.slice(8); - + // Process alterate values to derive information about each alt. alt = _.map(_.flatten(alt), function(a) { var alt_info = { @@ -1528,7 +1524,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, { start: 0 }, indel_info = get_indel_info(ref, a); - + return _.extend(alt_info, indel_info); }); @@ -1541,7 +1537,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, { if (draw_summary) { ctx.fillStyle = '#999999'; ctx.globalAlpha = 1; - for (j = 0; j < alt.length; j++) { + for (j = 0; j < alt.length; j++) { // Draw background for summary. draw_x_start = this.get_start_draw_pos(pos + alt[j].start, w_scale); ctx.fillRect(draw_x_start, 0, base_px, this.prefs.summary_height); @@ -1564,7 +1560,7 @@ _.extend(VariantPainter.prototype, Painter.prototype, { draw_y_start = (draw_summary ? this.prefs.summary_height + this.divider_height : 0); for (j = 0; j < sample_gts.length; j++, draw_y_start += row_height) { genotype = (sample_gts[j] ? sample_gts[j].split(/\/|\|/) : ['0', '0']); - + // Get variant to draw and set drawing properties. variant = null; if (genotype[0] === genotype[1]) { diff --git a/client/galaxy/scripts/viz/trackster/tracks.js b/client/galaxy/scripts/viz/trackster/tracks.js index f3719d6c2ef..31c8d3dfcff 100644 --- a/client/galaxy/scripts/viz/trackster/tracks.js +++ b/client/galaxy/scripts/viz/trackster/tracks.js @@ -1,6 +1,6 @@ -define( ["libs/underscore", "viz/visualization", "viz/viz_views", "viz/trackster/util", +define( ["libs/underscore", "viz/visualization", "viz/viz_views", "viz/trackster/util", "viz/trackster/slotting", "viz/trackster/painters", "viz/trackster/filters", - "mvc/data", "mvc/tools", "utils/config" ], + "mvc/data", "mvc/tools", "utils/config" ], function(_, visualization, viz_views, util, slotting, painters, filters_mod, data, tools_mod, config_mod) { var extend = _.extend; @@ -20,22 +20,22 @@ var is_container = function(element, obj) { html_elt_js_obj_dict[element.attr("id")] = obj; }; -/** +/** * Make `element` moveable within parent and sibling elements by dragging `handle` (a selector). * Function manages JS objects, containers as well. * * @param element HTML element to make moveable * @param handle_class classname that denotes HTML element to be used as handle * @param container_selector selector used to identify possible containers for this element - * @param element_js_obj JavaScript object associated with element; used + * @param element_js_obj JavaScript object associated with element; used */ var moveable = function(element, handle_class, container_selector, element_js_obj) { // HACK: set default value for container selector. container_selector = ".group"; - + // Register element with its object. html_elt_js_obj_dict[element.attr("id")] = element_js_obj; - + // Need to provide selector for handle, not class. element.bind( "drag", { handle: "." + handle_class, relative: true }, function ( e, d ) { var element = $(this), @@ -48,12 +48,12 @@ var moveable = function(element, handle_class, container_selector, element_js_ob top, bottom, i; - + // - // Enable three types of dragging: (a) out of container; (b) into container; + // Enable three types of dragging: (a) out of container; (b) into container; // (c) sibling movement, aka sorting. Handle in this order for simplicity. // - + // Handle dragging out of container. container = $(this).parents(container_selector); if (container.length !== 0) { @@ -73,9 +73,9 @@ var moveable = function(element, handle_class, container_selector, element_js_ob cur_container.remove_drawable(this_obj); cur_container.container.add_drawable(this_obj); return; - } + } } - + // Handle dragging into container. Child is appended to container's content_div. container = null; for ( i = 0; i < children.length; i++ ) { @@ -83,7 +83,7 @@ var moveable = function(element, handle_class, container_selector, element_js_ob top = child.position().top; bottom = top + child.outerHeight(); // Dragging into container if child is a container and offset is inside container. - if ( child.is(container_selector) && this !== child.get(0) && + if ( child.is(container_selector) && this !== child.get(0) && d.offsetY >= top && d.offsetY <= bottom ) { // Append/prepend based on where offsetY is closest to and return. if (d.offsetY - top < bottom - d.offsetY) { @@ -94,7 +94,7 @@ var moveable = function(element, handle_class, container_selector, element_js_ob } // Update containers. Object may not have container if it is being moved quickly. if (this_obj.container) { - this_obj.container.remove_drawable(this_obj); + this_obj.container.remove_drawable(this_obj); } html_elt_js_obj_dict[child.attr("id")].add_drawable(this_obj); return; @@ -102,7 +102,7 @@ var moveable = function(element, handle_class, container_selector, element_js_ob } // Handle sibling movement, aka sorting. - + // Determine new position for ( i = 0; i < children.length; i++ ) { child = $(children.get(i)); @@ -112,9 +112,9 @@ var moveable = function(element, handle_class, container_selector, element_js_ob break; } } - - // If not already in the right place, move. Need - // to handle the end specially since we don't have + + // If not already in the right place, move. Need + // to handle the end specially since we don't have // insert at index if ( i === children.length ) { if ( this !== children.get(i - 1) ) { @@ -124,7 +124,7 @@ var moveable = function(element, handle_class, container_selector, element_js_ob } else if ( this !== children.get(i) ) { $(this).insertBefore( children.get(i) ); - // Need to adjust insert position if moving down because move is changing + // Need to adjust insert position if moving down because move is changing // indices of all list items. html_elt_js_obj_dict[parent.attr("id")].move_drawable(this_obj, (d.deltaY > 0 ? i-1 : i) ); } @@ -138,7 +138,7 @@ var moveable = function(element, handle_class, container_selector, element_js_ob /** * Init constants & functions used throughout trackster. */ -var +var // Padding at the top of tracks for error messages ERROR_PADDING = 20, // Maximum number of rows un a slotted track @@ -153,7 +153,7 @@ var DATA_ERROR = "Cannot display dataset due to an error. ", DATA_NOCONVERTER = "A converter for this dataset is not installed. Please check your datatypes_conf.xml file.", DATA_NONE = "No data for this chrom/contig.", - DATA_PENDING = "Preparing data. This can take a while for a large dataset. " + + DATA_PENDING = "Preparing data. This can take a while for a large dataset. " + "If the visualization is saved and closed, preparation will continue in the background.", DATA_CANNOT_RUN_TOOL = "Tool cannot be rerun: ", DATA_LOADING = "Loading data...", @@ -163,7 +163,7 @@ var // Numerical/continuous data display modes. CONTINUOUS_DATA_MODES = ["Histogram", "Line", "Filled", "Intensity"]; - + /** * Round a number to a given number of decimal places. */ @@ -172,7 +172,7 @@ function round(num, places) { if (!places) { places = 0; } - + var val = Math.pow(10, places); return Math.round(num * val) / val; } @@ -188,13 +188,13 @@ function round(num, places) { */ /** - * Base class for all drawable objects. Drawable objects are associated with a view and live in a + * Base class for all drawable objects. Drawable objects are associated with a view and live in a * container. They have the following HTML elements and structure: *"+a.message+"",buttons:{Close:function(){Galaxy.modal.hide()}}})})),d.append($("").text(" ")),d.append($("").text("Try again").click(function(){b.init(!0)})))}}),this.update_icons(),c}},predraw_init:function(){var a=this;return $.getJSON(a.dataset.url(),{data_type:"data",stats:!0,chrom:a.view.chrom,low:0,high:a.view.max_high,hda_ldda:a.dataset.get("hda_ldda")},function(b){var c=b.data;if(void 0!==c&&void 0!==c.min&&void 0!==c.max){var d=c.min,e=c.max;d=Math.floor(Math.min(0,Math.max(d,c.mean-2*c.sd))),e=Math.ceil(Math.max(0,Math.min(e,c.mean+2*c.sd))),a.config.set_default_value("min_value",d),a.config.set_default_value("max_value",e),a.config.set_value("min_value",d),a.config.set_value("max_value",e)}})},get_drawables:function(){return this}});var O=function(c,d,e){N.call(this,c,d,e);var f=this;if(n(f.container_div,f.drag_handle_class,".group",f),this.filters_manager=new g.FiltersManager(this,"filters"in e?e.filters:null),this.data_manager.set("filters_manager",this.filters_manager),this.filters_available=!1,this.tool=e.tool?new G(a.extend(e.tool,{track:this,tool_state:e.tool_state})):null,this.tile_cache=new b.Cache(A),this.left_offset=0,this.header_div&&(this.set_filters_manager(this.filters_manager),this.tool)){var h=new I({model:this.tool});h.render(),this.dynamic_tool_div=h.$el,this.header_div.after(this.dynamic_tool_div)}this.tiles_div=$("").addClass("tiles").appendTo(this.content_div),this.config.get_value("content_visible")||this.tiles_div.hide(),this.overlay_div=$("").addClass("overlay").appendTo(this.content_div),e.mode&&this.change_mode(e.mode)};k(O.prototype,C.prototype,N.prototype,{action_icons_def:N.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(a){$(".tooltip").remove(),a.slotters[a.view.resolution_px_b].max_rows*=2,a.request_draw({clear_tile_cache:!0})},hide:!0}]),copy:function(a){var b=this.to_dict();k(b,{data_manager:this.data_manager});var c=new this.constructor(this.view,a,b);return c.change_mode(this.mode),c.enabled=this.enabled,c},set_filters_manager:function(a){this.filters_manager=a,this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),dataset:{id:this.dataset.id,hda_ldda:this.dataset.get("hda_ldda")},prefs:this.config.to_key_value_dict(),mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:this.tool?this.tool.state_dict():{}}},set_min_max:function(){var a=this;return $.getJSON(a.dataset.url(),{data_type:"data",stats:!0,chrom:a.view.chrom,low:0,high:a.view.max_high,hda_ldda:a.dataset.get("hda_ldda")},function(b){var c=b.data;if(isNaN(parseFloat(a.config.get_value("min_value")))||isNaN(parseFloat(a.config.get_value("max_value")))){var d=c.min,e=c.max;d=Math.floor(Math.min(0,Math.max(d,c.mean-2*c.sd))),e=Math.ceil(Math.max(0,Math.min(e,c.mean+2*c.sd))),a.config.set_value("min_value",d),a.config.set_value("max_value",e)}})},change_mode:function(a){var b=this;return b.mode=a,b.config.set_value("mode",a),"Auto"===a&&this.data_manager.clear(),b.request_draw({clear_tile_cache:!0}),this.action_icons.mode_icon.attr("title","Set display mode (now: "+b.mode+")"),b},update_icons:function(){var a=this;a.action_icons.filters_icon.toggle(a.filters_available),a.action_icons.tools_icon.toggle(null!==a.tool),a.action_icons.param_space_viz_icon.toggle(null!==a.tool)},_gen_tile_cache_key:function(a,b){return a+"_"+b},request_draw:function(a){a&&a.clear_tile_cache&&this.tile_cache.clear(),this.view.request_redraw(a,this)},before_draw:function(){this.max_height_px=0},_draw:function(c){if(this.can_draw()){var d=c&&c.clear_after,e=this.view.low,f=this.view.high,g=this.view.container.width(),h=this.view.resolution_px_b,i=1/h;this.is_overview&&(e=this.view.max_low,f=this.view.max_high,h=g/(view.max_high-view.max_low),i=1/h),this.before_draw(),this.tiles_div.children().addClass("remove");for(var j,k,l=Math.floor(r*i),m=Math.floor(e/l),n=[],o=[];f>m*l;)j=new b.GenomeRegion({chrom:this.view.chrom,start:m*l,end:Math.min((m+1)*l,this.view.max_high)}),k=this.draw_helper(j,h,c),n.push(k),$.when(k).then(function(a){o.push(a)}),m+=1;d||this.tiles_div.children(".remove").removeClass("remove").remove();var p=this;$.when.apply($,n).then(function(){p.tiles_div.children(".remove").remove(),o=a.filter(o,function(a){return null!==a}),0!==o.length&&p.postdraw_actions(o,g,h,d)})}},_add_yaxis_label:function(a,b){var c=this,d="max"===a?"top":"bottom",e="max"===a?"max":"min",f="max"===a?"max_value":"min_value",g=this.container_div.find(".yaxislabel."+d);b=b||function(){c.request_draw({clear_tile_cache:!0})},0!==g.length?g.text(c.config.get_value(f)):(g=$("").text(c.config.get_value(f)).make_text_editable({num_cols:12,on_finish:function(a){$(".tooltip").remove(),c.config.set_value(f,a),b()},help_text:"Set "+e+" value"}).addClass("yaxislabel "+d).css("color",this.config.get_value("label_color")),this.container_div.prepend(g))},postdraw_actions:function(b,c,d){var e=a.filter(b,function(a){return a instanceof L});if(e.length>0){this.max_height_px=0;var f=this;a.each(b,function(a){a instanceof L||(a.html_elt.remove(),f.draw_helper(a.region,d,{force:!0,mode:"Coverage"}))}),f._add_yaxis_label("max")}else{this.container_div.find(".yaxislabel").remove();var g=a.find(b,function(a){return a.has_icons});g&&a.each(b,function(a){a.has_icons||a.html_elt.css("padding-top",o)})}},get_mode:function(){return this.mode},update_auto_mode:function(){},_get_drawables:function(){return[this]},draw_helper:function(b,c,e){e||(e={});var f=e.force,g=e.mode||this.mode,h=1/c,i=this,j=this._get_drawables(),k=this._gen_tile_cache_key(c,b),l=function(a){return a&&"track"in a},m=f?void 0:i.tile_cache.get_elt(k);if(m)return l(m)&&i.show_tile(m,c),m;if(e.data_fetch===!1)return null;var n=function(){var c=a.find(B,function(a){return a===g})?"Coverage":g,d=a.map(j,function(a){return a.data_manager.get_data(b,c,h,i.data_url_extra_params)});return view.reference_track&&d.push(view.reference_track.data_manager.get_data(b,g,h,view.reference_track.data_url_extra_params)),d},o=$.Deferred();return i.tile_cache.set_elt(k,o),$.when.apply($,n()).then(function(){var f,g=n(),h=g;if(a.find(g,function(a){return d.is_deferred(a)}))return i.tile_cache.set_elt(k,void 0),void $.when(i.draw_helper(b,c,e)).then(function(a){o.resolve(a)});view.reference_track&&(f=view.reference_track.data_manager.subset_entry(g.pop(),b));var l=[],m=[];a.each(j,function(a,b){var d=a.mode,e=h[b];"Auto"===d&&(d=a.get_mode(e),a.update_auto_mode(d)),l.push(d),m.push(a.get_canvas_height(e,d,c,t))});var p,q=i.view.canvas_manager.new_canvas(),r=b.get("start"),s=b.get("end"),t=Math.ceil((s-r)*c)+i.left_offset,u=a.max(m);q.width=t,q.height=e.height||u;var v=q.getContext("2d");v.translate(i.left_offset,0),j.length>1&&(v.globalAlpha=.5,v.globalCompositeOperation="source-over"),a.each(j,function(a,d){p=a.draw_tile(h[d],v,l[d],b,c,f)}),void 0!==p&&(i.tile_cache.set_elt(k,p),i.show_tile(p,c)),o.resolve(p)}),o},get_canvas_height:function(){return this.visible_height_px},_draw_line_track_tile:function(a,b,c,d,e){var g=b.canvas,h=new f.LinePainter(a.data,d.get("start"),d.get("end"),this.config.to_key_value_dict(),c);return h.draw(b,g.width,g.height,e),new L(this,d,e,g,a.data)},draw_tile:function(){},show_tile:function(a,b){var c=a.html_elt;a.predisplay_actions();var d=Math.round((a.low-(this.is_overview?this.view.max_low:this.view.low))*b);this.left_offset&&(d-=this.left_offset),c.css("left",d),c.hasClass("remove")?c.removeClass("remove"):this.tiles_div.append(c),c.css("height","auto"),this.max_height_px=Math.max(this.max_height_px,c.height()-2),c.parent().children().css("height",this.max_height_px+"px");var e=this.max_height_px;0!==this.visible_height_px&&(e=Math.min(this.max_height_px,this.visible_height_px)),this.tiles_div.css("height",e+"px")},tool_region_and_parameters_str:function(b){var c=this,d=void 0!==b?b.toString():"all",e=a.values(c.tool.get_inputs_dict()).join(", ");return" - region=["+d+"], parameters=["+e+"]"},data_and_mode_compatible:function(a,b){return"Auto"===b?!0:"Coverage"===b?"bigwig"===a.dataset_type:"bigwig"===a.dataset_type||"no_detail"===a.extra_info?!1:!0},can_subset:function(a){return a.message||"no_detail"===a.extra_info?!1:"bigwig"===a.dataset_type?a.data[1][0]-a.data[0][0]===1:!0},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data"),this.data_query_wait=1e3,this.dataset_check_type="state"}});var P=function(a,b){N.call(this,a,b,{resize:!1,header:!1}),this.container_div.addClass("label-track")};k(P.prototype,N.prototype,{init:function(){this.enabled=!0},predraw_init:function(){},_draw:function(){for(var a=this.view,b=a.high-a.low,c=Math.floor(Math.pow(10,Math.floor(Math.log(b)/Math.log(10)))),d=Math.floor(a.low/c)*c,e=this.view.container.width(),f=$("").addClass("label-container");d
"+a.message+"",buttons:{Close:function(){Galaxy.modal.hide()}}})})),d.append($("").text(" ")),d.append($("").text("Try again").click(function(){b.init(!0)})))}}),this.update_icons(),c}},predraw_init:function(){var a=this;return $.getJSON(a.dataset.url(),{data_type:"data",stats:!0,chrom:a.view.chrom,low:0,high:a.view.max_high,hda_ldda:a.dataset.get("hda_ldda")},function(b){var c=b.data;if(void 0!==c&&void 0!==c.min&&void 0!==c.max){var d=c.min,e=c.max;d=Math.floor(Math.min(0,Math.max(d,c.mean-2*c.sd))),e=Math.ceil(Math.max(0,Math.min(e,c.mean+2*c.sd))),a.config.set_default_value("min_value",d),a.config.set_default_value("max_value",e),a.config.set_value("min_value",d),a.config.set_value("max_value",e)}})},get_drawables:function(){return this}});var O=function(c,d,e){N.call(this,c,d,e);var f=this;if(n(f.container_div,f.drag_handle_class,".group",f),this.filters_manager=new g.FiltersManager(this,"filters"in e?e.filters:null),this.data_manager.set("filters_manager",this.filters_manager),this.filters_available=!1,this.tool=e.tool?new G(a.extend(e.tool,{track:this,tool_state:e.tool_state})):null,this.tile_cache=new b.Cache(A),this.left_offset=0,this.header_div&&(this.set_filters_manager(this.filters_manager),this.tool)){var h=new I({model:this.tool});h.render(),this.dynamic_tool_div=h.$el,this.header_div.after(this.dynamic_tool_div)}this.tiles_div=$("").addClass("tiles").appendTo(this.content_div),this.config.get_value("content_visible")||this.tiles_div.hide(),this.overlay_div=$("").addClass("overlay").appendTo(this.content_div),e.mode&&this.change_mode(e.mode)};k(O.prototype,C.prototype,N.prototype,{action_icons_def:N.prototype.action_icons_def.concat([{name:"show_more_rows_icon",title:"To minimize track height, not all feature rows are displayed. Click to display more rows.",css_class:"exclamation",on_click_fn:function(a){$(".tooltip").remove(),a.slotters[a.view.resolution_px_b].max_rows*=2,a.request_draw({clear_tile_cache:!0})},hide:!0}]),copy:function(a){var b=this.to_dict();k(b,{data_manager:this.data_manager});var c=new this.constructor(this.view,a,b);return c.change_mode(this.mode),c.enabled=this.enabled,c},set_filters_manager:function(a){this.filters_manager=a,this.header_div.after(this.filters_manager.parent_div)},to_dict:function(){return{track_type:this.get_type(),dataset:{id:this.dataset.id,hda_ldda:this.dataset.get("hda_ldda")},prefs:this.config.to_key_value_dict(),mode:this.mode,filters:this.filters_manager.to_dict(),tool_state:this.tool?this.tool.state_dict():{}}},set_min_max:function(){var a=this;return $.getJSON(a.dataset.url(),{data_type:"data",stats:!0,chrom:a.view.chrom,low:0,high:a.view.max_high,hda_ldda:a.dataset.get("hda_ldda")},function(b){var c=b.data;if(isNaN(parseFloat(a.config.get_value("min_value")))||isNaN(parseFloat(a.config.get_value("max_value")))){var d=c.min,e=c.max;d=Math.floor(Math.min(0,Math.max(d,c.mean-2*c.sd))),e=Math.ceil(Math.max(0,Math.min(e,c.mean+2*c.sd))),a.config.set_value("min_value",d),a.config.set_value("max_value",e)}})},change_mode:function(a){var b=this;return b.mode=a,b.config.set_value("mode",a),"Auto"===a&&this.data_manager.clear(),b.request_draw({clear_tile_cache:!0}),this.action_icons.mode_icon.attr("title","Set display mode (now: "+b.mode+")"),b},update_icons:function(){var a=this;a.action_icons.filters_icon.toggle(a.filters_available),a.action_icons.tools_icon.toggle(null!==a.tool),a.action_icons.param_space_viz_icon.toggle(null!==a.tool)},_gen_tile_cache_key:function(a,b){return a+"_"+b},request_draw:function(a){a&&a.clear_tile_cache&&this.tile_cache.clear(),this.view.request_redraw(a,this)},before_draw:function(){this.max_height_px=0},_draw:function(c){if(this.can_draw()){var d=c&&c.clear_after,e=this.view.low,f=this.view.high,g=this.view.container.width(),h=this.view.resolution_px_b,i=1/h;this.is_overview&&(e=this.view.max_low,f=this.view.max_high,h=g/(view.max_high-view.max_low),i=1/h),this.before_draw(),this.tiles_div.children().addClass("remove");for(var j,k,l=Math.floor(r*i),m=Math.floor(e/l),n=[],o=[];f>m*l;)j=new b.GenomeRegion({chrom:this.view.chrom,start:m*l,end:Math.min((m+1)*l,this.view.max_high)}),k=this.draw_helper(j,h,c),n.push(k),$.when(k).then(function(a){o.push(a)}),m+=1;d||this.tiles_div.children(".remove").removeClass("remove").remove();var p=this;$.when.apply($,n).then(function(){p.tiles_div.children(".remove").remove(),o=a.filter(o,function(a){return null!==a}),0!==o.length&&p.postdraw_actions(o,g,h,d)})}},_add_yaxis_label:function(a,b){var c=this,d="max"===a?"top":"bottom",e="max"===a?"max":"min",f="max"===a?"max_value":"min_value",g=this.container_div.find(".yaxislabel."+d);b=b||function(){c.request_draw({clear_tile_cache:!0})},0!==g.length?g.text(c.config.get_value(f)):(g=$("").text(c.config.get_value(f)).make_text_editable({num_cols:12,on_finish:function(a){$(".tooltip").remove(),c.config.set_value(f,a),b()},help_text:"Set "+e+" value"}).addClass("yaxislabel "+d).css("color",this.config.get_value("label_color")),this.container_div.prepend(g))},postdraw_actions:function(b,c,d){var e=a.filter(b,function(a){return a instanceof L});if(e.length>0){this.max_height_px=0;var f=this;a.each(b,function(a){a instanceof L||(a.html_elt.remove(),f.draw_helper(a.region,d,{force:!0,mode:"Coverage"}))}),f._add_yaxis_label("max")}else{this.container_div.find(".yaxislabel").remove();var g=a.find(b,function(a){return a.has_icons});g&&a.each(b,function(a){a.has_icons||a.html_elt.css("padding-top",o)})}},get_mode:function(){return this.mode},update_auto_mode:function(){},_get_drawables:function(){return[this]},draw_helper:function(b,c,e){e||(e={});var f=e.force,g=e.mode||this.mode,h=1/c,i=this,j=this._get_drawables(),k=this._gen_tile_cache_key(c,b),l=function(a){return a&&"track"in a},m=f?void 0:i.tile_cache.get_elt(k);if(m)return l(m)&&i.show_tile(m,c),m;if(e.data_fetch===!1)return null;var n=function(){var c=a.find(B,function(a){return a===g})?"Coverage":g,d=a.map(j,function(a){return a.data_manager.get_data(b,c,h,i.data_url_extra_params)});return view.reference_track&&d.push(view.reference_track.data_manager.get_data(b,g,h,view.reference_track.data_url_extra_params)),d},o=$.Deferred();return i.tile_cache.set_elt(k,o),$.when.apply($,n()).then(function(){var f,g=n(),h=g;if(a.find(g,function(a){return d.is_deferred(a)}))return i.tile_cache.set_elt(k,void 0),void $.when(i.draw_helper(b,c,e)).then(function(a){o.resolve(a)});view.reference_track&&(f=view.reference_track.data_manager.subset_entry(g.pop(),b));var l=[],m=[];a.each(j,function(a,b){var d=a.mode,e=h[b];"Auto"===d&&(d=a.get_mode(e),a.update_auto_mode(d)),l.push(d),m.push(a.get_canvas_height(e,d,c,t))});var p,q=i.view.canvas_manager.new_canvas(),r=b.get("start"),s=b.get("end"),t=Math.ceil((s-r)*c)+i.left_offset,u=a.max(m);q.width=t,q.height=e.height||u;var v=q.getContext("2d");v.translate(i.left_offset,0),j.length>1&&(v.globalAlpha=.5,v.globalCompositeOperation="source-over"),a.each(j,function(a,d){p=a.draw_tile(h[d],v,l[d],b,c,f)}),void 0!==p&&(i.tile_cache.set_elt(k,p),i.show_tile(p,c)),o.resolve(p)}),o},get_canvas_height:function(){return this.visible_height_px},_draw_line_track_tile:function(a,b,c,d,e){var g=b.canvas,h=new f.LinePainter(a.data,d.get("start"),d.get("end"),this.config.to_key_value_dict(),c);return h.draw(b,g.width,g.height,e),new L(this,d,e,g,a.data)},draw_tile:function(){},show_tile:function(a,b){var c=a.html_elt;a.predisplay_actions();var d=Math.round((a.low-(this.is_overview?this.view.max_low:this.view.low))*b);this.left_offset&&(d-=this.left_offset),c.css("left",d),c.hasClass("remove")?c.removeClass("remove"):this.tiles_div.append(c),c.css("height","auto"),this.max_height_px=Math.max(this.max_height_px,c.height()-2),c.parent().children().css("height",this.max_height_px+"px");var e=this.max_height_px;0!==this.visible_height_px&&(e=Math.min(this.max_height_px,this.visible_height_px)),this.tiles_div.css("height",e+"px")},tool_region_and_parameters_str:function(b){var c=this,d=void 0!==b?b.toString():"all",e=a.values(c.tool.get_inputs_dict()).join(", ");return" - region=["+d+"], parameters=["+e+"]"},data_and_mode_compatible:function(a,b){return"Auto"===b?!0:"Coverage"===b?"bigwig"===a.dataset_type:"bigwig"===a.dataset_type||"no_detail"===a.extra_info?!1:!0},can_subset:function(a){return a.message||"no_detail"===a.extra_info?!1:"bigwig"===a.dataset_type?a.data[1][0]-a.data[0][0]===1:!0},init_for_tool_data:function(){this.data_manager.set("data_type","raw_data"),this.data_query_wait=1e3,this.dataset_check_type="state"}});var P=function(a,b){N.call(this,a,b,{resize:!1,header:!1}),this.container_div.addClass("label-track")};k(P.prototype,N.prototype,{init:function(){this.enabled=!0},predraw_init:function(){},_draw:function(){for(var a=this.view,b=a.high-a.low,c=Math.floor(Math.pow(10,Math.floor(Math.log(b)/Math.log(10)))),d=Math.floor(a.low/c)*c,e=this.view.container.width(),f=$("").addClass("label-container");d