/* Flot plugin for plotting images. 
 | 
  
 | 
Copyright (c) 2007-2014 IOLA and Ole Laursen. 
 | 
Licensed under the MIT license. 
 | 
  
 | 
The data syntax is [ [ image, x1, y1, x2, y2 ], ... ] where (x1, y1) and 
 | 
(x2, y2) are where you intend the two opposite corners of the image to end up 
 | 
in the plot. Image must be a fully loaded Javascript image (you can make one 
 | 
with new Image()). If the image is not complete, it's skipped when plotting. 
 | 
  
 | 
There are two helpers included for retrieving images. The easiest work the way 
 | 
that you put in URLs instead of images in the data, like this: 
 | 
  
 | 
    [ "myimage.png", 0, 0, 10, 10 ] 
 | 
  
 | 
Then call $.plot.image.loadData( data, options, callback ) where data and 
 | 
options are the same as you pass in to $.plot. This loads the images, replaces 
 | 
the URLs in the data with the corresponding images and calls "callback" when 
 | 
all images are loaded (or failed loading). In the callback, you can then call 
 | 
$.plot with the data set. See the included example. 
 | 
  
 | 
A more low-level helper, $.plot.image.load(urls, callback) is also included. 
 | 
Given a list of URLs, it calls callback with an object mapping from URL to 
 | 
Image object when all images are loaded or have failed loading. 
 | 
  
 | 
The plugin supports these options: 
 | 
  
 | 
    series: { 
 | 
        images: { 
 | 
            show: boolean 
 | 
            anchor: "corner" or "center" 
 | 
            alpha: [ 0, 1 ] 
 | 
        } 
 | 
    } 
 | 
  
 | 
They can be specified for a specific series: 
 | 
  
 | 
    $.plot( $("#placeholder"), [{ 
 | 
        data: [ ... ], 
 | 
        images: { ... } 
 | 
    ]) 
 | 
  
 | 
Note that because the data format is different from usual data points, you 
 | 
can't use images with anything else in a specific data series. 
 | 
  
 | 
Setting "anchor" to "center" causes the pixels in the image to be anchored at 
 | 
the corner pixel centers inside of at the pixel corners, effectively letting 
 | 
half a pixel stick out to each side in the plot. 
 | 
  
 | 
A possible future direction could be support for tiling for large images (like 
 | 
Google Maps). 
 | 
  
 | 
*/ 
 | 
  
 | 
(function ($) { 
 | 
    var options = { 
 | 
        series: { 
 | 
            images: { 
 | 
                show: false, 
 | 
                alpha: 1, 
 | 
                anchor: "corner" // or "center" 
 | 
            } 
 | 
        } 
 | 
    }; 
 | 
  
 | 
    $.plot.image = {}; 
 | 
  
 | 
    $.plot.image.loadDataImages = function (series, options, callback) { 
 | 
        var urls = [], points = []; 
 | 
  
 | 
        var defaultShow = options.series.images.show; 
 | 
         
 | 
        $.each(series, function (i, s) { 
 | 
            if (!(defaultShow || s.images.show)) 
 | 
                return; 
 | 
             
 | 
            if (s.data) 
 | 
                s = s.data; 
 | 
  
 | 
            $.each(s, function (i, p) { 
 | 
                if (typeof p[0] == "string") { 
 | 
                    urls.push(p[0]); 
 | 
                    points.push(p); 
 | 
                } 
 | 
            }); 
 | 
        }); 
 | 
  
 | 
        $.plot.image.load(urls, function (loadedImages) { 
 | 
            $.each(points, function (i, p) { 
 | 
                var url = p[0]; 
 | 
                if (loadedImages[url]) 
 | 
                    p[0] = loadedImages[url]; 
 | 
            }); 
 | 
  
 | 
            callback(); 
 | 
        }); 
 | 
    } 
 | 
     
 | 
    $.plot.image.load = function (urls, callback) { 
 | 
        var missing = urls.length, loaded = {}; 
 | 
        if (missing == 0) 
 | 
            callback({}); 
 | 
  
 | 
        $.each(urls, function (i, url) { 
 | 
            var handler = function () { 
 | 
                --missing; 
 | 
                 
 | 
                loaded[url] = this; 
 | 
                 
 | 
                if (missing == 0) 
 | 
                    callback(loaded); 
 | 
            }; 
 | 
  
 | 
            $('<img />').load(handler).error(handler).attr('src', url); 
 | 
        }); 
 | 
    }; 
 | 
     
 | 
    function drawSeries(plot, ctx, series) { 
 | 
        var plotOffset = plot.getPlotOffset(); 
 | 
         
 | 
        if (!series.images || !series.images.show) 
 | 
            return; 
 | 
         
 | 
        var points = series.datapoints.points, 
 | 
            ps = series.datapoints.pointsize; 
 | 
         
 | 
        for (var i = 0; i < points.length; i += ps) { 
 | 
            var img = points[i], 
 | 
                x1 = points[i + 1], y1 = points[i + 2], 
 | 
                x2 = points[i + 3], y2 = points[i + 4], 
 | 
                xaxis = series.xaxis, yaxis = series.yaxis, 
 | 
                tmp; 
 | 
  
 | 
            // actually we should check img.complete, but it 
 | 
            // appears to be a somewhat unreliable indicator in 
 | 
            // IE6 (false even after load event) 
 | 
            if (!img || img.width <= 0 || img.height <= 0) 
 | 
                continue; 
 | 
  
 | 
            if (x1 > x2) { 
 | 
                tmp = x2; 
 | 
                x2 = x1; 
 | 
                x1 = tmp; 
 | 
            } 
 | 
            if (y1 > y2) { 
 | 
                tmp = y2; 
 | 
                y2 = y1; 
 | 
                y1 = tmp; 
 | 
            } 
 | 
             
 | 
            // if the anchor is at the center of the pixel, expand the  
 | 
            // image by 1/2 pixel in each direction 
 | 
            if (series.images.anchor == "center") { 
 | 
                tmp = 0.5 * (x2-x1) / (img.width - 1); 
 | 
                x1 -= tmp; 
 | 
                x2 += tmp; 
 | 
                tmp = 0.5 * (y2-y1) / (img.height - 1); 
 | 
                y1 -= tmp; 
 | 
                y2 += tmp; 
 | 
            } 
 | 
             
 | 
            // clip 
 | 
            if (x1 == x2 || y1 == y2 || 
 | 
                x1 >= xaxis.max || x2 <= xaxis.min || 
 | 
                y1 >= yaxis.max || y2 <= yaxis.min) 
 | 
                continue; 
 | 
  
 | 
            var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; 
 | 
            if (x1 < xaxis.min) { 
 | 
                sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1); 
 | 
                x1 = xaxis.min; 
 | 
            } 
 | 
  
 | 
            if (x2 > xaxis.max) { 
 | 
                sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1); 
 | 
                x2 = xaxis.max; 
 | 
            } 
 | 
  
 | 
            if (y1 < yaxis.min) { 
 | 
                sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1); 
 | 
                y1 = yaxis.min; 
 | 
            } 
 | 
  
 | 
            if (y2 > yaxis.max) { 
 | 
                sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); 
 | 
                y2 = yaxis.max; 
 | 
            } 
 | 
             
 | 
            x1 = xaxis.p2c(x1); 
 | 
            x2 = xaxis.p2c(x2); 
 | 
            y1 = yaxis.p2c(y1); 
 | 
            y2 = yaxis.p2c(y2); 
 | 
             
 | 
            // the transformation may have swapped us 
 | 
            if (x1 > x2) { 
 | 
                tmp = x2; 
 | 
                x2 = x1; 
 | 
                x1 = tmp; 
 | 
            } 
 | 
            if (y1 > y2) { 
 | 
                tmp = y2; 
 | 
                y2 = y1; 
 | 
                y1 = tmp; 
 | 
            } 
 | 
  
 | 
            tmp = ctx.globalAlpha; 
 | 
            ctx.globalAlpha *= series.images.alpha; 
 | 
            ctx.drawImage(img, 
 | 
                          sx1, sy1, sx2 - sx1, sy2 - sy1, 
 | 
                          x1 + plotOffset.left, y1 + plotOffset.top, 
 | 
                          x2 - x1, y2 - y1); 
 | 
            ctx.globalAlpha = tmp; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    function processRawData(plot, series, data, datapoints) { 
 | 
        if (!series.images.show) 
 | 
            return; 
 | 
  
 | 
        // format is Image, x1, y1, x2, y2 (opposite corners) 
 | 
        datapoints.format = [ 
 | 
            { required: true }, 
 | 
            { x: true, number: true, required: true }, 
 | 
            { y: true, number: true, required: true }, 
 | 
            { x: true, number: true, required: true }, 
 | 
            { y: true, number: true, required: true } 
 | 
        ]; 
 | 
    } 
 | 
     
 | 
    function init(plot) { 
 | 
        plot.hooks.processRawData.push(processRawData); 
 | 
        plot.hooks.drawSeries.push(drawSeries); 
 | 
    } 
 | 
     
 | 
    $.plot.plugins.push({ 
 | 
        init: init, 
 | 
        options: options, 
 | 
        name: 'image', 
 | 
        version: '1.1' 
 | 
    }); 
 | 
})(jQuery); 
 |