<commit_before>define( [
            'dojo/_base/declare',
            'dojo/_base/array',
            'dojo/_base/Color',
            'dojo/dom-construct',
            'dojo/on',
            'JBrowse/View/Track/WiggleBase',
            'JBrowse/View/Track/_YScaleMixin',
            'JBrowse/Util',
            './_Scale',
            'CoGe/View/ColorDialog'
        ],
        function( declare, array, Color, domConstruct, on, WiggleBase, YScaleMixin, Util, Scale, ColorDialog ) {

var XYPlot = declare( [WiggleBase, YScaleMixin], // mdb: this file is a copy of XYPlot, extend that class instead?

/**
 * Wiggle track that shows data with an X-Y plot along the reference.
 *
 * @lends JBrowse.View.Track.Wiggle.XYPlot
 * @extends JBrowse.View.Track.WiggleBase
 */
{
    // Load cookie params - mdb added 1/13/14, issue 279
    constructor: function() {
        this.inherited(arguments); // call superclass constructor

        if (!this.config.style.featureColor)
            this.config.style.featureColor = {};
        if (typeof(this.config.showHoverScores) == "undefined")
        	this.config.showHoverScores = 1;
        if (typeof(this.config.showLabels) == "undefined")
        	this.config.showLabels = 1;
    },

    _defaultConfig: function() {
        return Util.deepUpdate(
            dojo.clone( this.inherited(arguments) ),
            {
                style: {
                    //pos_color: 'blue',
                    //neg_color: 'red',
                    origin_color: '#888',
                    variance_band_color: 'rgba(0,0,0,0.3)',
                },
            }
        );
    },

    _getScaling: function( viewArgs, successCallback, errorCallback ) {

        this._getScalingStats( viewArgs, dojo.hitch(this, function( stats ) {

            //calculate the scaling if necessary
            if( ! this.lastScaling || ! this.lastScaling.sameStats( stats ) ) {

                var scaling = new Scale( this.config, stats );

                // bump minDisplayed to 0 if it is within 0.5% of it
                if( Math.abs( scaling.min / scaling.max ) < 0.005 )
                    scaling.min = 0;

                // update our track y-scale to reflect it
                this.makeYScale({
                    fixBounds: true,
                    min: scaling.min,
                    max: scaling.max
                });

                // and finally adjust the scaling to match the ruler's scale rounding
                scaling.min = this.ruler.scaler.bounds.lower;
                scaling.max = this.ruler.scaler.bounds.upper;
                scaling.range = scaling.max - scaling.min;

                this.lastScaling = scaling;
            }

            successCallback( this.lastScaling );
        }), errorCallback );
    },

    updateStaticElements: function( coords ) {
        //console.log('updateStaticElements');
        this.inherited( arguments );
        this.updateYScaleFromViewDimensions( coords );
    },

    fillTooManyFeaturesMessage: function( blockIndex, block, scale ) {
        this.fillMessage(
            blockIndex,
            block,
            'Too much data to show'
                + (scale >= this.browser.view.maxPxPerBp ? '': '; zoom in to see detail')
                + '.'
        );
    },

    fillMessage: function( blockIndex, block, message, class_ ) {
        domConstruct.empty( block.domNode );
        var msgDiv = dojo.create(
            'div', {
                className: class_ || 'message',
                innerHTML: message
            }, block.domNode );
        this.heightUpdate( dojo.position(msgDiv).h, blockIndex );
    },

    renderBlock: function( args ) {
        var featureScale = this.config.style.featureScale;
        var scale = args.block.scale;
        if (scale <= featureScale) { // don't draw, too zoomed-out, modeled after HTMLFeatures
            this.fillTooManyFeaturesMessage(args.blockIndex, args.block, scale);
        }
        else { // render features
            this.inherited( arguments );
        }
    },

    _draw: function(scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale, pixels, spans) {
        this._preDraw(      scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale );

        this._drawFeatures( scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale );

        if ( spans ) {
            this._maskBySpans( scale, leftBase, rightBase, block, canvas, pixels, dataScale, spans );
        }
        this._postDraw(     scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale );
    },
    /**
     * Draw a set of features on the canvas.
     * @private
     */
    _drawFeatures: function( scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale ) {
        //console.log('_drawFeatures');
        var config = this.config;
        var context = canvas.getContext('2d');
        var canvasHeight = canvas.height;
        var toY = dojo.hitch( this, function( val ) {
           return canvasHeight * ( 1-dataScale.normalize.call(this, val) );
        });
        var originY = toY( dataScale.origin );
        var disableClipMarkers = this.config.disable_clip_markers;

        // Note: transform cases below can be consolidated/optimized
        if (config.transformAverage) {
            var sum_f = [];
            var sum_r = [];
            var count_f = [];
            var count_r = [];
            var width = [];
            dojo.forEach( features, function(f,i) {
                var score = f.get('score');
                var l = featureRects[i].l;
                var w = featureRects[i].w;
                if (score >= 0) {
                    sum_f[l] = l in sum_f ? sum_f[l] + score : score;
                    count_f[l] = l in count_f ? count_f[l] + 1 : 1;
                }
                else {
                    sum_r[l] = l in sum_r ? sum_r[l] + score : score;
                    count_r[l] = l in count_r ? count_r[l] + 1 : 1;
                }
                width[l] = l in width ? Math.max(width[l], w) : w;
            });
            sum_f.forEach( function(x,l) { // bar goes upward
                var avg = sum_f[l]/count_f[l];
                var height = toY( avg );
                context.fillStyle = 'gray';
                if( height <= canvasHeight ) { // if the rectangle is visible at all
                    context.fillRect( l, height, width[l], originY-height+1);
                }
            });
            sum_r.forEach( function(x,l) { // bar goes downward
                var avg = sum_r[l]/count_r[l];
                var height = toY( avg );
                context.fillStyle = 'gray';
                if( height <= canvasHeight ) { // if the rectangle is visible at all
                    context.fillRect( l, originY, width[l], height-originY+1 );
                }
            });
        }
        else if (config.transformDifference) {
            var width   = [];
            var max_f   = []; // forward strand
            var max_r   = []; // reverse strand
            var min_f   = [];
            var min_r   = [];
            var color_f = [];
            var color_r = [];
            var count_f = [];
            var count_r = [];
            dojo.forEach( features, dojo.hitch(this, function(f,i) {
                var score = f.get('score');
                var l = featureRects[i].l;
                var w = featureRects[i].w;
                width[l] = l in width ? Math.max(width[l], w) : w;

                var color = this._getFeatureColor( f.get('id') );

                if (score >= 0) {
                    count_f[l] = l in count_f ? count_f[l] + 1 : 1;
                    max_f[l] = l in max_f ? Math.max(max_f[l], score) : score;
                    min_f[l] = l in min_f ? Math.min(min_f[l], score) : score;
                    if (score >= max_f[l])
                        color_f[l] = color;
                }
                else {
                    count_r[l] = l in count_r ? count_r[l] + 1 : 1;
                    score = Math.abs(score);
                    max_r[l] = l in max_r ? Math.max(max_r[l], score) : score;
                    min_r[l] = l in min_r ? Math.min(min_r[l], score) : score;
                    if (score >= max_r[l])
                        color_r[l] = color;
                }
            }));
            max_f.forEach( function(x,l) { // bar goes upward
                var diff = max_f[l] - min_f[l];
                if (count_f[l] == 1)
                    diff = max_f[l];
                var height = toY(diff);
                if( height <= canvasHeight ) { // if the rectangle is visible at all
                    context.fillStyle = color_f[l];
                    context.fillRect( l, height, width[l], originY-height+1);
                }
            });
            max_r.forEach( function(x,l) { // bar goes downward
                var diff = max_r[l] - min_r[l];
                if (count_r[l] == 1)
                    diff = max_r[l];
                var height = toY(-1*diff);
                if( height <= canvasHeight ) { // if the rectangle is visible at all
                    context.fillStyle = color_r[l];
                    context.fillRect( l, originY, width[l], height-originY+1 );
                }
            });
        }
        else if (config.transformInflate) {
            // sort features by score
            var sorted = [];
            dojo.forEach( features, function(f,i) {
                sorted.push({ feature: f, featureRect: featureRects[i] });
            });
            sorted.sort( sortByScore );

            dojo.forEach( sorted, function(pair,i) {
                var f = pair.feature;
                var fRect = pair.featureRect;
                var score = ( f.get('score') > 0 ? 1 : -1 );

                fRect.t = toY( score );
                if( fRect.t <= canvasHeight ) { // if the rectangle is visible at all
                    var id = f.get('id');
                    context.fillStyle = this._getFeatureColor(id);
                    if (fRect.t <= originY) // bar goes upward
                        context.fillRect( fRect.l, fRect.t, fRect.w, originY-fRect.t+1);
                    else // downward
                        context.fillRect( fRect.l, originY, fRect.w, fRect.t-originY+1 );
                }
            }, this );
        }
        else {
            // sort features by score
            var sorted = [];
            dojo.forEach( features, function(f,i) {
                sorted.push({ feature: f, featureRect: featureRects[i] });
            });
            sorted.sort( sortByScore );

            dojo.forEach( sorted, function(pair,i) {
                var f = pair.feature;
                var fRect = pair.featureRect;
                var score = f.get('score');

                if (config.transformLog10) {
                    if (score >= 0)
                        score = log10(Math.abs(score)+1);
                    else
                        score = -1*log10(Math.abs(score)+1);
                }
                else if (config.transformLog2) {
                    if (score >= 0)
                        score = log2(Math.abs(score)+1);
                    else
                        score = -1*log2(Math.abs(score)+1);
                }

                fRect.t = toY( score );
                if( fRect.t <= canvasHeight ) { // if the rectangle is visible at all
                    var id = f.get('id');
                    context.fillStyle = this._getFeatureColor(id);
                    if (fRect.t <= originY) // bar goes upward
                        context.fillRect( fRect.l, fRect.t, fRect.w, originY-fRect.t+1);
                    else // downward
                        context.fillRect( fRect.l, originY, fRect.w, fRect.t-originY+1 );
                }
            }, this );
            
            // mdb added 4/2/14 - draw labels on top of bars, issue 346
            var prevStart, prevEnd;
            if (this.config.showLabels && scale > this.config.style.labelScale) {
	            dojo.forEach( sorted, function(pair,i) {
	                var f = pair.feature;
	                var fRect = pair.featureRect;
	                var isUpward = (fRect.t <= originY); // bar goes upward
	                var start = f.get('start'); 
	                if (start >= block.startBase && start <= block.endBase) { // print label only for first spanning block
		                var label = f.get('label');
		                if (label && label != '.') {
		                	if (!(start >= prevStart && start <= prevEnd)) { // mdb added 4/15/14 - don't allow overlapping labels, only print the first one
			                	var topOffset = ( isUpward ? fRect.t-12 : fRect.t );
			                    var rulerdiv =
			                        dojo.create('div', 
			                    		{   style: {
			                                	width: '100px',
			                                    position: 'absolute',
			                                    left: fRect.l,
			                                    top: topOffset,
			                                    //zIndex: 10,
			                                },
			                                innerHTML: label
			                            }, canvas.parentNode );
			                    prevStart = start;
			                    prevEnd = f.get('end');
		                	}
		                }
	                }                
	            }, this );
            }
        }
    },
<commit_msg>Allow labels to be draw when transformed<commit_after>define( [
            'dojo/_base/declare',
            'dojo/_base/array',
            'dojo/_base/Color',
            'dojo/dom-construct',
            'dojo/on',
            'JBrowse/View/Track/WiggleBase',
            'JBrowse/View/Track/_YScaleMixin',
            'JBrowse/Util',
            './_Scale',
            'CoGe/View/ColorDialog'
        ],
        function( declare, array, Color, domConstruct, on, WiggleBase, YScaleMixin, Util, Scale, ColorDialog ) {

var XYPlot = declare( [WiggleBase, YScaleMixin], // mdb: this file is a copy of XYPlot, extend that class instead?

/**
 * Wiggle track that shows data with an X-Y plot along the reference.
 *
 * @lends JBrowse.View.Track.Wiggle.XYPlot
 * @extends JBrowse.View.Track.WiggleBase
 */
{
    // Load cookie params - mdb added 1/13/14, issue 279
    constructor: function() {
        this.inherited(arguments); // call superclass constructor

        if (!this.config.style.featureColor)
            this.config.style.featureColor = {};
        if (typeof(this.config.showHoverScores) == "undefined")
        	this.config.showHoverScores = 1;
        if (typeof(this.config.showLabels) == "undefined")
        	this.config.showLabels = 1;
    },

    _defaultConfig: function() {
        return Util.deepUpdate(
            dojo.clone( this.inherited(arguments) ),
            {
                style: {
                    //pos_color: 'blue',
                    //neg_color: 'red',
                    origin_color: '#888',
                    variance_band_color: 'rgba(0,0,0,0.3)',
                },
            }
        );
    },

    _getScaling: function( viewArgs, successCallback, errorCallback ) {

        this._getScalingStats( viewArgs, dojo.hitch(this, function( stats ) {

            //calculate the scaling if necessary
            if( ! this.lastScaling || ! this.lastScaling.sameStats( stats ) ) {

                var scaling = new Scale( this.config, stats );

                // bump minDisplayed to 0 if it is within 0.5% of it
                if( Math.abs( scaling.min / scaling.max ) < 0.005 )
                    scaling.min = 0;

                // update our track y-scale to reflect it
                this.makeYScale({
                    fixBounds: true,
                    min: scaling.min,
                    max: scaling.max
                });

                // and finally adjust the scaling to match the ruler's scale rounding
                scaling.min = this.ruler.scaler.bounds.lower;
                scaling.max = this.ruler.scaler.bounds.upper;
                scaling.range = scaling.max - scaling.min;

                this.lastScaling = scaling;
            }

            successCallback( this.lastScaling );
        }), errorCallback );
    },

    updateStaticElements: function( coords ) {
        //console.log('updateStaticElements');
        this.inherited( arguments );
        this.updateYScaleFromViewDimensions( coords );
    },

    fillTooManyFeaturesMessage: function( blockIndex, block, scale ) {
        this.fillMessage(
            blockIndex,
            block,
            'Too much data to show'
                + (scale >= this.browser.view.maxPxPerBp ? '': '; zoom in to see detail')
                + '.'
        );
    },

    fillMessage: function( blockIndex, block, message, class_ ) {
        domConstruct.empty( block.domNode );
        var msgDiv = dojo.create(
            'div', {
                className: class_ || 'message',
                innerHTML: message
            }, block.domNode );
        this.heightUpdate( dojo.position(msgDiv).h, blockIndex );
    },

    renderBlock: function( args ) {
        var featureScale = this.config.style.featureScale;
        var scale = args.block.scale;
        if (scale <= featureScale) { // don't draw, too zoomed-out, modeled after HTMLFeatures
            this.fillTooManyFeaturesMessage(args.blockIndex, args.block, scale);
        }
        else { // render features
            this.inherited( arguments );
        }
    },

    _draw: function(scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale, pixels, spans) {
        this._preDraw(      scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale );

        this._drawFeatures( scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale );

        if ( spans ) {
            this._maskBySpans( scale, leftBase, rightBase, block, canvas, pixels, dataScale, spans );
        }
        this._postDraw(     scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale );
    },
    /**
     * Draw a set of features on the canvas.
     * @private
     */
    _drawFeatures: function( scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale ) {
        //console.log('_drawFeatures');
        var config = this.config;
        var context = canvas.getContext('2d');
        var canvasHeight = canvas.height;
        var toY = dojo.hitch( this, function( val ) {
           return canvasHeight * ( 1-dataScale.normalize.call(this, val) );
        });
        var originY = toY( dataScale.origin );
        var disableClipMarkers = this.config.disable_clip_markers;

        // Note: transform cases below can be consolidated/optimized
        if (config.transformAverage) {
            var sum_f = [];
            var sum_r = [];
            var count_f = [];
            var count_r = [];
            var width = [];
            dojo.forEach( features, function(f,i) {
                var score = f.get('score');
                var l = featureRects[i].l;
                var w = featureRects[i].w;
                if (score >= 0) {
                    sum_f[l] = l in sum_f ? sum_f[l] + score : score;
                    count_f[l] = l in count_f ? count_f[l] + 1 : 1;
                }
                else {
                    sum_r[l] = l in sum_r ? sum_r[l] + score : score;
                    count_r[l] = l in count_r ? count_r[l] + 1 : 1;
                }
                width[l] = l in width ? Math.max(width[l], w) : w;
            });
            sum_f.forEach( function(x,l) { // bar goes upward
                var avg = sum_f[l]/count_f[l];
                var height = toY( avg );
                context.fillStyle = 'gray';
                if( height <= canvasHeight ) { // if the rectangle is visible at all
                    context.fillRect( l, height, width[l], originY-height+1);
                }
            });
            sum_r.forEach( function(x,l) { // bar goes downward
                var avg = sum_r[l]/count_r[l];
                var height = toY( avg );
                context.fillStyle = 'gray';
                if( height <= canvasHeight ) { // if the rectangle is visible at all
                    context.fillRect( l, originY, width[l], height-originY+1 );
                }
            });
        }
        else if (config.transformDifference) {
            var width   = [];
            var max_f   = []; // forward strand
            var max_r   = []; // reverse strand
            var min_f   = [];
            var min_r   = [];
            var color_f = [];
            var color_r = [];
            var count_f = [];
            var count_r = [];
            dojo.forEach( features, dojo.hitch(this, function(f,i) {
                var score = f.get('score');
                var l = featureRects[i].l;
                var w = featureRects[i].w;
                width[l] = l in width ? Math.max(width[l], w) : w;

                var color = this._getFeatureColor( f.get('id') );

                if (score >= 0) {
                    count_f[l] = l in count_f ? count_f[l] + 1 : 1;
                    max_f[l] = l in max_f ? Math.max(max_f[l], score) : score;
                    min_f[l] = l in min_f ? Math.min(min_f[l], score) : score;
                    if (score >= max_f[l])
                        color_f[l] = color;
                }
                else {
                    count_r[l] = l in count_r ? count_r[l] + 1 : 1;
                    score = Math.abs(score);
                    max_r[l] = l in max_r ? Math.max(max_r[l], score) : score;
                    min_r[l] = l in min_r ? Math.min(min_r[l], score) : score;
                    if (score >= max_r[l])
                        color_r[l] = color;
                }
            }));
            max_f.forEach( function(x,l) { // bar goes upward
                var diff = max_f[l] - min_f[l];
                if (count_f[l] == 1)
                    diff = max_f[l];
                var height = toY(diff);
                if( height <= canvasHeight ) { // if the rectangle is visible at all
                    context.fillStyle = color_f[l];
                    context.fillRect( l, height, width[l], originY-height+1);
                }
            });
            max_r.forEach( function(x,l) { // bar goes downward
                var diff = max_r[l] - min_r[l];
                if (count_r[l] == 1)
                    diff = max_r[l];
                var height = toY(-1*diff);
                if( height <= canvasHeight ) { // if the rectangle is visible at all
                    context.fillStyle = color_r[l];
                    context.fillRect( l, originY, width[l], height-originY+1 );
                }
            });
        }
        else if (config.transformInflate) {
            // sort features by score
            var sorted = [];
            dojo.forEach( features, function(f,i) {
                sorted.push({ feature: f, featureRect: featureRects[i] });
            });
            sorted.sort( sortByScore );

            dojo.forEach( sorted, function(pair,i) {
                var f = pair.feature;
                var fRect = pair.featureRect;
                var score = ( f.get('score') > 0 ? 1 : -1 );

                fRect.t = toY( score );
                if( fRect.t <= canvasHeight ) { // if the rectangle is visible at all
                    var id = f.get('id');
                    context.fillStyle = this._getFeatureColor(id);
                    if (fRect.t <= originY) // bar goes upward
                        context.fillRect( fRect.l, fRect.t, fRect.w, originY-fRect.t+1);
                    else // downward
                        context.fillRect( fRect.l, originY, fRect.w, fRect.t-originY+1 );
                }
            }, this );
        }
        else {
            // sort features by score
            var sorted = [];
            dojo.forEach( features, function(f,i) {
                sorted.push({ feature: f, featureRect: featureRects[i] });
            });
            sorted.sort( sortByScore );

            dojo.forEach( sorted, function(pair,i) {
                var f = pair.feature;
                var fRect = pair.featureRect;
                var score = f.get('score');

                if (config.transformLog10) {
                    if (score >= 0)
                        score = log10(Math.abs(score)+1);
                    else
                        score = -1*log10(Math.abs(score)+1);
                }
                else if (config.transformLog2) {
                    if (score >= 0)
                        score = log2(Math.abs(score)+1);
                    else
                        score = -1*log2(Math.abs(score)+1);
                }

                fRect.t = toY( score );
                if( fRect.t <= canvasHeight ) { // if the rectangle is visible at all
                    var id = f.get('id');
                    context.fillStyle = this._getFeatureColor(id);
                    if (fRect.t <= originY) // bar goes upward
                        context.fillRect( fRect.l, fRect.t, fRect.w, originY-fRect.t+1);
                    else // downward
                        context.fillRect( fRect.l, originY, fRect.w, fRect.t-originY+1 );
                }
            }, this );
        }
        
        // mdb added 4/2/14 - draw labels on top of bars, issue 346
        console.log('showLabels='+this.config.showLabels);
        var prevStart, prevEnd;
        if (this.config.showLabels && scale > this.config.style.labelScale) {
            dojo.forEach( sorted, function(pair,i) {
                var f = pair.feature;
                var fRect = pair.featureRect;
                var isUpward = (fRect.t <= originY); // bar goes upward
                var start = f.get('start'); 
                if (start >= block.startBase && start <= block.endBase) { // print label only for first spanning block
	                var label = f.get('label');
	                if (label && label != '.') {
	                	if (!(start >= prevStart && start <= prevEnd)) { // mdb added 4/15/14 - don't allow overlapping labels, only print the first one
		                	var topOffset = ( isUpward ? fRect.t-12 : fRect.t );
		                    var rulerdiv =
		                        dojo.create('div', 
		                    		{   style: {
		                                	width: '100px',
		                                    position: 'absolute',
		                                    left: fRect.l,
		                                    top: topOffset,
		                                    //zIndex: 10,
		                                },
		                                innerHTML: label
		                            }, canvas.parentNode );
		                    console.log('label='+label);
		                    prevStart = start;
		                    prevEnd = f.get('end');
	                	}
	                }
                }                
            }, this );
        }
    },

{'fraction_non_alphanumeric': 0.11543545307681011, 'fraction_numerical': 0.006462035541195477, 'mean_word_length': 1.2977054158933694, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1114987', 'n_tokens_mistral': 7945, 'n_tokens_neox': 7356, 'n_words': 1824}