UNPKG

6.24 kBJavaScriptView Raw
1/* Flot plugin for plotting textual data or categories.
2
3Copyright (c) 2007-2014 IOLA and Ole Laursen.
4Licensed under the MIT license.
5
6Consider a dataset like [["February", 34], ["March", 20], ...]. This plugin
7allows you to plot such a dataset directly.
8
9To enable it, you must specify mode: "categories" on the axis with the textual
10labels, e.g.
11
12 $.plot("#placeholder", data, { xaxis: { mode: "categories" } });
13
14By default, the labels are ordered as they are met in the data series. If you
15need a different ordering, you can specify "categories" on the axis options
16and list the categories there:
17
18 xaxis: {
19 mode: "categories",
20 categories: ["February", "March", "April"]
21 }
22
23If 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
31If you don't specify all categories, the remaining categories will be numbered
32from the max value plus 1 (with a spacing of 1 between each).
33
34Internally, the plugin works by transforming the input data through an auto-
35generated mapping where the first category becomes 0, the second 1, etc.
36Hence, a point like ["February", 34] becomes [0, 34] internally in Flot (this
37is visible in hover and click events that return numbers rather than the
38category labels). The plugin also overrides the tick generator to spit out the
39categories as ticks instead of the values.
40
41If you need to map a value back to its label, the mapping is always accessible
42as "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
68 var format = datapoints.format;
69
70 if (!format) {
71 // FIXME: auto-detection should really not be defined here
72 var s = series;
73 format = [];
74 format.push({ x: true, number: true, required: true, computeRange: true});
75 format.push({ y: true, number: true, required: true, computeRange: true });
76
77 if (s.bars.show || (s.lines.show && s.lines.fill)) {
78 var autoScale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero));
79 format.push({ y: true, number: true, required: false, defaultValue: 0, computeRange: autoScale });
80 if (s.bars.horizontal) {
81 delete format[format.length - 1].y;
82 format[format.length - 1].x = true;
83 }
84 }
85
86 datapoints.format = format;
87 }
88
89 for (var m = 0; m < format.length; ++m) {
90 if (format[m].x && xCategories) {
91 format[m].number = false;
92 }
93
94 if (format[m].y && yCategories) {
95 format[m].number = false;
96 format[m].computeRange = false;
97 }
98 }
99 }
100
101 function getNextIndex(categories) {
102 var index = -1;
103
104 for (var v in categories) {
105 if (categories[v] > index) {
106 index = categories[v];
107 }
108 }
109
110 return index + 1;
111 }
112
113 function categoriesTickGenerator(axis) {
114 var res = [];
115 for (var label in axis.categories) {
116 var v = axis.categories[label];
117 if (v >= axis.min && v <= axis.max) {
118 res.push([v, label]);
119 }
120 }
121
122 res.sort(function (a, b) { return a[0] - b[0]; });
123
124 return res;
125 }
126
127 function setupCategoriesForAxis(series, axis, datapoints) {
128 if (series[axis].options.mode !== "categories") {
129 return;
130 }
131
132 if (!series[axis].categories) {
133 // parse options
134 var c = {}, o = series[axis].options.categories || {};
135 if ($.isArray(o)) {
136 for (var i = 0; i < o.length; ++i) {
137 c[o[i]] = i;
138 }
139 } else {
140 for (var v in o) {
141 c[v] = o[v];
142 }
143 }
144
145 series[axis].categories = c;
146 }
147
148 // fix ticks
149 if (!series[axis].options.ticks) {
150 series[axis].options.ticks = categoriesTickGenerator;
151 }
152
153 transformPointsOnAxis(datapoints, axis, series[axis].categories);
154 }
155
156 function transformPointsOnAxis(datapoints, axis, categories) {
157 // go through the points, transforming them
158 var points = datapoints.points,
159 ps = datapoints.pointsize,
160 format = datapoints.format,
161 formatColumn = axis.charAt(0),
162 index = getNextIndex(categories);
163
164 for (var i = 0; i < points.length; i += ps) {
165 if (points[i] == null) {
166 continue;
167 }
168
169 for (var m = 0; m < ps; ++m) {
170 var val = points[i + m];
171
172 if (val == null || !format[m][formatColumn]) {
173 continue;
174 }
175
176 if (!(val in categories)) {
177 categories[val] = index;
178 ++index;
179 }
180
181 points[i + m] = categories[val];
182 }
183 }
184 }
185
186 function processDatapoints(plot, series, datapoints) {
187 setupCategoriesForAxis(series, "xaxis", datapoints);
188 setupCategoriesForAxis(series, "yaxis", datapoints);
189 }
190
191 function init(plot) {
192 plot.hooks.processRawData.push(processRawData);
193 plot.hooks.processDatapoints.push(processDatapoints);
194 }
195
196 $.plot.plugins.push({
197 init: init,
198 options: options,
199 name: 'categories',
200 version: '1.0'
201 });
202})(jQuery);