/* Flot plugin for plotting textual data or categories. 
 | 
  
 | 
Copyright (c) 2007-2014 IOLA and Ole Laursen. 
 | 
Licensed under the MIT license. 
 | 
  
 | 
Consider a dataset like [["February", 34], ["March", 20], ...]. This plugin 
 | 
allows you to plot such a dataset directly. 
 | 
  
 | 
To enable it, you must specify mode: "categories" on the axis with the textual 
 | 
labels, e.g. 
 | 
  
 | 
    $.plot("#placeholder", data, { xaxis: { mode: "categories" } }); 
 | 
  
 | 
By default, the labels are ordered as they are met in the data series. If you 
 | 
need a different ordering, you can specify "categories" on the axis options 
 | 
and list the categories there: 
 | 
  
 | 
    xaxis: { 
 | 
        mode: "categories", 
 | 
        categories: ["February", "March", "April"] 
 | 
    } 
 | 
  
 | 
If you need to customize the distances between the categories, you can specify 
 | 
"categories" as an object mapping labels to values 
 | 
  
 | 
    xaxis: { 
 | 
        mode: "categories", 
 | 
        categories: { "February": 1, "March": 3, "April": 4 } 
 | 
    } 
 | 
  
 | 
If you don't specify all categories, the remaining categories will be numbered 
 | 
from the max value plus 1 (with a spacing of 1 between each). 
 | 
  
 | 
Internally, the plugin works by transforming the input data through an auto- 
 | 
generated mapping where the first category becomes 0, the second 1, etc. 
 | 
Hence, a point like ["February", 34] becomes [0, 34] internally in Flot (this 
 | 
is visible in hover and click events that return numbers rather than the 
 | 
category labels). The plugin also overrides the tick generator to spit out the 
 | 
categories as ticks instead of the values. 
 | 
  
 | 
If you need to map a value back to its label, the mapping is always accessible 
 | 
as "categories" on the axis object, e.g. plot.getAxes().xaxis.categories. 
 | 
  
 | 
*/ 
 | 
  
 | 
(function ($) { 
 | 
    var options = { 
 | 
        xaxis: { 
 | 
            categories: null 
 | 
        }, 
 | 
        yaxis: { 
 | 
            categories: null 
 | 
        } 
 | 
    }; 
 | 
     
 | 
    function processRawData(plot, series, data, datapoints) { 
 | 
        // if categories are enabled, we need to disable 
 | 
        // auto-transformation to numbers so the strings are intact 
 | 
        // for later processing 
 | 
  
 | 
        var xCategories = series.xaxis.options.mode == "categories", 
 | 
            yCategories = series.yaxis.options.mode == "categories"; 
 | 
         
 | 
        if (!(xCategories || yCategories)) 
 | 
            return; 
 | 
  
 | 
        var format = datapoints.format; 
 | 
  
 | 
        if (!format) { 
 | 
            // FIXME: auto-detection should really not be defined here 
 | 
            var s = series; 
 | 
            format = []; 
 | 
            format.push({ x: true, number: true, required: true }); 
 | 
            format.push({ y: true, number: true, required: true }); 
 | 
  
 | 
            if (s.bars.show || (s.lines.show && s.lines.fill)) { 
 | 
                var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); 
 | 
                format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); 
 | 
                if (s.bars.horizontal) { 
 | 
                    delete format[format.length - 1].y; 
 | 
                    format[format.length - 1].x = true; 
 | 
                } 
 | 
            } 
 | 
             
 | 
            datapoints.format = format; 
 | 
        } 
 | 
  
 | 
        for (var m = 0; m < format.length; ++m) { 
 | 
            if (format[m].x && xCategories) 
 | 
                format[m].number = false; 
 | 
             
 | 
            if (format[m].y && yCategories) 
 | 
                format[m].number = false; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    function getNextIndex(categories) { 
 | 
        var index = -1; 
 | 
         
 | 
        for (var v in categories) 
 | 
            if (categories[v] > index) 
 | 
                index = categories[v]; 
 | 
  
 | 
        return index + 1; 
 | 
    } 
 | 
  
 | 
    function categoriesTickGenerator(axis) { 
 | 
        var res = []; 
 | 
        for (var label in axis.categories) { 
 | 
            var v = axis.categories[label]; 
 | 
            if (v >= axis.min && v <= axis.max) 
 | 
                res.push([v, label]); 
 | 
        } 
 | 
  
 | 
        res.sort(function (a, b) { return a[0] - b[0]; }); 
 | 
  
 | 
        return res; 
 | 
    } 
 | 
     
 | 
    function setupCategoriesForAxis(series, axis, datapoints) { 
 | 
        if (series[axis].options.mode != "categories") 
 | 
            return; 
 | 
         
 | 
        if (!series[axis].categories) { 
 | 
            // parse options 
 | 
            var c = {}, o = series[axis].options.categories || {}; 
 | 
            if ($.isArray(o)) { 
 | 
                for (var i = 0; i < o.length; ++i) 
 | 
                    c[o[i]] = i; 
 | 
            } 
 | 
            else { 
 | 
                for (var v in o) 
 | 
                    c[v] = o[v]; 
 | 
            } 
 | 
             
 | 
            series[axis].categories = c; 
 | 
        } 
 | 
  
 | 
        // fix ticks 
 | 
        if (!series[axis].options.ticks) 
 | 
            series[axis].options.ticks = categoriesTickGenerator; 
 | 
  
 | 
        transformPointsOnAxis(datapoints, axis, series[axis].categories); 
 | 
    } 
 | 
     
 | 
    function transformPointsOnAxis(datapoints, axis, categories) { 
 | 
        // go through the points, transforming them 
 | 
        var points = datapoints.points, 
 | 
            ps = datapoints.pointsize, 
 | 
            format = datapoints.format, 
 | 
            formatColumn = axis.charAt(0), 
 | 
            index = getNextIndex(categories); 
 | 
  
 | 
        for (var i = 0; i < points.length; i += ps) { 
 | 
            if (points[i] == null) 
 | 
                continue; 
 | 
             
 | 
            for (var m = 0; m < ps; ++m) { 
 | 
                var val = points[i + m]; 
 | 
  
 | 
                if (val == null || !format[m][formatColumn]) 
 | 
                    continue; 
 | 
  
 | 
                if (!(val in categories)) { 
 | 
                    categories[val] = index; 
 | 
                    ++index; 
 | 
                } 
 | 
                 
 | 
                points[i + m] = categories[val]; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    function processDatapoints(plot, series, datapoints) { 
 | 
        setupCategoriesForAxis(series, "xaxis", datapoints); 
 | 
        setupCategoriesForAxis(series, "yaxis", datapoints); 
 | 
    } 
 | 
  
 | 
    function init(plot) { 
 | 
        plot.hooks.processRawData.push(processRawData); 
 | 
        plot.hooks.processDatapoints.push(processDatapoints); 
 | 
    } 
 | 
     
 | 
    $.plot.plugins.push({ 
 | 
        init: init, 
 | 
        options: options, 
 | 
        name: 'categories', 
 | 
        version: '1.0' 
 | 
    }); 
 | 
})(jQuery); 
 |