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); |
---|