Project

General

Profile

1
/* Flot plugin for plotting textual data or categories.
2

    
3
Copyright (c) 2007-2013 IOLA and Ole Laursen.
4
Licensed under the MIT license.
5

    
6
Consider a dataset like [["February", 34], ["March", 20], ...]. This plugin
7
allows you to plot such a dataset directly.
8

    
9
To enable it, you must specify mode: "categories" on the axis with the textual
10
labels, e.g.
11

    
12
	$.plot("#placeholder", data, { xaxis: { mode: "categories" } });
13

    
14
By default, the labels are ordered as they are met in the data series. If you
15
need a different ordering, you can specify "categories" on the axis options
16
and list the categories there:
17

    
18
	xaxis: {
19
		mode: "categories",
20
		categories: ["February", "March", "April"]
21
	}
22

    
23
If you need to customize the distances between the categories, you can specify
24
"categories" as an object mapping labels to values
25

    
26
	xaxis: {
27
		mode: "categories",
28
		categories: { "February": 1, "March": 3, "April": 4 }
29
	}
30

    
31
If you don't specify all categories, the remaining categories will be numbered
32
from the max value plus 1 (with a spacing of 1 between each).
33

    
34
Internally, the plugin works by transforming the input data through an auto-
35
generated mapping where the first category becomes 0, the second 1, etc.
36
Hence, a point like ["February", 34] becomes [0, 34] internally in Flot (this
37
is visible in hover and click events that return numbers rather than the
38
category labels). The plugin also overrides the tick generator to spit out the
39
categories as ticks instead of the values.
40

    
41
If you need to map a value back to its label, the mapping is always accessible
42
as "categories" on the axis object, e.g. plot.getAxes().xaxis.categories.
43

    
44
*/
45

    
46
(function ($) {
47
    var options = {
48
        xaxis: {
49
            categories: null
50
        },
51
        yaxis: {
52
            categories: null
53
        }
54
    };
55
    
56
    function processRawData(plot, series, data, datapoints) {
57
        // if categories are enabled, we need to disable
58
        // auto-transformation to numbers so the strings are intact
59
        // for later processing
60

    
61
        var xCategories = series.xaxis.options.mode == "categories",
62
            yCategories = series.yaxis.options.mode == "categories";
63
        
64
        if (!(xCategories || yCategories))
65
            return;
66

    
67
        var format = datapoints.format;
68

    
69
        if (!format) {
70
            // FIXME: auto-detection should really not be defined here
71
            var s = series;
72
            format = [];
73
            format.push({ x: true, number: true, required: true });
74
            format.push({ y: true, number: true, required: true });
75

    
76
            if (s.bars.show || (s.lines.show && s.lines.fill)) {
77
                var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero));
78
                format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale });
79
                if (s.bars.horizontal) {
80
                    delete format[format.length - 1].y;
81
                    format[format.length - 1].x = true;
82
                }
83
            }
84
            
85
            datapoints.format = format;
86
        }
87

    
88
        for (var m = 0; m < format.length; ++m) {
89
            if (format[m].x && xCategories)
90
                format[m].number = false;
91
            
92
            if (format[m].y && yCategories)
93
                format[m].number = false;
94
        }
95
    }
96

    
97
    function getNextIndex(categories) {
98
        var index = -1;
99
        
100
        for (var v in categories)
101
            if (categories[v] > index)
102
                index = categories[v];
103

    
104
        return index + 1;
105
    }
106

    
107
    function categoriesTickGenerator(axis) {
108
        var res = [];
109
        for (var label in axis.categories) {
110
            var v = axis.categories[label];
111
            if (v >= axis.min && v <= axis.max)
112
                res.push([v, label]);
113
        }
114

    
115
        res.sort(function (a, b) { return a[0] - b[0]; });
116

    
117
        return res;
118
    }
119
    
120
    function setupCategoriesForAxis(series, axis, datapoints) {
121
        if (series[axis].options.mode != "categories")
122
            return;
123
        
124
        if (!series[axis].categories) {
125
            // parse options
126
            var c = {}, o = series[axis].options.categories || {};
127
            if ($.isArray(o)) {
128
                for (var i = 0; i < o.length; ++i)
129
                    c[o[i]] = i;
130
            }
131
            else {
132
                for (var v in o)
133
                    c[v] = o[v];
134
            }
135
            
136
            series[axis].categories = c;
137
        }
138

    
139
        // fix ticks
140
        if (!series[axis].options.ticks)
141
            series[axis].options.ticks = categoriesTickGenerator;
142

    
143
        transformPointsOnAxis(datapoints, axis, series[axis].categories);
144
    }
145
    
146
    function transformPointsOnAxis(datapoints, axis, categories) {
147
        // go through the points, transforming them
148
        var points = datapoints.points,
149
            ps = datapoints.pointsize,
150
            format = datapoints.format,
151
            formatColumn = axis.charAt(0),
152
            index = getNextIndex(categories);
153

    
154
        for (var i = 0; i < points.length; i += ps) {
155
            if (points[i] == null)
156
                continue;
157
            
158
            for (var m = 0; m < ps; ++m) {
159
                var val = points[i + m];
160

    
161
                if (val == null || !format[m][formatColumn])
162
                    continue;
163

    
164
                if (!(val in categories)) {
165
                    categories[val] = index;
166
                    ++index;
167
                }
168
                
169
                points[i + m] = categories[val];
170
            }
171
        }
172
    }
173

    
174
    function processDatapoints(plot, series, datapoints) {
175
        setupCategoriesForAxis(series, "xaxis", datapoints);
176
        setupCategoriesForAxis(series, "yaxis", datapoints);
177
    }
178

    
179
    function init(plot) {
180
        plot.hooks.processRawData.push(processRawData);
181
        plot.hooks.processDatapoints.push(processDatapoints);
182
    }
183
    
184
    $.plot.plugins.push({
185
        init: init,
186
        options: options,
187
        name: 'categories',
188
        version: '1.0'
189
    });
190
})(jQuery);
(6-6/34)