/* Flot plugin for computing bottoms for filled line and bar charts. 
 | 
  
 | 
Copyright (c) 2007-2014 IOLA and Ole Laursen. 
 | 
Licensed under the MIT license. 
 | 
  
 | 
The case: you've got two series that you want to fill the area between. In Flot 
 | 
terms, you need to use one as the fill bottom of the other. You can specify the 
 | 
bottom of each data point as the third coordinate manually, or you can use this 
 | 
plugin to compute it for you. 
 | 
  
 | 
In order to name the other series, you need to give it an id, like this: 
 | 
  
 | 
    var dataset = [ 
 | 
        { data: [ ... ], id: "foo" } ,         // use default bottom 
 | 
        { data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom 
 | 
    ]; 
 | 
  
 | 
    $.plot($("#placeholder"), dataset, { lines: { show: true, fill: true }}); 
 | 
  
 | 
As a convenience, if the id given is a number that doesn't appear as an id in 
 | 
the series, it is interpreted as the index in the array instead (so fillBetween: 
 | 
0 can also mean the first series). 
 | 
  
 | 
Internally, the plugin modifies the datapoints in each series. For line series, 
 | 
extra data points might be inserted through interpolation. Note that at points 
 | 
where the bottom line is not defined (due to a null point or start/end of line), 
 | 
the current line will show a gap too. The algorithm comes from the 
 | 
jquery.flot.stack.js plugin, possibly some code could be shared. 
 | 
  
 | 
*/ 
 | 
  
 | 
(function ( $ ) { 
 | 
  
 | 
    var options = { 
 | 
        series: { 
 | 
            fillBetween: null    // or number 
 | 
        } 
 | 
    }; 
 | 
  
 | 
    function init( plot ) { 
 | 
  
 | 
        function findBottomSeries( s, allseries ) { 
 | 
  
 | 
            var i; 
 | 
  
 | 
            for ( i = 0; i < allseries.length; ++i ) { 
 | 
                if ( allseries[ i ].id === s.fillBetween ) { 
 | 
                    return allseries[ i ]; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            if ( typeof s.fillBetween === "number" ) { 
 | 
                if ( s.fillBetween < 0 || s.fillBetween >= allseries.length ) { 
 | 
                    return null; 
 | 
                } 
 | 
                return allseries[ s.fillBetween ]; 
 | 
            } 
 | 
  
 | 
            return null; 
 | 
        } 
 | 
  
 | 
        function computeFillBottoms( plot, s, datapoints ) { 
 | 
  
 | 
            if ( s.fillBetween == null ) { 
 | 
                return; 
 | 
            } 
 | 
  
 | 
            var other = findBottomSeries( s, plot.getData() ); 
 | 
  
 | 
            if ( !other ) { 
 | 
                return; 
 | 
            } 
 | 
  
 | 
            var ps = datapoints.pointsize, 
 | 
                points = datapoints.points, 
 | 
                otherps = other.datapoints.pointsize, 
 | 
                otherpoints = other.datapoints.points, 
 | 
                newpoints = [], 
 | 
                px, py, intery, qx, qy, bottom, 
 | 
                withlines = s.lines.show, 
 | 
                withbottom = ps > 2 && datapoints.format[2].y, 
 | 
                withsteps = withlines && s.lines.steps, 
 | 
                fromgap = true, 
 | 
                i = 0, 
 | 
                j = 0, 
 | 
                l, m; 
 | 
  
 | 
            while ( true ) { 
 | 
  
 | 
                if ( i >= points.length ) { 
 | 
                    break; 
 | 
                } 
 | 
  
 | 
                l = newpoints.length; 
 | 
  
 | 
                if ( points[ i ] == null ) { 
 | 
  
 | 
                    // copy gaps 
 | 
  
 | 
                    for ( m = 0; m < ps; ++m ) { 
 | 
                        newpoints.push( points[ i + m ] ); 
 | 
                    } 
 | 
  
 | 
                    i += ps; 
 | 
  
 | 
                } else if ( j >= otherpoints.length ) { 
 | 
  
 | 
                    // for lines, we can't use the rest of the points 
 | 
  
 | 
                    if ( !withlines ) { 
 | 
                        for ( m = 0; m < ps; ++m ) { 
 | 
                            newpoints.push( points[ i + m ] ); 
 | 
                        } 
 | 
                    } 
 | 
  
 | 
                    i += ps; 
 | 
  
 | 
                } else if ( otherpoints[ j ] == null ) { 
 | 
  
 | 
                    // oops, got a gap 
 | 
  
 | 
                    for ( m = 0; m < ps; ++m ) { 
 | 
                        newpoints.push( null ); 
 | 
                    } 
 | 
  
 | 
                    fromgap = true; 
 | 
                    j += otherps; 
 | 
  
 | 
                } else { 
 | 
  
 | 
                    // cases where we actually got two points 
 | 
  
 | 
                    px = points[ i ]; 
 | 
                    py = points[ i + 1 ]; 
 | 
                    qx = otherpoints[ j ]; 
 | 
                    qy = otherpoints[ j + 1 ]; 
 | 
                    bottom = 0; 
 | 
  
 | 
                    if ( px === qx ) { 
 | 
  
 | 
                        for ( m = 0; m < ps; ++m ) { 
 | 
                            newpoints.push( points[ i + m ] ); 
 | 
                        } 
 | 
  
 | 
                        //newpoints[ l + 1 ] += qy; 
 | 
                        bottom = qy; 
 | 
  
 | 
                        i += ps; 
 | 
                        j += otherps; 
 | 
  
 | 
                    } else if ( px > qx ) { 
 | 
  
 | 
                        // we got past point below, might need to 
 | 
                        // insert interpolated extra point 
 | 
  
 | 
                        if ( withlines && i > 0 && points[ i - ps ] != null ) { 
 | 
                            intery = py + ( points[ i - ps + 1 ] - py ) * ( qx - px ) / ( points[ i - ps ] - px ); 
 | 
                            newpoints.push( qx ); 
 | 
                            newpoints.push( intery ); 
 | 
                            for ( m = 2; m < ps; ++m ) { 
 | 
                                newpoints.push( points[ i + m ] ); 
 | 
                            } 
 | 
                            bottom = qy; 
 | 
                        } 
 | 
  
 | 
                        j += otherps; 
 | 
  
 | 
                    } else { // px < qx 
 | 
  
 | 
                        // if we come from a gap, we just skip this point 
 | 
  
 | 
                        if ( fromgap && withlines ) { 
 | 
                            i += ps; 
 | 
                            continue; 
 | 
                        } 
 | 
  
 | 
                        for ( m = 0; m < ps; ++m ) { 
 | 
                            newpoints.push( points[ i + m ] ); 
 | 
                        } 
 | 
  
 | 
                        // we might be able to interpolate a point below, 
 | 
                        // this can give us a better y 
 | 
  
 | 
                        if ( withlines && j > 0 && otherpoints[ j - otherps ] != null ) { 
 | 
                            bottom = qy + ( otherpoints[ j - otherps + 1 ] - qy ) * ( px - qx ) / ( otherpoints[ j - otherps ] - qx ); 
 | 
                        } 
 | 
  
 | 
                        //newpoints[l + 1] += bottom; 
 | 
  
 | 
                        i += ps; 
 | 
                    } 
 | 
  
 | 
                    fromgap = false; 
 | 
  
 | 
                    if ( l !== newpoints.length && withbottom ) { 
 | 
                        newpoints[ l + 2 ] = bottom; 
 | 
                    } 
 | 
                } 
 | 
  
 | 
                // maintain the line steps invariant 
 | 
  
 | 
                if ( withsteps && l !== newpoints.length && l > 0 && 
 | 
                    newpoints[ l ] !== null && 
 | 
                    newpoints[ l ] !== newpoints[ l - ps ] && 
 | 
                    newpoints[ l + 1 ] !== newpoints[ l - ps + 1 ] ) { 
 | 
                    for (m = 0; m < ps; ++m) { 
 | 
                        newpoints[ l + ps + m ] = newpoints[ l + m ]; 
 | 
                    } 
 | 
                    newpoints[ l + 1 ] = newpoints[ l - ps + 1 ]; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            datapoints.points = newpoints; 
 | 
        } 
 | 
  
 | 
        plot.hooks.processDatapoints.push( computeFillBottoms ); 
 | 
    } 
 | 
  
 | 
    $.plot.plugins.push({ 
 | 
        init: init, 
 | 
        options: options, 
 | 
        name: "fillbetween", 
 | 
        version: "1.0" 
 | 
    }); 
 | 
  
 | 
})(jQuery); 
 |