blacklight_range_limit 8.5.0 → 9.0.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +117 -26
- data/.gitignore +2 -1
- data/.solr_wrapper.yml +8 -0
- data/Gemfile +7 -1
- data/README.md +84 -87
- data/Rakefile +69 -0
- data/VERSION +1 -1
- data/app/assets/javascripts/blacklight-range-limit/index.js +345 -0
- data/app/components/blacklight_range_limit/range_facet_component.html.erb +29 -27
- data/app/components/blacklight_range_limit/range_facet_component.rb +27 -9
- data/app/components/blacklight_range_limit/range_form_component.html.erb +23 -5
- data/app/components/blacklight_range_limit/range_form_component.rb +8 -20
- data/app/presenters/blacklight_range_limit/facet_field_presenter.rb +13 -2
- data/app/presenters/blacklight_range_limit/facet_item_presenter.rb +15 -11
- data/app/presenters/blacklight_range_limit/filter_field.rb +9 -3
- data/blacklight_range_limit.gemspec +1 -1
- data/config/importmap.rb +9 -0
- data/config/locales/blacklight_range_limit.ar.yml +13 -8
- data/config/locales/blacklight_range_limit.de.yml +13 -8
- data/config/locales/blacklight_range_limit.en.yml +5 -1
- data/config/locales/blacklight_range_limit.es.yml +18 -0
- data/config/locales/blacklight_range_limit.it.yml +13 -8
- data/doc/example-screenshot.png +0 -0
- data/lib/blacklight_range_limit/controller_override.rb +8 -35
- data/lib/blacklight_range_limit/engine.rb +48 -0
- data/lib/blacklight_range_limit/range_limit_builder.rb +61 -3
- data/lib/blacklight_range_limit.rb +10 -30
- data/lib/generators/blacklight_range_limit/assets_generator.rb +82 -18
- data/lib/generators/blacklight_range_limit/install_generator.rb +5 -2
- data/lib/generators/blacklight_range_limit/jsbundling_bl7_fixup_generator.rb +98 -0
- data/package-lock.json +8 -7
- data/package.json +10 -14
- data/spec/components/range_facet_component_spec.rb +101 -32
- data/spec/components/range_form_component_spec.rb +2 -2
- data/spec/components/range_segments_component_spec.rb +64 -0
- data/spec/features/blacklight_range_limit_spec.rb +21 -13
- data/spec/features/run_through_spec.rb +210 -0
- data/spec/presenters/facet_field_presenter_spec.rb +72 -0
- data/spec/presenters/filter_field_spec.rb +36 -1
- data/spec/requests/bad_param_requests_spec.rb +61 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/test_app_templates/Gemfile.extra +11 -1
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +37 -6
- metadata +27 -51
- data/app/assets/javascripts/blacklight_range_limit/blacklight_range_limit.esm.js +0 -534
- data/app/assets/javascripts/blacklight_range_limit/blacklight_range_limit.esm.js.map +0 -1
- data/app/assets/javascripts/blacklight_range_limit/blacklight_range_limit.umd.js +0 -542
- data/app/assets/javascripts/blacklight_range_limit/blacklight_range_limit.umd.js.map +0 -1
- data/app/assets/javascripts/blacklight_range_limit.js +0 -27
- data/app/assets/stylesheets/blacklight_range_limit/blacklight_range_limit.css +0 -60
- data/app/assets/stylesheets/blacklight_range_limit.css +0 -7
- data/app/helpers/range_limit_helper.rb +0 -130
- data/app/javascript/blacklight_range_limit/index.js +0 -11
- data/app/javascript/blacklight_range_limit/range_limit_distro_facets.js +0 -76
- data/app/javascript/blacklight_range_limit/range_limit_plotting.js +0 -186
- data/app/javascript/blacklight_range_limit/range_limit_shared.js +0 -112
- data/app/javascript/blacklight_range_limit/range_limit_slider.js +0 -163
- data/app/javascripts/blacklight_range_limit/index.js +0 -13
- data/app/views/blacklight_range_limit/_range_segments.html.erb +0 -8
- data/app/views/blacklight_range_limit/range_segments.html.erb +0 -1
- data/app/views/catalog/range_limit_panel.html.erb +0 -1
- data/rollup.config.js +0 -37
- data/spec/features/a_javascript_spec.rb +0 -70
- data/spec/helpers/blacklight_range_limit_helper_spec.rb +0 -37
- data/vendor/assets/javascripts/bootstrap-slider.js +0 -388
- data/vendor/assets/javascripts/flot/jquery.canvaswrapper.js +0 -549
- data/vendor/assets/javascripts/flot/jquery.colorhelpers.js +0 -199
- data/vendor/assets/javascripts/flot/jquery.event.drag.js +0 -145
- data/vendor/assets/javascripts/flot/jquery.flot.browser.js +0 -98
- data/vendor/assets/javascripts/flot/jquery.flot.drawSeries.js +0 -662
- data/vendor/assets/javascripts/flot/jquery.flot.hover.js +0 -359
- data/vendor/assets/javascripts/flot/jquery.flot.js +0 -2818
- data/vendor/assets/javascripts/flot/jquery.flot.saturated.js +0 -43
- data/vendor/assets/javascripts/flot/jquery.flot.selection.js +0 -527
- data/vendor/assets/javascripts/flot/jquery.flot.uiConstants.js +0 -10
- data/vendor/assets/stylesheets/slider.css +0 -138
@@ -1,2818 +0,0 @@
|
|
1
|
-
/* Javascript plotting library for jQuery, version 3.0.0.
|
2
|
-
|
3
|
-
Copyright (c) 2007-2014 IOLA and Ole Laursen.
|
4
|
-
Licensed under the MIT license.
|
5
|
-
|
6
|
-
*/
|
7
|
-
|
8
|
-
// the actual Flot code
|
9
|
-
(function($) {
|
10
|
-
"use strict";
|
11
|
-
|
12
|
-
var Canvas = window.Flot.Canvas;
|
13
|
-
|
14
|
-
function defaultTickGenerator(axis) {
|
15
|
-
var ticks = [],
|
16
|
-
start = $.plot.saturated.saturate($.plot.saturated.floorInBase(axis.min, axis.tickSize)),
|
17
|
-
i = 0,
|
18
|
-
v = Number.NaN,
|
19
|
-
prev;
|
20
|
-
|
21
|
-
if (start === -Number.MAX_VALUE) {
|
22
|
-
ticks.push(start);
|
23
|
-
start = $.plot.saturated.floorInBase(axis.min + axis.tickSize, axis.tickSize);
|
24
|
-
}
|
25
|
-
|
26
|
-
do {
|
27
|
-
prev = v;
|
28
|
-
//v = start + i * axis.tickSize;
|
29
|
-
v = $.plot.saturated.multiplyAdd(axis.tickSize, i, start);
|
30
|
-
ticks.push(v);
|
31
|
-
++i;
|
32
|
-
} while (v < axis.max && v !== prev);
|
33
|
-
|
34
|
-
return ticks;
|
35
|
-
}
|
36
|
-
|
37
|
-
function defaultTickFormatter(value, axis, precision) {
|
38
|
-
var oldTickDecimals = axis.tickDecimals,
|
39
|
-
expPosition = ("" + value).indexOf("e");
|
40
|
-
|
41
|
-
if (expPosition !== -1) {
|
42
|
-
return expRepTickFormatter(value, axis, precision);
|
43
|
-
}
|
44
|
-
|
45
|
-
if (precision > 0) {
|
46
|
-
axis.tickDecimals = precision;
|
47
|
-
}
|
48
|
-
|
49
|
-
var factor = axis.tickDecimals ? parseFloat('1e' + axis.tickDecimals) : 1,
|
50
|
-
formatted = "" + Math.round(value * factor) / factor;
|
51
|
-
|
52
|
-
// If tickDecimals was specified, ensure that we have exactly that
|
53
|
-
// much precision; otherwise default to the value's own precision.
|
54
|
-
if (axis.tickDecimals != null) {
|
55
|
-
var decimal = formatted.indexOf("."),
|
56
|
-
decimalPrecision = decimal === -1 ? 0 : formatted.length - decimal - 1;
|
57
|
-
if (decimalPrecision < axis.tickDecimals) {
|
58
|
-
var decimals = ("" + factor).substr(1, axis.tickDecimals - decimalPrecision);
|
59
|
-
formatted = (decimalPrecision ? formatted : formatted + ".") + decimals;
|
60
|
-
}
|
61
|
-
}
|
62
|
-
|
63
|
-
axis.tickDecimals = oldTickDecimals;
|
64
|
-
return formatted;
|
65
|
-
};
|
66
|
-
|
67
|
-
function expRepTickFormatter(value, axis, precision) {
|
68
|
-
var expPosition = ("" + value).indexOf("e"),
|
69
|
-
exponentValue = parseInt(("" + value).substr(expPosition + 1)),
|
70
|
-
tenExponent = expPosition !== -1 ? exponentValue : (value > 0 ? Math.floor(Math.log(value) / Math.LN10) : 0),
|
71
|
-
roundWith = parseFloat('1e' + tenExponent),
|
72
|
-
x = value / roundWith;
|
73
|
-
|
74
|
-
if (precision) {
|
75
|
-
var updatedPrecision = recomputePrecision(value, precision);
|
76
|
-
return (value / roundWith).toFixed(updatedPrecision) + 'e' + tenExponent;
|
77
|
-
}
|
78
|
-
|
79
|
-
if (axis.tickDecimals > 0) {
|
80
|
-
return x.toFixed(recomputePrecision(value, axis.tickDecimals)) + 'e' + tenExponent;
|
81
|
-
}
|
82
|
-
return x.toFixed() + 'e' + tenExponent;
|
83
|
-
}
|
84
|
-
|
85
|
-
function recomputePrecision(num, precision) {
|
86
|
-
//for numbers close to zero, the precision from flot will be a big number
|
87
|
-
//while for big numbers, the precision will be negative
|
88
|
-
var log10Value = Math.log(Math.abs(num)) * Math.LOG10E,
|
89
|
-
newPrecision = Math.abs(log10Value + precision);
|
90
|
-
|
91
|
-
return newPrecision <= 20 ? Math.floor(newPrecision) : 20;
|
92
|
-
}
|
93
|
-
|
94
|
-
///////////////////////////////////////////////////////////////////////////
|
95
|
-
// The top-level container for the entire plot.
|
96
|
-
function Plot(placeholder, data_, options_, plugins) {
|
97
|
-
// data is on the form:
|
98
|
-
// [ series1, series2 ... ]
|
99
|
-
// where series is either just the data as [ [x1, y1], [x2, y2], ... ]
|
100
|
-
// or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... }
|
101
|
-
|
102
|
-
var series = [],
|
103
|
-
options = {
|
104
|
-
// the color theme used for graphs
|
105
|
-
colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
|
106
|
-
xaxis: {
|
107
|
-
show: null, // null = auto-detect, true = always, false = never
|
108
|
-
position: "bottom", // or "top"
|
109
|
-
mode: null, // null or "time"
|
110
|
-
font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" }
|
111
|
-
color: null, // base color, labels, ticks
|
112
|
-
tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)"
|
113
|
-
transform: null, // null or f: number -> number to transform axis
|
114
|
-
inverseTransform: null, // if transform is set, this should be the inverse function
|
115
|
-
min: null, // min. value to show, null means set automatically
|
116
|
-
max: null, // max. value to show, null means set automatically
|
117
|
-
autoScaleMargin: null, // margin in % to add if autoScale option is on "loose" mode,
|
118
|
-
autoScale: "exact", // Available modes: "none", "loose", "exact", "sliding-window"
|
119
|
-
windowSize: null, // null or number. This is the size of sliding-window.
|
120
|
-
growOnly: null, // grow only, useful for smoother auto-scale, the scales will grow to accomodate data but won't shrink back.
|
121
|
-
ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
|
122
|
-
tickFormatter: null, // fn: number -> string
|
123
|
-
showTickLabels: "major", // "none", "endpoints", "major", "all"
|
124
|
-
labelWidth: null, // size of tick labels in pixels
|
125
|
-
labelHeight: null,
|
126
|
-
reserveSpace: null, // whether to reserve space even if axis isn't shown
|
127
|
-
tickLength: null, // size in pixels of major tick marks
|
128
|
-
showMinorTicks: null, // true = show minor tick marks, false = hide minor tick marks
|
129
|
-
showTicks: null, // true = show tick marks, false = hide all tick marks
|
130
|
-
gridLines: null, // true = show grid lines, false = hide grid lines
|
131
|
-
alignTicksWithAxis: null, // axis number or null for no sync
|
132
|
-
tickDecimals: null, // no. of decimals, null means auto
|
133
|
-
tickSize: null, // number or [number, "unit"]
|
134
|
-
minTickSize: null, // number or [number, "unit"]
|
135
|
-
offset: { below: 0, above: 0 }, // the plot drawing offset. this is calculated by the flot.navigate for each axis
|
136
|
-
boxPosition: { centerX: 0, centerY: 0 } //position of the axis on the corresponding axis box
|
137
|
-
},
|
138
|
-
yaxis: {
|
139
|
-
autoScaleMargin: 0.02, // margin in % to add if autoScale option is on "loose" mode
|
140
|
-
autoScale: "loose", // Available modes: "none", "loose", "exact"
|
141
|
-
growOnly: null, // grow only, useful for smoother auto-scale, the scales will grow to accomodate data but won't shrink back.
|
142
|
-
position: "left", // or "right"
|
143
|
-
showTickLabels: "major", // "none", "endpoints", "major", "all"
|
144
|
-
offset: { below: 0, above: 0 }, // the plot drawing offset. this is calculated by the flot.navigate for each axis
|
145
|
-
boxPosition: { centerX: 0, centerY: 0 } //position of the axis on the corresponding axis box
|
146
|
-
},
|
147
|
-
xaxes: [],
|
148
|
-
yaxes: [],
|
149
|
-
series: {
|
150
|
-
points: {
|
151
|
-
show: false,
|
152
|
-
radius: 3,
|
153
|
-
lineWidth: 2, // in pixels
|
154
|
-
fill: true,
|
155
|
-
fillColor: "#ffffff",
|
156
|
-
symbol: 'circle' // or callback
|
157
|
-
},
|
158
|
-
lines: {
|
159
|
-
// we don't put in show: false so we can see
|
160
|
-
// whether lines were actively disabled
|
161
|
-
lineWidth: 1, // in pixels
|
162
|
-
fill: false,
|
163
|
-
fillColor: null,
|
164
|
-
steps: false
|
165
|
-
// Omit 'zero', so we can later default its value to
|
166
|
-
// match that of the 'fill' option.
|
167
|
-
},
|
168
|
-
bars: {
|
169
|
-
show: false,
|
170
|
-
lineWidth: 2, // in pixels
|
171
|
-
// barWidth: number or [number, absolute]
|
172
|
-
// when 'absolute' is false, 'number' is relative to the minimum distance between points for the series
|
173
|
-
// when 'absolute' is true, 'number' is considered to be in units of the x-axis
|
174
|
-
horizontal: false,
|
175
|
-
barWidth: 0.8,
|
176
|
-
fill: true,
|
177
|
-
fillColor: null,
|
178
|
-
align: "left", // "left", "right", or "center"
|
179
|
-
zero: true
|
180
|
-
},
|
181
|
-
shadowSize: 3,
|
182
|
-
highlightColor: null
|
183
|
-
},
|
184
|
-
grid: {
|
185
|
-
show: true,
|
186
|
-
aboveData: false,
|
187
|
-
color: "#545454", // primary color used for outline and labels
|
188
|
-
backgroundColor: null, // null for transparent, else color
|
189
|
-
borderColor: null, // set if different from the grid color
|
190
|
-
tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)"
|
191
|
-
margin: 0, // distance from the canvas edge to the grid
|
192
|
-
labelMargin: 5, // in pixels
|
193
|
-
axisMargin: 8, // in pixels
|
194
|
-
borderWidth: 1, // in pixels
|
195
|
-
minBorderMargin: null, // in pixels, null means taken from points radius
|
196
|
-
markings: null, // array of ranges or fn: axes -> array of ranges
|
197
|
-
markingsColor: "#f4f4f4",
|
198
|
-
markingsLineWidth: 2,
|
199
|
-
// interactive stuff
|
200
|
-
clickable: false,
|
201
|
-
hoverable: false,
|
202
|
-
autoHighlight: true, // highlight in case mouse is near
|
203
|
-
mouseActiveRadius: 15 // how far the mouse can be away to activate an item
|
204
|
-
},
|
205
|
-
interaction: {
|
206
|
-
redrawOverlayInterval: 1000 / 60 // time between updates, -1 means in same flow
|
207
|
-
},
|
208
|
-
hooks: {}
|
209
|
-
},
|
210
|
-
surface = null, // the canvas for the plot itself
|
211
|
-
overlay = null, // canvas for interactive stuff on top of plot
|
212
|
-
eventHolder = null, // jQuery object that events should be bound to
|
213
|
-
ctx = null,
|
214
|
-
octx = null,
|
215
|
-
xaxes = [],
|
216
|
-
yaxes = [],
|
217
|
-
plotOffset = {
|
218
|
-
left: 0,
|
219
|
-
right: 0,
|
220
|
-
top: 0,
|
221
|
-
bottom: 0
|
222
|
-
},
|
223
|
-
plotWidth = 0,
|
224
|
-
plotHeight = 0,
|
225
|
-
hooks = {
|
226
|
-
processOptions: [],
|
227
|
-
processRawData: [],
|
228
|
-
processDatapoints: [],
|
229
|
-
processOffset: [],
|
230
|
-
setupGrid: [],
|
231
|
-
adjustSeriesDataRange: [],
|
232
|
-
setRange: [],
|
233
|
-
drawBackground: [],
|
234
|
-
drawSeries: [],
|
235
|
-
drawAxis: [],
|
236
|
-
draw: [],
|
237
|
-
findNearbyItems: [],
|
238
|
-
axisReserveSpace: [],
|
239
|
-
bindEvents: [],
|
240
|
-
drawOverlay: [],
|
241
|
-
resize: [],
|
242
|
-
shutdown: []
|
243
|
-
},
|
244
|
-
plot = this;
|
245
|
-
|
246
|
-
var eventManager = {};
|
247
|
-
|
248
|
-
// interactive features
|
249
|
-
|
250
|
-
var redrawTimeout = null;
|
251
|
-
|
252
|
-
// public functions
|
253
|
-
plot.setData = setData;
|
254
|
-
plot.setupGrid = setupGrid;
|
255
|
-
plot.draw = draw;
|
256
|
-
plot.getPlaceholder = function() {
|
257
|
-
return placeholder;
|
258
|
-
};
|
259
|
-
plot.getCanvas = function() {
|
260
|
-
return surface.element;
|
261
|
-
};
|
262
|
-
plot.getSurface = function() {
|
263
|
-
return surface;
|
264
|
-
};
|
265
|
-
plot.getEventHolder = function() {
|
266
|
-
return eventHolder[0];
|
267
|
-
};
|
268
|
-
plot.getPlotOffset = function() {
|
269
|
-
return plotOffset;
|
270
|
-
};
|
271
|
-
plot.width = function() {
|
272
|
-
return plotWidth;
|
273
|
-
};
|
274
|
-
plot.height = function() {
|
275
|
-
return plotHeight;
|
276
|
-
};
|
277
|
-
plot.offset = function() {
|
278
|
-
var o = eventHolder.offset();
|
279
|
-
o.left += plotOffset.left;
|
280
|
-
o.top += plotOffset.top;
|
281
|
-
return o;
|
282
|
-
};
|
283
|
-
plot.getData = function() {
|
284
|
-
return series;
|
285
|
-
};
|
286
|
-
plot.getAxes = function() {
|
287
|
-
var res = {};
|
288
|
-
$.each(xaxes.concat(yaxes), function(_, axis) {
|
289
|
-
if (axis) {
|
290
|
-
res[axis.direction + (axis.n !== 1 ? axis.n : "") + "axis"] = axis;
|
291
|
-
}
|
292
|
-
});
|
293
|
-
return res;
|
294
|
-
};
|
295
|
-
plot.getXAxes = function() {
|
296
|
-
return xaxes;
|
297
|
-
};
|
298
|
-
plot.getYAxes = function() {
|
299
|
-
return yaxes;
|
300
|
-
};
|
301
|
-
plot.c2p = canvasToCartesianAxisCoords;
|
302
|
-
plot.p2c = cartesianAxisToCanvasCoords;
|
303
|
-
plot.getOptions = function() {
|
304
|
-
return options;
|
305
|
-
};
|
306
|
-
plot.triggerRedrawOverlay = triggerRedrawOverlay;
|
307
|
-
plot.pointOffset = function(point) {
|
308
|
-
return {
|
309
|
-
left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10),
|
310
|
-
top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10)
|
311
|
-
};
|
312
|
-
};
|
313
|
-
plot.shutdown = shutdown;
|
314
|
-
plot.destroy = function() {
|
315
|
-
shutdown();
|
316
|
-
placeholder.removeData("plot").empty();
|
317
|
-
|
318
|
-
series = [];
|
319
|
-
options = null;
|
320
|
-
surface = null;
|
321
|
-
overlay = null;
|
322
|
-
eventHolder = null;
|
323
|
-
ctx = null;
|
324
|
-
octx = null;
|
325
|
-
xaxes = [];
|
326
|
-
yaxes = [];
|
327
|
-
hooks = null;
|
328
|
-
plot = null;
|
329
|
-
};
|
330
|
-
|
331
|
-
plot.resize = function() {
|
332
|
-
var width = placeholder.width(),
|
333
|
-
height = placeholder.height();
|
334
|
-
surface.resize(width, height);
|
335
|
-
overlay.resize(width, height);
|
336
|
-
|
337
|
-
executeHooks(hooks.resize, [width, height]);
|
338
|
-
};
|
339
|
-
|
340
|
-
plot.clearTextCache = function () {
|
341
|
-
surface.clearCache();
|
342
|
-
overlay.clearCache();
|
343
|
-
};
|
344
|
-
|
345
|
-
plot.autoScaleAxis = autoScaleAxis;
|
346
|
-
plot.computeRangeForDataSeries = computeRangeForDataSeries;
|
347
|
-
plot.adjustSeriesDataRange = adjustSeriesDataRange;
|
348
|
-
plot.findNearbyItem = findNearbyItem;
|
349
|
-
plot.findNearbyItems = findNearbyItems;
|
350
|
-
plot.findNearbyInterpolationPoint = findNearbyInterpolationPoint;
|
351
|
-
plot.computeValuePrecision = computeValuePrecision;
|
352
|
-
plot.computeTickSize = computeTickSize;
|
353
|
-
plot.addEventHandler = addEventHandler;
|
354
|
-
|
355
|
-
// public attributes
|
356
|
-
plot.hooks = hooks;
|
357
|
-
|
358
|
-
// initialize
|
359
|
-
var MINOR_TICKS_COUNT_CONSTANT = $.plot.uiConstants.MINOR_TICKS_COUNT_CONSTANT;
|
360
|
-
var TICK_LENGTH_CONSTANT = $.plot.uiConstants.TICK_LENGTH_CONSTANT;
|
361
|
-
initPlugins(plot);
|
362
|
-
setupCanvases();
|
363
|
-
parseOptions(options_);
|
364
|
-
setData(data_);
|
365
|
-
setupGrid(true);
|
366
|
-
draw();
|
367
|
-
bindEvents();
|
368
|
-
|
369
|
-
function executeHooks(hook, args) {
|
370
|
-
args = [plot].concat(args);
|
371
|
-
for (var i = 0; i < hook.length; ++i) {
|
372
|
-
hook[i].apply(this, args);
|
373
|
-
}
|
374
|
-
}
|
375
|
-
|
376
|
-
function initPlugins() {
|
377
|
-
// References to key classes, allowing plugins to modify them
|
378
|
-
|
379
|
-
var classes = {
|
380
|
-
Canvas: Canvas
|
381
|
-
};
|
382
|
-
|
383
|
-
for (var i = 0; i < plugins.length; ++i) {
|
384
|
-
var p = plugins[i];
|
385
|
-
p.init(plot, classes);
|
386
|
-
if (p.options) {
|
387
|
-
$.extend(true, options, p.options);
|
388
|
-
}
|
389
|
-
}
|
390
|
-
}
|
391
|
-
|
392
|
-
function parseOptions(opts) {
|
393
|
-
$.extend(true, options, opts);
|
394
|
-
|
395
|
-
// $.extend merges arrays, rather than replacing them. When less
|
396
|
-
// colors are provided than the size of the default palette, we
|
397
|
-
// end up with those colors plus the remaining defaults, which is
|
398
|
-
// not expected behavior; avoid it by replacing them here.
|
399
|
-
|
400
|
-
if (opts && opts.colors) {
|
401
|
-
options.colors = opts.colors;
|
402
|
-
}
|
403
|
-
|
404
|
-
if (options.xaxis.color == null) {
|
405
|
-
options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString();
|
406
|
-
}
|
407
|
-
|
408
|
-
if (options.yaxis.color == null) {
|
409
|
-
options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString();
|
410
|
-
}
|
411
|
-
|
412
|
-
if (options.xaxis.tickColor == null) {
|
413
|
-
// grid.tickColor for back-compatibility
|
414
|
-
options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color;
|
415
|
-
}
|
416
|
-
|
417
|
-
if (options.yaxis.tickColor == null) {
|
418
|
-
// grid.tickColor for back-compatibility
|
419
|
-
options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color;
|
420
|
-
}
|
421
|
-
|
422
|
-
if (options.grid.borderColor == null) {
|
423
|
-
options.grid.borderColor = options.grid.color;
|
424
|
-
}
|
425
|
-
|
426
|
-
if (options.grid.tickColor == null) {
|
427
|
-
options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();
|
428
|
-
}
|
429
|
-
|
430
|
-
// Fill in defaults for axis options, including any unspecified
|
431
|
-
// font-spec fields, if a font-spec was provided.
|
432
|
-
|
433
|
-
// If no x/y axis options were provided, create one of each anyway,
|
434
|
-
// since the rest of the code assumes that they exist.
|
435
|
-
|
436
|
-
var i, axisOptions, axisCount,
|
437
|
-
fontSize = placeholder.css("font-size"),
|
438
|
-
fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13,
|
439
|
-
fontDefaults = {
|
440
|
-
style: placeholder.css("font-style"),
|
441
|
-
size: Math.round(0.8 * fontSizeDefault),
|
442
|
-
variant: placeholder.css("font-variant"),
|
443
|
-
weight: placeholder.css("font-weight"),
|
444
|
-
family: placeholder.css("font-family")
|
445
|
-
};
|
446
|
-
|
447
|
-
axisCount = options.xaxes.length || 1;
|
448
|
-
for (i = 0; i < axisCount; ++i) {
|
449
|
-
axisOptions = options.xaxes[i];
|
450
|
-
if (axisOptions && !axisOptions.tickColor) {
|
451
|
-
axisOptions.tickColor = axisOptions.color;
|
452
|
-
}
|
453
|
-
|
454
|
-
axisOptions = $.extend(true, {}, options.xaxis, axisOptions);
|
455
|
-
options.xaxes[i] = axisOptions;
|
456
|
-
|
457
|
-
if (axisOptions.font) {
|
458
|
-
axisOptions.font = $.extend({}, fontDefaults, axisOptions.font);
|
459
|
-
if (!axisOptions.font.color) {
|
460
|
-
axisOptions.font.color = axisOptions.color;
|
461
|
-
}
|
462
|
-
if (!axisOptions.font.lineHeight) {
|
463
|
-
axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15);
|
464
|
-
}
|
465
|
-
}
|
466
|
-
}
|
467
|
-
|
468
|
-
axisCount = options.yaxes.length || 1;
|
469
|
-
for (i = 0; i < axisCount; ++i) {
|
470
|
-
axisOptions = options.yaxes[i];
|
471
|
-
if (axisOptions && !axisOptions.tickColor) {
|
472
|
-
axisOptions.tickColor = axisOptions.color;
|
473
|
-
}
|
474
|
-
|
475
|
-
axisOptions = $.extend(true, {}, options.yaxis, axisOptions);
|
476
|
-
options.yaxes[i] = axisOptions;
|
477
|
-
|
478
|
-
if (axisOptions.font) {
|
479
|
-
axisOptions.font = $.extend({}, fontDefaults, axisOptions.font);
|
480
|
-
if (!axisOptions.font.color) {
|
481
|
-
axisOptions.font.color = axisOptions.color;
|
482
|
-
}
|
483
|
-
if (!axisOptions.font.lineHeight) {
|
484
|
-
axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15);
|
485
|
-
}
|
486
|
-
}
|
487
|
-
}
|
488
|
-
|
489
|
-
// save options on axes for future reference
|
490
|
-
for (i = 0; i < options.xaxes.length; ++i) {
|
491
|
-
getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
|
492
|
-
}
|
493
|
-
|
494
|
-
for (i = 0; i < options.yaxes.length; ++i) {
|
495
|
-
getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];
|
496
|
-
}
|
497
|
-
|
498
|
-
//process boxPosition options used for axis.box size
|
499
|
-
$.each(allAxes(), function(_, axis) {
|
500
|
-
axis.boxPosition = axis.options.boxPosition || {centerX: 0, centerY: 0};
|
501
|
-
});
|
502
|
-
|
503
|
-
// add hooks from options
|
504
|
-
for (var n in hooks) {
|
505
|
-
if (options.hooks[n] && options.hooks[n].length) {
|
506
|
-
hooks[n] = hooks[n].concat(options.hooks[n]);
|
507
|
-
}
|
508
|
-
}
|
509
|
-
|
510
|
-
executeHooks(hooks.processOptions, [options]);
|
511
|
-
}
|
512
|
-
|
513
|
-
function setData(d) {
|
514
|
-
var oldseries = series;
|
515
|
-
series = parseData(d);
|
516
|
-
fillInSeriesOptions();
|
517
|
-
processData(oldseries);
|
518
|
-
}
|
519
|
-
|
520
|
-
function parseData(d) {
|
521
|
-
var res = [];
|
522
|
-
for (var i = 0; i < d.length; ++i) {
|
523
|
-
var s = $.extend(true, {}, options.series);
|
524
|
-
|
525
|
-
if (d[i].data != null) {
|
526
|
-
s.data = d[i].data; // move the data instead of deep-copy
|
527
|
-
delete d[i].data;
|
528
|
-
|
529
|
-
$.extend(true, s, d[i]);
|
530
|
-
|
531
|
-
d[i].data = s.data;
|
532
|
-
} else {
|
533
|
-
s.data = d[i];
|
534
|
-
}
|
535
|
-
|
536
|
-
res.push(s);
|
537
|
-
}
|
538
|
-
|
539
|
-
return res;
|
540
|
-
}
|
541
|
-
|
542
|
-
function axisNumber(obj, coord) {
|
543
|
-
var a = obj[coord + "axis"];
|
544
|
-
if (typeof a === "object") {
|
545
|
-
// if we got a real axis, extract number
|
546
|
-
a = a.n;
|
547
|
-
}
|
548
|
-
|
549
|
-
if (typeof a !== "number") {
|
550
|
-
a = 1; // default to first axis
|
551
|
-
}
|
552
|
-
|
553
|
-
return a;
|
554
|
-
}
|
555
|
-
|
556
|
-
function allAxes() {
|
557
|
-
// return flat array without annoying null entries
|
558
|
-
return xaxes.concat(yaxes).filter(function(a) {
|
559
|
-
return a;
|
560
|
-
});
|
561
|
-
}
|
562
|
-
|
563
|
-
// canvas to axis for cartesian axes
|
564
|
-
function canvasToCartesianAxisCoords(pos) {
|
565
|
-
// return an object with x/y corresponding to all used axes
|
566
|
-
var res = {},
|
567
|
-
i, axis;
|
568
|
-
for (i = 0; i < xaxes.length; ++i) {
|
569
|
-
axis = xaxes[i];
|
570
|
-
if (axis && axis.used) {
|
571
|
-
res["x" + axis.n] = axis.c2p(pos.left);
|
572
|
-
}
|
573
|
-
}
|
574
|
-
|
575
|
-
for (i = 0; i < yaxes.length; ++i) {
|
576
|
-
axis = yaxes[i];
|
577
|
-
if (axis && axis.used) {
|
578
|
-
res["y" + axis.n] = axis.c2p(pos.top);
|
579
|
-
}
|
580
|
-
}
|
581
|
-
|
582
|
-
if (res.x1 !== undefined) {
|
583
|
-
res.x = res.x1;
|
584
|
-
}
|
585
|
-
|
586
|
-
if (res.y1 !== undefined) {
|
587
|
-
res.y = res.y1;
|
588
|
-
}
|
589
|
-
|
590
|
-
return res;
|
591
|
-
}
|
592
|
-
|
593
|
-
// axis to canvas for cartesian axes
|
594
|
-
function cartesianAxisToCanvasCoords(pos) {
|
595
|
-
// get canvas coords from the first pair of x/y found in pos
|
596
|
-
var res = {},
|
597
|
-
i, axis, key;
|
598
|
-
|
599
|
-
for (i = 0; i < xaxes.length; ++i) {
|
600
|
-
axis = xaxes[i];
|
601
|
-
if (axis && axis.used) {
|
602
|
-
key = "x" + axis.n;
|
603
|
-
if (pos[key] == null && axis.n === 1) {
|
604
|
-
key = "x";
|
605
|
-
}
|
606
|
-
|
607
|
-
if (pos[key] != null) {
|
608
|
-
res.left = axis.p2c(pos[key]);
|
609
|
-
break;
|
610
|
-
}
|
611
|
-
}
|
612
|
-
}
|
613
|
-
|
614
|
-
for (i = 0; i < yaxes.length; ++i) {
|
615
|
-
axis = yaxes[i];
|
616
|
-
if (axis && axis.used) {
|
617
|
-
key = "y" + axis.n;
|
618
|
-
if (pos[key] == null && axis.n === 1) {
|
619
|
-
key = "y";
|
620
|
-
}
|
621
|
-
|
622
|
-
if (pos[key] != null) {
|
623
|
-
res.top = axis.p2c(pos[key]);
|
624
|
-
break;
|
625
|
-
}
|
626
|
-
}
|
627
|
-
}
|
628
|
-
|
629
|
-
return res;
|
630
|
-
}
|
631
|
-
|
632
|
-
function getOrCreateAxis(axes, number) {
|
633
|
-
if (!axes[number - 1]) {
|
634
|
-
axes[number - 1] = {
|
635
|
-
n: number, // save the number for future reference
|
636
|
-
direction: axes === xaxes ? "x" : "y",
|
637
|
-
options: $.extend(true, {}, axes === xaxes ? options.xaxis : options.yaxis)
|
638
|
-
};
|
639
|
-
}
|
640
|
-
|
641
|
-
return axes[number - 1];
|
642
|
-
}
|
643
|
-
|
644
|
-
function fillInSeriesOptions() {
|
645
|
-
var neededColors = series.length,
|
646
|
-
maxIndex = -1,
|
647
|
-
i;
|
648
|
-
|
649
|
-
// Subtract the number of series that already have fixed colors or
|
650
|
-
// color indexes from the number that we still need to generate.
|
651
|
-
|
652
|
-
for (i = 0; i < series.length; ++i) {
|
653
|
-
var sc = series[i].color;
|
654
|
-
if (sc != null) {
|
655
|
-
neededColors--;
|
656
|
-
if (typeof sc === "number" && sc > maxIndex) {
|
657
|
-
maxIndex = sc;
|
658
|
-
}
|
659
|
-
}
|
660
|
-
}
|
661
|
-
|
662
|
-
// If any of the series have fixed color indexes, then we need to
|
663
|
-
// generate at least as many colors as the highest index.
|
664
|
-
|
665
|
-
if (neededColors <= maxIndex) {
|
666
|
-
neededColors = maxIndex + 1;
|
667
|
-
}
|
668
|
-
|
669
|
-
// Generate all the colors, using first the option colors and then
|
670
|
-
// variations on those colors once they're exhausted.
|
671
|
-
|
672
|
-
var c, colors = [],
|
673
|
-
colorPool = options.colors,
|
674
|
-
colorPoolSize = colorPool.length,
|
675
|
-
variation = 0,
|
676
|
-
definedColors = Math.max(0, series.length - neededColors);
|
677
|
-
|
678
|
-
for (i = 0; i < neededColors; i++) {
|
679
|
-
c = $.color.parse(colorPool[(definedColors + i) % colorPoolSize] || "#666");
|
680
|
-
|
681
|
-
// Each time we exhaust the colors in the pool we adjust
|
682
|
-
// a scaling factor used to produce more variations on
|
683
|
-
// those colors. The factor alternates negative/positive
|
684
|
-
// to produce lighter/darker colors.
|
685
|
-
|
686
|
-
// Reset the variation after every few cycles, or else
|
687
|
-
// it will end up producing only white or black colors.
|
688
|
-
|
689
|
-
if (i % colorPoolSize === 0 && i) {
|
690
|
-
if (variation >= 0) {
|
691
|
-
if (variation < 0.5) {
|
692
|
-
variation = -variation - 0.2;
|
693
|
-
} else variation = 0;
|
694
|
-
} else variation = -variation;
|
695
|
-
}
|
696
|
-
|
697
|
-
colors[i] = c.scale('rgb', 1 + variation);
|
698
|
-
}
|
699
|
-
|
700
|
-
// Finalize the series options, filling in their colors
|
701
|
-
|
702
|
-
var colori = 0,
|
703
|
-
s;
|
704
|
-
for (i = 0; i < series.length; ++i) {
|
705
|
-
s = series[i];
|
706
|
-
|
707
|
-
// assign colors
|
708
|
-
if (s.color == null) {
|
709
|
-
s.color = colors[colori].toString();
|
710
|
-
++colori;
|
711
|
-
} else if (typeof s.color === "number") {
|
712
|
-
s.color = colors[s.color].toString();
|
713
|
-
}
|
714
|
-
|
715
|
-
// turn on lines automatically in case nothing is set
|
716
|
-
if (s.lines.show == null) {
|
717
|
-
var v, show = true;
|
718
|
-
for (v in s) {
|
719
|
-
if (s[v] && s[v].show) {
|
720
|
-
show = false;
|
721
|
-
break;
|
722
|
-
}
|
723
|
-
}
|
724
|
-
|
725
|
-
if (show) {
|
726
|
-
s.lines.show = true;
|
727
|
-
}
|
728
|
-
}
|
729
|
-
|
730
|
-
// If nothing was provided for lines.zero, default it to match
|
731
|
-
// lines.fill, since areas by default should extend to zero.
|
732
|
-
|
733
|
-
if (s.lines.zero == null) {
|
734
|
-
s.lines.zero = !!s.lines.fill;
|
735
|
-
}
|
736
|
-
|
737
|
-
// setup axes
|
738
|
-
s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
|
739
|
-
s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
|
740
|
-
}
|
741
|
-
}
|
742
|
-
|
743
|
-
function processData(prevSeries) {
|
744
|
-
var topSentry = Number.POSITIVE_INFINITY,
|
745
|
-
bottomSentry = Number.NEGATIVE_INFINITY,
|
746
|
-
i, j, k, m,
|
747
|
-
s, points, ps, val, f, p,
|
748
|
-
data, format;
|
749
|
-
|
750
|
-
function updateAxis(axis, min, max) {
|
751
|
-
if (min < axis.datamin && min !== -Infinity) {
|
752
|
-
axis.datamin = min;
|
753
|
-
}
|
754
|
-
|
755
|
-
if (max > axis.datamax && max !== Infinity) {
|
756
|
-
axis.datamax = max;
|
757
|
-
}
|
758
|
-
}
|
759
|
-
|
760
|
-
function reusePoints(prevSeries, i) {
|
761
|
-
if (prevSeries && prevSeries[i] && prevSeries[i].datapoints && prevSeries[i].datapoints.points) {
|
762
|
-
return prevSeries[i].datapoints.points;
|
763
|
-
}
|
764
|
-
|
765
|
-
return [];
|
766
|
-
}
|
767
|
-
|
768
|
-
$.each(allAxes(), function(_, axis) {
|
769
|
-
// init axis
|
770
|
-
if (axis.options.growOnly !== true) {
|
771
|
-
axis.datamin = topSentry;
|
772
|
-
axis.datamax = bottomSentry;
|
773
|
-
} else {
|
774
|
-
if (axis.datamin === undefined) {
|
775
|
-
axis.datamin = topSentry;
|
776
|
-
}
|
777
|
-
if (axis.datamax === undefined) {
|
778
|
-
axis.datamax = bottomSentry;
|
779
|
-
}
|
780
|
-
}
|
781
|
-
axis.used = false;
|
782
|
-
});
|
783
|
-
|
784
|
-
for (i = 0; i < series.length; ++i) {
|
785
|
-
s = series[i];
|
786
|
-
s.datapoints = {
|
787
|
-
points: []
|
788
|
-
};
|
789
|
-
|
790
|
-
if (s.datapoints.points.length === 0) {
|
791
|
-
s.datapoints.points = reusePoints(prevSeries, i);
|
792
|
-
}
|
793
|
-
|
794
|
-
executeHooks(hooks.processRawData, [s, s.data, s.datapoints]);
|
795
|
-
}
|
796
|
-
|
797
|
-
// first pass: clean and copy data
|
798
|
-
for (i = 0; i < series.length; ++i) {
|
799
|
-
s = series[i];
|
800
|
-
|
801
|
-
data = s.data;
|
802
|
-
format = s.datapoints.format;
|
803
|
-
|
804
|
-
if (!format) {
|
805
|
-
format = [];
|
806
|
-
// find out how to copy
|
807
|
-
format.push({
|
808
|
-
x: true,
|
809
|
-
y: false,
|
810
|
-
number: true,
|
811
|
-
required: true,
|
812
|
-
computeRange: s.xaxis.options.autoScale !== 'none',
|
813
|
-
defaultValue: null
|
814
|
-
});
|
815
|
-
|
816
|
-
format.push({
|
817
|
-
x: false,
|
818
|
-
y: true,
|
819
|
-
number: true,
|
820
|
-
required: true,
|
821
|
-
computeRange: s.yaxis.options.autoScale !== 'none',
|
822
|
-
defaultValue: null
|
823
|
-
});
|
824
|
-
|
825
|
-
if (s.stack || s.bars.show || (s.lines.show && s.lines.fill)) {
|
826
|
-
var expectedPs = s.datapoints.pointsize != null ? s.datapoints.pointsize : (s.data && s.data[0] && s.data[0].length ? s.data[0].length : 3);
|
827
|
-
if (expectedPs > 2) {
|
828
|
-
format.push({
|
829
|
-
x: s.bars.horizontal,
|
830
|
-
y: !s.bars.horizontal,
|
831
|
-
number: true,
|
832
|
-
required: false,
|
833
|
-
computeRange: s.yaxis.options.autoScale !== 'none',
|
834
|
-
defaultValue: 0
|
835
|
-
});
|
836
|
-
}
|
837
|
-
}
|
838
|
-
|
839
|
-
s.datapoints.format = format;
|
840
|
-
}
|
841
|
-
|
842
|
-
s.xaxis.used = s.yaxis.used = true;
|
843
|
-
|
844
|
-
if (s.datapoints.pointsize != null) continue; // already filled in
|
845
|
-
|
846
|
-
s.datapoints.pointsize = format.length;
|
847
|
-
ps = s.datapoints.pointsize;
|
848
|
-
points = s.datapoints.points;
|
849
|
-
|
850
|
-
for (j = k = 0; j < data.length; ++j, k += ps) {
|
851
|
-
p = data[j];
|
852
|
-
|
853
|
-
var nullify = p == null;
|
854
|
-
if (!nullify) {
|
855
|
-
for (m = 0; m < ps; ++m) {
|
856
|
-
val = p[m];
|
857
|
-
f = format[m];
|
858
|
-
|
859
|
-
if (f) {
|
860
|
-
if (f.number && val != null) {
|
861
|
-
val = +val; // convert to number
|
862
|
-
if (isNaN(val)) {
|
863
|
-
val = null;
|
864
|
-
}
|
865
|
-
}
|
866
|
-
|
867
|
-
if (val == null) {
|
868
|
-
if (f.required) nullify = true;
|
869
|
-
|
870
|
-
if (f.defaultValue != null) val = f.defaultValue;
|
871
|
-
}
|
872
|
-
}
|
873
|
-
|
874
|
-
points[k + m] = val;
|
875
|
-
}
|
876
|
-
}
|
877
|
-
|
878
|
-
if (nullify) {
|
879
|
-
for (m = 0; m < ps; ++m) {
|
880
|
-
val = points[k + m];
|
881
|
-
if (val != null) {
|
882
|
-
f = format[m];
|
883
|
-
// extract min/max info
|
884
|
-
if (f.computeRange) {
|
885
|
-
if (f.x) {
|
886
|
-
updateAxis(s.xaxis, val, val);
|
887
|
-
}
|
888
|
-
if (f.y) {
|
889
|
-
updateAxis(s.yaxis, val, val);
|
890
|
-
}
|
891
|
-
}
|
892
|
-
}
|
893
|
-
points[k + m] = null;
|
894
|
-
}
|
895
|
-
}
|
896
|
-
}
|
897
|
-
|
898
|
-
points.length = k; //trims the internal buffer to the correct length
|
899
|
-
}
|
900
|
-
|
901
|
-
// give the hooks a chance to run
|
902
|
-
for (i = 0; i < series.length; ++i) {
|
903
|
-
s = series[i];
|
904
|
-
|
905
|
-
executeHooks(hooks.processDatapoints, [s, s.datapoints]);
|
906
|
-
}
|
907
|
-
|
908
|
-
// second pass: find datamax/datamin for auto-scaling
|
909
|
-
for (i = 0; i < series.length; ++i) {
|
910
|
-
s = series[i];
|
911
|
-
format = s.datapoints.format;
|
912
|
-
|
913
|
-
if (format.every(function (f) { return !f.computeRange; })) {
|
914
|
-
continue;
|
915
|
-
}
|
916
|
-
|
917
|
-
var range = plot.adjustSeriesDataRange(s,
|
918
|
-
plot.computeRangeForDataSeries(s));
|
919
|
-
|
920
|
-
executeHooks(hooks.adjustSeriesDataRange, [s, range]);
|
921
|
-
|
922
|
-
updateAxis(s.xaxis, range.xmin, range.xmax);
|
923
|
-
updateAxis(s.yaxis, range.ymin, range.ymax);
|
924
|
-
}
|
925
|
-
|
926
|
-
$.each(allAxes(), function(_, axis) {
|
927
|
-
if (axis.datamin === topSentry) {
|
928
|
-
axis.datamin = null;
|
929
|
-
}
|
930
|
-
|
931
|
-
if (axis.datamax === bottomSentry) {
|
932
|
-
axis.datamax = null;
|
933
|
-
}
|
934
|
-
});
|
935
|
-
}
|
936
|
-
|
937
|
-
function setupCanvases() {
|
938
|
-
// Make sure the placeholder is clear of everything except canvases
|
939
|
-
// from a previous plot in this container that we'll try to re-use.
|
940
|
-
|
941
|
-
placeholder.css("padding", 0) // padding messes up the positioning
|
942
|
-
.children().filter(function() {
|
943
|
-
return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base');
|
944
|
-
}).remove();
|
945
|
-
|
946
|
-
if (placeholder.css("position") === 'static') {
|
947
|
-
placeholder.css("position", "relative"); // for positioning labels and overlay
|
948
|
-
}
|
949
|
-
|
950
|
-
surface = new Canvas("flot-base", placeholder[0]);
|
951
|
-
overlay = new Canvas("flot-overlay", placeholder[0]); // overlay canvas for interactive features
|
952
|
-
|
953
|
-
ctx = surface.context;
|
954
|
-
octx = overlay.context;
|
955
|
-
|
956
|
-
// define which element we're listening for events on
|
957
|
-
eventHolder = $(overlay.element).unbind();
|
958
|
-
|
959
|
-
// If we're re-using a plot object, shut down the old one
|
960
|
-
|
961
|
-
var existing = placeholder.data("plot");
|
962
|
-
|
963
|
-
if (existing) {
|
964
|
-
existing.shutdown();
|
965
|
-
overlay.clear();
|
966
|
-
}
|
967
|
-
|
968
|
-
// save in case we get replotted
|
969
|
-
placeholder.data("plot", plot);
|
970
|
-
}
|
971
|
-
|
972
|
-
function bindEvents() {
|
973
|
-
executeHooks(hooks.bindEvents, [eventHolder]);
|
974
|
-
}
|
975
|
-
|
976
|
-
function addEventHandler(event, handler, eventHolder, priority) {
|
977
|
-
var key = eventHolder + event;
|
978
|
-
var eventList = eventManager[key] || [];
|
979
|
-
|
980
|
-
eventList.push({"event": event, "handler": handler, "eventHolder": eventHolder, "priority": priority});
|
981
|
-
eventList.sort((a, b) => b.priority - a.priority);
|
982
|
-
eventList.forEach(eventData => {
|
983
|
-
eventData.eventHolder.unbind(eventData.event, eventData.handler);
|
984
|
-
eventData.eventHolder.bind(eventData.event, eventData.handler);
|
985
|
-
});
|
986
|
-
|
987
|
-
eventManager[key] = eventList;
|
988
|
-
}
|
989
|
-
|
990
|
-
function shutdown() {
|
991
|
-
if (redrawTimeout) {
|
992
|
-
clearTimeout(redrawTimeout);
|
993
|
-
}
|
994
|
-
|
995
|
-
executeHooks(hooks.shutdown, [eventHolder]);
|
996
|
-
}
|
997
|
-
|
998
|
-
function setTransformationHelpers(axis) {
|
999
|
-
// set helper functions on the axis, assumes plot area
|
1000
|
-
// has been computed already
|
1001
|
-
|
1002
|
-
function identity(x) {
|
1003
|
-
return x;
|
1004
|
-
}
|
1005
|
-
|
1006
|
-
var s, m, t = axis.options.transform || identity,
|
1007
|
-
it = axis.options.inverseTransform;
|
1008
|
-
|
1009
|
-
// precompute how much the axis is scaling a point
|
1010
|
-
// in canvas space
|
1011
|
-
if (axis.direction === "x") {
|
1012
|
-
if (isFinite(t(axis.max) - t(axis.min))) {
|
1013
|
-
s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));
|
1014
|
-
} else {
|
1015
|
-
s = axis.scale = 1 / Math.abs($.plot.saturated.delta(t(axis.min), t(axis.max), plotWidth));
|
1016
|
-
}
|
1017
|
-
m = Math.min(t(axis.max), t(axis.min));
|
1018
|
-
} else {
|
1019
|
-
if (isFinite(t(axis.max) - t(axis.min))) {
|
1020
|
-
s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));
|
1021
|
-
} else {
|
1022
|
-
s = axis.scale = 1 / Math.abs($.plot.saturated.delta(t(axis.min), t(axis.max), plotHeight));
|
1023
|
-
}
|
1024
|
-
s = -s;
|
1025
|
-
m = Math.max(t(axis.max), t(axis.min));
|
1026
|
-
}
|
1027
|
-
|
1028
|
-
// data point to canvas coordinate
|
1029
|
-
if (t === identity) {
|
1030
|
-
// slight optimization
|
1031
|
-
axis.p2c = function(p) {
|
1032
|
-
if (isFinite(p - m)) {
|
1033
|
-
return (p - m) * s;
|
1034
|
-
} else {
|
1035
|
-
return (p / 4 - m / 4) * s * 4;
|
1036
|
-
}
|
1037
|
-
};
|
1038
|
-
} else {
|
1039
|
-
axis.p2c = function(p) {
|
1040
|
-
var tp = t(p);
|
1041
|
-
|
1042
|
-
if (isFinite(tp - m)) {
|
1043
|
-
return (tp - m) * s;
|
1044
|
-
} else {
|
1045
|
-
return (tp / 4 - m / 4) * s * 4;
|
1046
|
-
}
|
1047
|
-
};
|
1048
|
-
}
|
1049
|
-
|
1050
|
-
// canvas coordinate to data point
|
1051
|
-
if (!it) {
|
1052
|
-
axis.c2p = function(c) {
|
1053
|
-
return m + c / s;
|
1054
|
-
};
|
1055
|
-
} else {
|
1056
|
-
axis.c2p = function(c) {
|
1057
|
-
return it(m + c / s);
|
1058
|
-
};
|
1059
|
-
}
|
1060
|
-
}
|
1061
|
-
|
1062
|
-
function measureTickLabels(axis) {
|
1063
|
-
var opts = axis.options,
|
1064
|
-
ticks = opts.showTickLabels !== 'none' && axis.ticks ? axis.ticks : [],
|
1065
|
-
showMajorTickLabels = opts.showTickLabels === 'major' || opts.showTickLabels === 'all',
|
1066
|
-
showEndpointsTickLabels = opts.showTickLabels === 'endpoints' || opts.showTickLabels === 'all',
|
1067
|
-
labelWidth = opts.labelWidth || 0,
|
1068
|
-
labelHeight = opts.labelHeight || 0,
|
1069
|
-
legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
|
1070
|
-
layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
|
1071
|
-
font = opts.font || "flot-tick-label tickLabel";
|
1072
|
-
|
1073
|
-
for (var i = 0; i < ticks.length; ++i) {
|
1074
|
-
var t = ticks[i];
|
1075
|
-
var label = t.label;
|
1076
|
-
|
1077
|
-
if (!t.label ||
|
1078
|
-
(showMajorTickLabels === false && i > 0 && i < ticks.length - 1) ||
|
1079
|
-
(showEndpointsTickLabels === false && (i === 0 || i === ticks.length - 1))) {
|
1080
|
-
continue;
|
1081
|
-
}
|
1082
|
-
|
1083
|
-
if (typeof t.label === 'object') {
|
1084
|
-
label = t.label.name;
|
1085
|
-
}
|
1086
|
-
|
1087
|
-
var info = surface.getTextInfo(layer, label, font);
|
1088
|
-
|
1089
|
-
labelWidth = Math.max(labelWidth, info.width);
|
1090
|
-
labelHeight = Math.max(labelHeight, info.height);
|
1091
|
-
}
|
1092
|
-
|
1093
|
-
axis.labelWidth = opts.labelWidth || labelWidth;
|
1094
|
-
axis.labelHeight = opts.labelHeight || labelHeight;
|
1095
|
-
}
|
1096
|
-
|
1097
|
-
function allocateAxisBoxFirstPhase(axis) {
|
1098
|
-
// find the bounding box of the axis by looking at label
|
1099
|
-
// widths/heights and ticks, make room by diminishing the
|
1100
|
-
// plotOffset; this first phase only looks at one
|
1101
|
-
// dimension per axis, the other dimension depends on the
|
1102
|
-
// other axes so will have to wait
|
1103
|
-
|
1104
|
-
// here reserve additional space
|
1105
|
-
executeHooks(hooks.axisReserveSpace, [axis]);
|
1106
|
-
|
1107
|
-
var lw = axis.labelWidth,
|
1108
|
-
lh = axis.labelHeight,
|
1109
|
-
pos = axis.options.position,
|
1110
|
-
isXAxis = axis.direction === "x",
|
1111
|
-
tickLength = axis.options.tickLength,
|
1112
|
-
showTicks = axis.options.showTicks,
|
1113
|
-
showMinorTicks = axis.options.showMinorTicks,
|
1114
|
-
gridLines = axis.options.gridLines,
|
1115
|
-
axisMargin = options.grid.axisMargin,
|
1116
|
-
padding = options.grid.labelMargin,
|
1117
|
-
innermost = true,
|
1118
|
-
outermost = true,
|
1119
|
-
found = false;
|
1120
|
-
|
1121
|
-
// Determine the axis's position in its direction and on its side
|
1122
|
-
|
1123
|
-
$.each(isXAxis ? xaxes : yaxes, function(i, a) {
|
1124
|
-
if (a && (a.show || a.reserveSpace)) {
|
1125
|
-
if (a === axis) {
|
1126
|
-
found = true;
|
1127
|
-
} else if (a.options.position === pos) {
|
1128
|
-
if (found) {
|
1129
|
-
outermost = false;
|
1130
|
-
} else {
|
1131
|
-
innermost = false;
|
1132
|
-
}
|
1133
|
-
}
|
1134
|
-
}
|
1135
|
-
});
|
1136
|
-
|
1137
|
-
// The outermost axis on each side has no margin
|
1138
|
-
if (outermost) {
|
1139
|
-
axisMargin = 0;
|
1140
|
-
}
|
1141
|
-
|
1142
|
-
// Set the default tickLength if necessary
|
1143
|
-
if (tickLength == null) {
|
1144
|
-
tickLength = TICK_LENGTH_CONSTANT;
|
1145
|
-
}
|
1146
|
-
|
1147
|
-
// By default, major tick marks are visible
|
1148
|
-
if (showTicks == null) {
|
1149
|
-
showTicks = true;
|
1150
|
-
}
|
1151
|
-
|
1152
|
-
// By default, minor tick marks are visible
|
1153
|
-
if (showMinorTicks == null) {
|
1154
|
-
showMinorTicks = true;
|
1155
|
-
}
|
1156
|
-
|
1157
|
-
// By default, grid lines are visible
|
1158
|
-
if (gridLines == null) {
|
1159
|
-
if (innermost) {
|
1160
|
-
gridLines = true;
|
1161
|
-
} else {
|
1162
|
-
gridLines = false;
|
1163
|
-
}
|
1164
|
-
}
|
1165
|
-
|
1166
|
-
if (!isNaN(+tickLength)) {
|
1167
|
-
padding += showTicks ? +tickLength : 0;
|
1168
|
-
}
|
1169
|
-
|
1170
|
-
if (isXAxis) {
|
1171
|
-
lh += padding;
|
1172
|
-
|
1173
|
-
if (pos === "bottom") {
|
1174
|
-
plotOffset.bottom += lh + axisMargin;
|
1175
|
-
axis.box = {
|
1176
|
-
top: surface.height - plotOffset.bottom,
|
1177
|
-
height: lh
|
1178
|
-
};
|
1179
|
-
} else {
|
1180
|
-
axis.box = {
|
1181
|
-
top: plotOffset.top + axisMargin,
|
1182
|
-
height: lh
|
1183
|
-
};
|
1184
|
-
plotOffset.top += lh + axisMargin;
|
1185
|
-
}
|
1186
|
-
} else {
|
1187
|
-
lw += padding;
|
1188
|
-
|
1189
|
-
if (pos === "left") {
|
1190
|
-
axis.box = {
|
1191
|
-
left: plotOffset.left + axisMargin,
|
1192
|
-
width: lw
|
1193
|
-
};
|
1194
|
-
plotOffset.left += lw + axisMargin;
|
1195
|
-
} else {
|
1196
|
-
plotOffset.right += lw + axisMargin;
|
1197
|
-
axis.box = {
|
1198
|
-
left: surface.width - plotOffset.right,
|
1199
|
-
width: lw
|
1200
|
-
};
|
1201
|
-
}
|
1202
|
-
}
|
1203
|
-
|
1204
|
-
// save for future reference
|
1205
|
-
axis.position = pos;
|
1206
|
-
axis.tickLength = tickLength;
|
1207
|
-
axis.showMinorTicks = showMinorTicks;
|
1208
|
-
axis.showTicks = showTicks;
|
1209
|
-
axis.gridLines = gridLines;
|
1210
|
-
axis.box.padding = padding;
|
1211
|
-
axis.innermost = innermost;
|
1212
|
-
}
|
1213
|
-
|
1214
|
-
function allocateAxisBoxSecondPhase(axis) {
|
1215
|
-
// now that all axis boxes have been placed in one
|
1216
|
-
// dimension, we can set the remaining dimension coordinates
|
1217
|
-
if (axis.direction === "x") {
|
1218
|
-
axis.box.left = plotOffset.left - axis.labelWidth / 2;
|
1219
|
-
axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth;
|
1220
|
-
} else {
|
1221
|
-
axis.box.top = plotOffset.top - axis.labelHeight / 2;
|
1222
|
-
axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight;
|
1223
|
-
}
|
1224
|
-
}
|
1225
|
-
|
1226
|
-
function adjustLayoutForThingsStickingOut() {
|
1227
|
-
// possibly adjust plot offset to ensure everything stays
|
1228
|
-
// inside the canvas and isn't clipped off
|
1229
|
-
|
1230
|
-
var minMargin = options.grid.minBorderMargin,
|
1231
|
-
i;
|
1232
|
-
|
1233
|
-
// check stuff from the plot (FIXME: this should just read
|
1234
|
-
// a value from the series, otherwise it's impossible to
|
1235
|
-
// customize)
|
1236
|
-
if (minMargin == null) {
|
1237
|
-
minMargin = 0;
|
1238
|
-
for (i = 0; i < series.length; ++i) {
|
1239
|
-
minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth / 2));
|
1240
|
-
}
|
1241
|
-
}
|
1242
|
-
|
1243
|
-
var a, offset = {},
|
1244
|
-
margins = {
|
1245
|
-
left: minMargin,
|
1246
|
-
right: minMargin,
|
1247
|
-
top: minMargin,
|
1248
|
-
bottom: minMargin
|
1249
|
-
};
|
1250
|
-
|
1251
|
-
// check axis labels, note we don't check the actual
|
1252
|
-
// labels but instead use the overall width/height to not
|
1253
|
-
// jump as much around with replots
|
1254
|
-
$.each(allAxes(), function(_, axis) {
|
1255
|
-
if (axis.reserveSpace && axis.ticks && axis.ticks.length) {
|
1256
|
-
if (axis.direction === "x") {
|
1257
|
-
margins.left = Math.max(margins.left, axis.labelWidth / 2);
|
1258
|
-
margins.right = Math.max(margins.right, axis.labelWidth / 2);
|
1259
|
-
} else {
|
1260
|
-
margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2);
|
1261
|
-
margins.top = Math.max(margins.top, axis.labelHeight / 2);
|
1262
|
-
}
|
1263
|
-
}
|
1264
|
-
});
|
1265
|
-
|
1266
|
-
for (a in margins) {
|
1267
|
-
offset[a] = margins[a] - plotOffset[a];
|
1268
|
-
}
|
1269
|
-
$.each(xaxes.concat(yaxes), function(_, axis) {
|
1270
|
-
alignAxisWithGrid(axis, offset, function (offset) {
|
1271
|
-
return offset > 0;
|
1272
|
-
});
|
1273
|
-
});
|
1274
|
-
|
1275
|
-
plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left));
|
1276
|
-
plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right));
|
1277
|
-
plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top));
|
1278
|
-
plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom));
|
1279
|
-
}
|
1280
|
-
|
1281
|
-
function alignAxisWithGrid(axis, offset, isValid) {
|
1282
|
-
if (axis.direction === "x") {
|
1283
|
-
if (axis.position === "bottom" && isValid(offset.bottom)) {
|
1284
|
-
axis.box.top -= Math.ceil(offset.bottom);
|
1285
|
-
}
|
1286
|
-
if (axis.position === "top" && isValid(offset.top)) {
|
1287
|
-
axis.box.top += Math.ceil(offset.top);
|
1288
|
-
}
|
1289
|
-
} else {
|
1290
|
-
if (axis.position === "left" && isValid(offset.left)) {
|
1291
|
-
axis.box.left += Math.ceil(offset.left);
|
1292
|
-
}
|
1293
|
-
if (axis.position === "right" && isValid(offset.right)) {
|
1294
|
-
axis.box.left -= Math.ceil(offset.right);
|
1295
|
-
}
|
1296
|
-
}
|
1297
|
-
}
|
1298
|
-
|
1299
|
-
function setupGrid(autoScale) {
|
1300
|
-
var i, a, axes = allAxes(),
|
1301
|
-
showGrid = options.grid.show;
|
1302
|
-
|
1303
|
-
// Initialize the plot's offset from the edge of the canvas
|
1304
|
-
|
1305
|
-
for (a in plotOffset) {
|
1306
|
-
plotOffset[a] = 0;
|
1307
|
-
}
|
1308
|
-
|
1309
|
-
executeHooks(hooks.processOffset, [plotOffset]);
|
1310
|
-
|
1311
|
-
// If the grid is visible, add its border width to the offset
|
1312
|
-
for (a in plotOffset) {
|
1313
|
-
if (typeof (options.grid.borderWidth) === "object") {
|
1314
|
-
plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0;
|
1315
|
-
} else {
|
1316
|
-
plotOffset[a] += showGrid ? options.grid.borderWidth : 0;
|
1317
|
-
}
|
1318
|
-
}
|
1319
|
-
|
1320
|
-
$.each(axes, function(_, axis) {
|
1321
|
-
var axisOpts = axis.options;
|
1322
|
-
axis.show = axisOpts.show == null ? axis.used : axisOpts.show;
|
1323
|
-
axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace;
|
1324
|
-
setupTickFormatter(axis);
|
1325
|
-
executeHooks(hooks.setRange, [axis, autoScale]);
|
1326
|
-
setRange(axis, autoScale);
|
1327
|
-
});
|
1328
|
-
|
1329
|
-
if (showGrid) {
|
1330
|
-
plotWidth = surface.width - plotOffset.left - plotOffset.right;
|
1331
|
-
plotHeight = surface.height - plotOffset.bottom - plotOffset.top;
|
1332
|
-
|
1333
|
-
var allocatedAxes = $.grep(axes, function(axis) {
|
1334
|
-
return axis.show || axis.reserveSpace;
|
1335
|
-
});
|
1336
|
-
|
1337
|
-
$.each(allocatedAxes, function(_, axis) {
|
1338
|
-
// make the ticks
|
1339
|
-
setupTickGeneration(axis);
|
1340
|
-
setMajorTicks(axis);
|
1341
|
-
snapRangeToTicks(axis, axis.ticks, series);
|
1342
|
-
|
1343
|
-
//for computing the endpoints precision, transformationHelpers are needed
|
1344
|
-
setTransformationHelpers(axis);
|
1345
|
-
setEndpointTicks(axis, series);
|
1346
|
-
|
1347
|
-
// find labelWidth/Height for axis
|
1348
|
-
measureTickLabels(axis);
|
1349
|
-
});
|
1350
|
-
|
1351
|
-
// with all dimensions calculated, we can compute the
|
1352
|
-
// axis bounding boxes, start from the outside
|
1353
|
-
// (reverse order)
|
1354
|
-
for (i = allocatedAxes.length - 1; i >= 0; --i) {
|
1355
|
-
allocateAxisBoxFirstPhase(allocatedAxes[i]);
|
1356
|
-
}
|
1357
|
-
|
1358
|
-
// make sure we've got enough space for things that
|
1359
|
-
// might stick out
|
1360
|
-
adjustLayoutForThingsStickingOut();
|
1361
|
-
|
1362
|
-
$.each(allocatedAxes, function(_, axis) {
|
1363
|
-
allocateAxisBoxSecondPhase(axis);
|
1364
|
-
});
|
1365
|
-
}
|
1366
|
-
|
1367
|
-
//adjust axis and plotOffset according to grid.margins
|
1368
|
-
if (options.grid.margin) {
|
1369
|
-
for (a in plotOffset) {
|
1370
|
-
var margin = options.grid.margin || 0;
|
1371
|
-
plotOffset[a] += typeof margin === "number" ? margin : (margin[a] || 0);
|
1372
|
-
}
|
1373
|
-
$.each(xaxes.concat(yaxes), function(_, axis) {
|
1374
|
-
alignAxisWithGrid(axis, options.grid.margin, function(offset) {
|
1375
|
-
return offset !== undefined && offset !== null;
|
1376
|
-
});
|
1377
|
-
});
|
1378
|
-
}
|
1379
|
-
|
1380
|
-
//after adjusting the axis, plot width and height will be modified
|
1381
|
-
plotWidth = surface.width - plotOffset.left - plotOffset.right;
|
1382
|
-
plotHeight = surface.height - plotOffset.bottom - plotOffset.top;
|
1383
|
-
|
1384
|
-
// now we got the proper plot dimensions, we can compute the scaling
|
1385
|
-
$.each(axes, function(_, axis) {
|
1386
|
-
setTransformationHelpers(axis);
|
1387
|
-
});
|
1388
|
-
|
1389
|
-
if (showGrid) {
|
1390
|
-
drawAxisLabels();
|
1391
|
-
}
|
1392
|
-
|
1393
|
-
executeHooks(hooks.setupGrid, []);
|
1394
|
-
}
|
1395
|
-
|
1396
|
-
function widenMinMax(minimum, maximum) {
|
1397
|
-
var min = (minimum === undefined ? null : minimum);
|
1398
|
-
var max = (maximum === undefined ? null : maximum);
|
1399
|
-
var delta = max - min;
|
1400
|
-
if (delta === 0.0) {
|
1401
|
-
// degenerate case
|
1402
|
-
var widen = max === 0 ? 1 : 0.01;
|
1403
|
-
var wmin = null;
|
1404
|
-
if (min == null) {
|
1405
|
-
wmin -= widen;
|
1406
|
-
}
|
1407
|
-
|
1408
|
-
// always widen max if we couldn't widen min to ensure we
|
1409
|
-
// don't fall into min == max which doesn't work
|
1410
|
-
if (max == null || min != null) {
|
1411
|
-
max += widen;
|
1412
|
-
}
|
1413
|
-
|
1414
|
-
if (wmin != null) {
|
1415
|
-
min = wmin;
|
1416
|
-
}
|
1417
|
-
}
|
1418
|
-
|
1419
|
-
return {
|
1420
|
-
min: min,
|
1421
|
-
max: max
|
1422
|
-
};
|
1423
|
-
}
|
1424
|
-
|
1425
|
-
function autoScaleAxis(axis) {
|
1426
|
-
var opts = axis.options,
|
1427
|
-
min = opts.min,
|
1428
|
-
max = opts.max,
|
1429
|
-
datamin = axis.datamin,
|
1430
|
-
datamax = axis.datamax,
|
1431
|
-
delta;
|
1432
|
-
|
1433
|
-
switch (opts.autoScale) {
|
1434
|
-
case "none":
|
1435
|
-
min = +(opts.min != null ? opts.min : datamin);
|
1436
|
-
max = +(opts.max != null ? opts.max : datamax);
|
1437
|
-
break;
|
1438
|
-
case "loose":
|
1439
|
-
if (datamin != null && datamax != null) {
|
1440
|
-
min = datamin;
|
1441
|
-
max = datamax;
|
1442
|
-
delta = $.plot.saturated.saturate(max - min);
|
1443
|
-
var margin = ((typeof opts.autoScaleMargin === 'number') ? opts.autoScaleMargin : 0.02);
|
1444
|
-
min = $.plot.saturated.saturate(min - delta * margin);
|
1445
|
-
max = $.plot.saturated.saturate(max + delta * margin);
|
1446
|
-
|
1447
|
-
// make sure we don't go below zero if all values are positive
|
1448
|
-
if (min < 0 && datamin >= 0) {
|
1449
|
-
min = 0;
|
1450
|
-
}
|
1451
|
-
} else {
|
1452
|
-
min = opts.min;
|
1453
|
-
max = opts.max;
|
1454
|
-
}
|
1455
|
-
break;
|
1456
|
-
case "exact":
|
1457
|
-
min = (datamin != null ? datamin : opts.min);
|
1458
|
-
max = (datamax != null ? datamax : opts.max);
|
1459
|
-
break;
|
1460
|
-
case "sliding-window":
|
1461
|
-
if (datamax > max) {
|
1462
|
-
// move the window to fit the new data,
|
1463
|
-
// keeping the axis range constant
|
1464
|
-
max = datamax;
|
1465
|
-
min = Math.max(datamax - (opts.windowSize || 100), min);
|
1466
|
-
}
|
1467
|
-
break;
|
1468
|
-
}
|
1469
|
-
|
1470
|
-
var widenedMinMax = widenMinMax(min, max);
|
1471
|
-
min = widenedMinMax.min;
|
1472
|
-
max = widenedMinMax.max;
|
1473
|
-
|
1474
|
-
// grow loose or grow exact supported
|
1475
|
-
if (opts.growOnly === true && opts.autoScale !== "none" && opts.autoScale !== "sliding-window") {
|
1476
|
-
min = (min < datamin) ? min : (datamin !== null ? datamin : min);
|
1477
|
-
max = (max > datamax) ? max : (datamax !== null ? datamax : max);
|
1478
|
-
}
|
1479
|
-
|
1480
|
-
axis.autoScaledMin = min;
|
1481
|
-
axis.autoScaledMax = max;
|
1482
|
-
}
|
1483
|
-
|
1484
|
-
function setRange(axis, autoScale) {
|
1485
|
-
var min = typeof axis.options.min === 'number' ? axis.options.min : axis.min,
|
1486
|
-
max = typeof axis.options.max === 'number' ? axis.options.max : axis.max,
|
1487
|
-
plotOffset = axis.options.offset;
|
1488
|
-
|
1489
|
-
if (autoScale) {
|
1490
|
-
autoScaleAxis(axis);
|
1491
|
-
min = axis.autoScaledMin;
|
1492
|
-
max = axis.autoScaledMax;
|
1493
|
-
}
|
1494
|
-
|
1495
|
-
min = (min != null ? min : -1) + (plotOffset.below || 0);
|
1496
|
-
max = (max != null ? max : 1) + (plotOffset.above || 0);
|
1497
|
-
|
1498
|
-
if (min > max) {
|
1499
|
-
var tmp = min;
|
1500
|
-
min = max;
|
1501
|
-
max = tmp;
|
1502
|
-
axis.options.offset = { above: 0, below: 0 };
|
1503
|
-
}
|
1504
|
-
|
1505
|
-
axis.min = $.plot.saturated.saturate(min);
|
1506
|
-
axis.max = $.plot.saturated.saturate(max);
|
1507
|
-
}
|
1508
|
-
|
1509
|
-
function computeValuePrecision (min, max, direction, ticks, tickDecimals) {
|
1510
|
-
var noTicks = fixupNumberOfTicks(direction, surface, ticks);
|
1511
|
-
|
1512
|
-
var delta = $.plot.saturated.delta(min, max, noTicks),
|
1513
|
-
dec = -Math.floor(Math.log(delta) / Math.LN10);
|
1514
|
-
|
1515
|
-
//if it is called with tickDecimals, then the precision should not be greather then that
|
1516
|
-
if (tickDecimals && dec > tickDecimals) {
|
1517
|
-
dec = tickDecimals;
|
1518
|
-
}
|
1519
|
-
|
1520
|
-
var magn = parseFloat('1e' + (-dec)),
|
1521
|
-
norm = delta / magn;
|
1522
|
-
|
1523
|
-
if (norm > 2.25 && norm < 3 && (dec + 1) <= tickDecimals) {
|
1524
|
-
//we need an extra decimals when tickSize is 2.5
|
1525
|
-
++dec;
|
1526
|
-
}
|
1527
|
-
|
1528
|
-
return isFinite(dec) ? dec : 0;
|
1529
|
-
};
|
1530
|
-
|
1531
|
-
function computeTickSize (min, max, noTicks, tickDecimals) {
|
1532
|
-
var delta = $.plot.saturated.delta(min, max, noTicks),
|
1533
|
-
dec = -Math.floor(Math.log(delta) / Math.LN10);
|
1534
|
-
|
1535
|
-
//if it is called with tickDecimals, then the precision should not be greather then that
|
1536
|
-
if (tickDecimals && dec > tickDecimals) {
|
1537
|
-
dec = tickDecimals;
|
1538
|
-
}
|
1539
|
-
|
1540
|
-
var magn = parseFloat('1e' + (-dec)),
|
1541
|
-
norm = delta / magn, // norm is between 1.0 and 10.0
|
1542
|
-
size;
|
1543
|
-
|
1544
|
-
if (norm < 1.5) {
|
1545
|
-
size = 1;
|
1546
|
-
} else if (norm < 3) {
|
1547
|
-
size = 2;
|
1548
|
-
if (norm > 2.25 && (tickDecimals == null || (dec + 1) <= tickDecimals)) {
|
1549
|
-
size = 2.5;
|
1550
|
-
}
|
1551
|
-
} else if (norm < 7.5) {
|
1552
|
-
size = 5;
|
1553
|
-
} else {
|
1554
|
-
size = 10;
|
1555
|
-
}
|
1556
|
-
|
1557
|
-
size *= magn;
|
1558
|
-
return size;
|
1559
|
-
}
|
1560
|
-
|
1561
|
-
function getAxisTickSize(min, max, direction, options, tickDecimals) {
|
1562
|
-
var noTicks;
|
1563
|
-
|
1564
|
-
if (typeof options.ticks === "number" && options.ticks > 0) {
|
1565
|
-
noTicks = options.ticks;
|
1566
|
-
} else {
|
1567
|
-
// heuristic based on the model a*sqrt(x) fitted to
|
1568
|
-
// some data points that seemed reasonable
|
1569
|
-
noTicks = 0.3 * Math.sqrt(direction === "x" ? surface.width : surface.height);
|
1570
|
-
}
|
1571
|
-
|
1572
|
-
var size = computeTickSize(min, max, noTicks, tickDecimals);
|
1573
|
-
|
1574
|
-
if (options.minTickSize != null && size < options.minTickSize) {
|
1575
|
-
size = options.minTickSize;
|
1576
|
-
}
|
1577
|
-
|
1578
|
-
return options.tickSize || size;
|
1579
|
-
};
|
1580
|
-
|
1581
|
-
function fixupNumberOfTicks(direction, surface, ticksOption) {
|
1582
|
-
var noTicks;
|
1583
|
-
|
1584
|
-
if (typeof ticksOption === "number" && ticksOption > 0) {
|
1585
|
-
noTicks = ticksOption;
|
1586
|
-
} else {
|
1587
|
-
noTicks = 0.3 * Math.sqrt(direction === "x" ? surface.width : surface.height);
|
1588
|
-
}
|
1589
|
-
|
1590
|
-
return noTicks;
|
1591
|
-
}
|
1592
|
-
|
1593
|
-
function setupTickFormatter(axis) {
|
1594
|
-
var opts = axis.options;
|
1595
|
-
if (!axis.tickFormatter) {
|
1596
|
-
if (typeof opts.tickFormatter === 'function') {
|
1597
|
-
axis.tickFormatter = function() {
|
1598
|
-
var args = Array.prototype.slice.call(arguments);
|
1599
|
-
return "" + opts.tickFormatter.apply(null, args);
|
1600
|
-
};
|
1601
|
-
} else {
|
1602
|
-
axis.tickFormatter = defaultTickFormatter;
|
1603
|
-
}
|
1604
|
-
}
|
1605
|
-
}
|
1606
|
-
|
1607
|
-
function setupTickGeneration(axis) {
|
1608
|
-
var opts = axis.options;
|
1609
|
-
var noTicks;
|
1610
|
-
|
1611
|
-
noTicks = fixupNumberOfTicks(axis.direction, surface, opts.ticks);
|
1612
|
-
|
1613
|
-
axis.delta = $.plot.saturated.delta(axis.min, axis.max, noTicks);
|
1614
|
-
var precision = plot.computeValuePrecision(axis.min, axis.max, axis.direction, noTicks, opts.tickDecimals);
|
1615
|
-
|
1616
|
-
axis.tickDecimals = Math.max(0, opts.tickDecimals != null ? opts.tickDecimals : precision);
|
1617
|
-
axis.tickSize = getAxisTickSize(axis.min, axis.max, axis.direction, opts, opts.tickDecimals);
|
1618
|
-
|
1619
|
-
// Flot supports base-10 axes; any other mode else is handled by a plug-in,
|
1620
|
-
// like flot.time.js.
|
1621
|
-
|
1622
|
-
if (!axis.tickGenerator) {
|
1623
|
-
if (typeof opts.tickGenerator === 'function') {
|
1624
|
-
axis.tickGenerator = opts.tickGenerator;
|
1625
|
-
} else {
|
1626
|
-
axis.tickGenerator = defaultTickGenerator;
|
1627
|
-
}
|
1628
|
-
}
|
1629
|
-
|
1630
|
-
if (opts.alignTicksWithAxis != null) {
|
1631
|
-
var otherAxis = (axis.direction === "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];
|
1632
|
-
if (otherAxis && otherAxis.used && otherAxis !== axis) {
|
1633
|
-
// consider snapping min/max to outermost nice ticks
|
1634
|
-
var niceTicks = axis.tickGenerator(axis, plot);
|
1635
|
-
if (niceTicks.length > 0) {
|
1636
|
-
if (opts.min == null) {
|
1637
|
-
axis.min = Math.min(axis.min, niceTicks[0]);
|
1638
|
-
}
|
1639
|
-
|
1640
|
-
if (opts.max == null && niceTicks.length > 1) {
|
1641
|
-
axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
|
1642
|
-
}
|
1643
|
-
}
|
1644
|
-
|
1645
|
-
axis.tickGenerator = function(axis) {
|
1646
|
-
// copy ticks, scaled to this axis
|
1647
|
-
var ticks = [],
|
1648
|
-
v, i;
|
1649
|
-
for (i = 0; i < otherAxis.ticks.length; ++i) {
|
1650
|
-
v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);
|
1651
|
-
v = axis.min + v * (axis.max - axis.min);
|
1652
|
-
ticks.push(v);
|
1653
|
-
}
|
1654
|
-
return ticks;
|
1655
|
-
};
|
1656
|
-
|
1657
|
-
// we might need an extra decimal since forced
|
1658
|
-
// ticks don't necessarily fit naturally
|
1659
|
-
if (!axis.mode && opts.tickDecimals == null) {
|
1660
|
-
var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1),
|
1661
|
-
ts = axis.tickGenerator(axis, plot);
|
1662
|
-
|
1663
|
-
// only proceed if the tick interval rounded
|
1664
|
-
// with an extra decimal doesn't give us a
|
1665
|
-
// zero at end
|
1666
|
-
if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) {
|
1667
|
-
axis.tickDecimals = extraDec;
|
1668
|
-
}
|
1669
|
-
}
|
1670
|
-
}
|
1671
|
-
}
|
1672
|
-
}
|
1673
|
-
|
1674
|
-
function setMajorTicks(axis) {
|
1675
|
-
var oticks = axis.options.ticks,
|
1676
|
-
ticks = [];
|
1677
|
-
if (oticks == null || (typeof oticks === "number" && oticks > 0)) {
|
1678
|
-
ticks = axis.tickGenerator(axis, plot);
|
1679
|
-
} else if (oticks) {
|
1680
|
-
if ($.isFunction(oticks)) {
|
1681
|
-
// generate the ticks
|
1682
|
-
ticks = oticks(axis);
|
1683
|
-
} else {
|
1684
|
-
ticks = oticks;
|
1685
|
-
}
|
1686
|
-
}
|
1687
|
-
|
1688
|
-
// clean up/labelify the supplied ticks, copy them over
|
1689
|
-
var i, v;
|
1690
|
-
axis.ticks = [];
|
1691
|
-
for (i = 0; i < ticks.length; ++i) {
|
1692
|
-
var label = null;
|
1693
|
-
var t = ticks[i];
|
1694
|
-
if (typeof t === "object") {
|
1695
|
-
v = +t[0];
|
1696
|
-
if (t.length > 1) {
|
1697
|
-
label = t[1];
|
1698
|
-
}
|
1699
|
-
} else {
|
1700
|
-
v = +t;
|
1701
|
-
}
|
1702
|
-
|
1703
|
-
if (!isNaN(v)) {
|
1704
|
-
axis.ticks.push(
|
1705
|
-
newTick(v, label, axis, 'major'));
|
1706
|
-
}
|
1707
|
-
}
|
1708
|
-
}
|
1709
|
-
|
1710
|
-
function newTick(v, label, axis, type) {
|
1711
|
-
if (label === null) {
|
1712
|
-
switch (type) {
|
1713
|
-
case 'min':
|
1714
|
-
case 'max':
|
1715
|
-
//improving the precision of endpoints
|
1716
|
-
var precision = getEndpointPrecision(v, axis);
|
1717
|
-
label = isFinite(precision) ? axis.tickFormatter(v, axis, precision, plot) : axis.tickFormatter(v, axis, precision, plot);
|
1718
|
-
break;
|
1719
|
-
case 'major':
|
1720
|
-
label = axis.tickFormatter(v, axis, undefined, plot);
|
1721
|
-
}
|
1722
|
-
}
|
1723
|
-
return {
|
1724
|
-
v: v,
|
1725
|
-
label: label
|
1726
|
-
};
|
1727
|
-
}
|
1728
|
-
|
1729
|
-
function snapRangeToTicks(axis, ticks, series) {
|
1730
|
-
var anyDataInSeries = function(series) {
|
1731
|
-
return series.some(e => e.datapoints.points.length > 0);
|
1732
|
-
}
|
1733
|
-
|
1734
|
-
if (axis.options.autoScale === "loose" && ticks.length > 0 && anyDataInSeries(series)) {
|
1735
|
-
// snap to ticks
|
1736
|
-
axis.min = Math.min(axis.min, ticks[0].v);
|
1737
|
-
axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
|
1738
|
-
}
|
1739
|
-
}
|
1740
|
-
|
1741
|
-
function getEndpointPrecision(value, axis) {
|
1742
|
-
var canvas1 = Math.floor(axis.p2c(value)),
|
1743
|
-
canvas2 = axis.direction === "x" ? canvas1 + 1 : canvas1 - 1,
|
1744
|
-
point1 = axis.c2p(canvas1),
|
1745
|
-
point2 = axis.c2p(canvas2),
|
1746
|
-
precision = computeValuePrecision(point1, point2, axis.direction, 1);
|
1747
|
-
|
1748
|
-
return precision;
|
1749
|
-
}
|
1750
|
-
|
1751
|
-
function setEndpointTicks(axis, series) {
|
1752
|
-
if (isValidEndpointTick(axis, series)) {
|
1753
|
-
axis.ticks.unshift(newTick(axis.min, null, axis, 'min'));
|
1754
|
-
axis.ticks.push(newTick(axis.max, null, axis, 'max'));
|
1755
|
-
}
|
1756
|
-
}
|
1757
|
-
|
1758
|
-
function isValidEndpointTick(axis, series) {
|
1759
|
-
if (axis.options.showTickLabels === 'endpoints') {
|
1760
|
-
return true;
|
1761
|
-
}
|
1762
|
-
if (axis.options.showTickLabels === 'all') {
|
1763
|
-
var associatedSeries = series.filter(function(s) {
|
1764
|
-
return s.bars.horizontal ? s.yaxis === axis : s.xaxis === axis;
|
1765
|
-
}),
|
1766
|
-
notAllBarSeries = associatedSeries.some(function(s) {
|
1767
|
-
return !s.bars.show;
|
1768
|
-
});
|
1769
|
-
return associatedSeries.length === 0 || notAllBarSeries;
|
1770
|
-
}
|
1771
|
-
if (axis.options.showTickLabels === 'major' || axis.options.showTickLabels === 'none') {
|
1772
|
-
return false;
|
1773
|
-
}
|
1774
|
-
}
|
1775
|
-
|
1776
|
-
function draw() {
|
1777
|
-
surface.clear();
|
1778
|
-
executeHooks(hooks.drawBackground, [ctx]);
|
1779
|
-
|
1780
|
-
var grid = options.grid;
|
1781
|
-
|
1782
|
-
// draw background, if any
|
1783
|
-
if (grid.show && grid.backgroundColor) {
|
1784
|
-
drawBackground();
|
1785
|
-
}
|
1786
|
-
|
1787
|
-
if (grid.show && !grid.aboveData) {
|
1788
|
-
drawGrid();
|
1789
|
-
}
|
1790
|
-
|
1791
|
-
for (var i = 0; i < series.length; ++i) {
|
1792
|
-
executeHooks(hooks.drawSeries, [ctx, series[i], i, getColorOrGradient]);
|
1793
|
-
drawSeries(series[i]);
|
1794
|
-
}
|
1795
|
-
|
1796
|
-
executeHooks(hooks.draw, [ctx]);
|
1797
|
-
|
1798
|
-
if (grid.show && grid.aboveData) {
|
1799
|
-
drawGrid();
|
1800
|
-
}
|
1801
|
-
|
1802
|
-
surface.render();
|
1803
|
-
|
1804
|
-
// A draw implies that either the axes or data have changed, so we
|
1805
|
-
// should probably update the overlay highlights as well.
|
1806
|
-
triggerRedrawOverlay();
|
1807
|
-
}
|
1808
|
-
|
1809
|
-
function extractRange(ranges, coord) {
|
1810
|
-
var axis, from, to, key, axes = allAxes();
|
1811
|
-
|
1812
|
-
for (var i = 0; i < axes.length; ++i) {
|
1813
|
-
axis = axes[i];
|
1814
|
-
if (axis.direction === coord) {
|
1815
|
-
key = coord + axis.n + "axis";
|
1816
|
-
if (!ranges[key] && axis.n === 1) {
|
1817
|
-
// support x1axis as xaxis
|
1818
|
-
key = coord + "axis";
|
1819
|
-
}
|
1820
|
-
|
1821
|
-
if (ranges[key]) {
|
1822
|
-
from = ranges[key].from;
|
1823
|
-
to = ranges[key].to;
|
1824
|
-
break;
|
1825
|
-
}
|
1826
|
-
}
|
1827
|
-
}
|
1828
|
-
|
1829
|
-
// backwards-compat stuff - to be removed in future
|
1830
|
-
if (!ranges[key]) {
|
1831
|
-
axis = coord === "x" ? xaxes[0] : yaxes[0];
|
1832
|
-
from = ranges[coord + "1"];
|
1833
|
-
to = ranges[coord + "2"];
|
1834
|
-
}
|
1835
|
-
|
1836
|
-
// auto-reverse as an added bonus
|
1837
|
-
if (from != null && to != null && from > to) {
|
1838
|
-
var tmp = from;
|
1839
|
-
from = to;
|
1840
|
-
to = tmp;
|
1841
|
-
}
|
1842
|
-
|
1843
|
-
return {
|
1844
|
-
from: from,
|
1845
|
-
to: to,
|
1846
|
-
axis: axis
|
1847
|
-
};
|
1848
|
-
}
|
1849
|
-
|
1850
|
-
function drawBackground() {
|
1851
|
-
ctx.save();
|
1852
|
-
ctx.translate(plotOffset.left, plotOffset.top);
|
1853
|
-
|
1854
|
-
ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
|
1855
|
-
ctx.fillRect(0, 0, plotWidth, plotHeight);
|
1856
|
-
ctx.restore();
|
1857
|
-
}
|
1858
|
-
|
1859
|
-
function drawMarkings() {
|
1860
|
-
// draw markings
|
1861
|
-
var markings = options.grid.markings,
|
1862
|
-
axes;
|
1863
|
-
|
1864
|
-
if (markings) {
|
1865
|
-
if ($.isFunction(markings)) {
|
1866
|
-
axes = plot.getAxes();
|
1867
|
-
// xmin etc. is backwards compatibility, to be
|
1868
|
-
// removed in the future
|
1869
|
-
axes.xmin = axes.xaxis.min;
|
1870
|
-
axes.xmax = axes.xaxis.max;
|
1871
|
-
axes.ymin = axes.yaxis.min;
|
1872
|
-
axes.ymax = axes.yaxis.max;
|
1873
|
-
|
1874
|
-
markings = markings(axes);
|
1875
|
-
}
|
1876
|
-
|
1877
|
-
var i;
|
1878
|
-
for (i = 0; i < markings.length; ++i) {
|
1879
|
-
var m = markings[i],
|
1880
|
-
xrange = extractRange(m, "x"),
|
1881
|
-
yrange = extractRange(m, "y");
|
1882
|
-
|
1883
|
-
// fill in missing
|
1884
|
-
if (xrange.from == null) {
|
1885
|
-
xrange.from = xrange.axis.min;
|
1886
|
-
}
|
1887
|
-
|
1888
|
-
if (xrange.to == null) {
|
1889
|
-
xrange.to = xrange.axis.max;
|
1890
|
-
}
|
1891
|
-
|
1892
|
-
if (yrange.from == null) {
|
1893
|
-
yrange.from = yrange.axis.min;
|
1894
|
-
}
|
1895
|
-
|
1896
|
-
if (yrange.to == null) {
|
1897
|
-
yrange.to = yrange.axis.max;
|
1898
|
-
}
|
1899
|
-
|
1900
|
-
// clip
|
1901
|
-
if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
|
1902
|
-
yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) {
|
1903
|
-
continue;
|
1904
|
-
}
|
1905
|
-
|
1906
|
-
xrange.from = Math.max(xrange.from, xrange.axis.min);
|
1907
|
-
xrange.to = Math.min(xrange.to, xrange.axis.max);
|
1908
|
-
yrange.from = Math.max(yrange.from, yrange.axis.min);
|
1909
|
-
yrange.to = Math.min(yrange.to, yrange.axis.max);
|
1910
|
-
|
1911
|
-
var xequal = xrange.from === xrange.to,
|
1912
|
-
yequal = yrange.from === yrange.to;
|
1913
|
-
|
1914
|
-
if (xequal && yequal) {
|
1915
|
-
continue;
|
1916
|
-
}
|
1917
|
-
|
1918
|
-
// then draw
|
1919
|
-
xrange.from = Math.floor(xrange.axis.p2c(xrange.from));
|
1920
|
-
xrange.to = Math.floor(xrange.axis.p2c(xrange.to));
|
1921
|
-
yrange.from = Math.floor(yrange.axis.p2c(yrange.from));
|
1922
|
-
yrange.to = Math.floor(yrange.axis.p2c(yrange.to));
|
1923
|
-
|
1924
|
-
if (xequal || yequal) {
|
1925
|
-
var lineWidth = m.lineWidth || options.grid.markingsLineWidth,
|
1926
|
-
subPixel = lineWidth % 2 ? 0.5 : 0;
|
1927
|
-
ctx.beginPath();
|
1928
|
-
ctx.strokeStyle = m.color || options.grid.markingsColor;
|
1929
|
-
ctx.lineWidth = lineWidth;
|
1930
|
-
if (xequal) {
|
1931
|
-
ctx.moveTo(xrange.to + subPixel, yrange.from);
|
1932
|
-
ctx.lineTo(xrange.to + subPixel, yrange.to);
|
1933
|
-
} else {
|
1934
|
-
ctx.moveTo(xrange.from, yrange.to + subPixel);
|
1935
|
-
ctx.lineTo(xrange.to, yrange.to + subPixel);
|
1936
|
-
}
|
1937
|
-
ctx.stroke();
|
1938
|
-
} else {
|
1939
|
-
ctx.fillStyle = m.color || options.grid.markingsColor;
|
1940
|
-
ctx.fillRect(xrange.from, yrange.to,
|
1941
|
-
xrange.to - xrange.from,
|
1942
|
-
yrange.from - yrange.to);
|
1943
|
-
}
|
1944
|
-
}
|
1945
|
-
}
|
1946
|
-
}
|
1947
|
-
|
1948
|
-
function findEdges(axis) {
|
1949
|
-
var box = axis.box,
|
1950
|
-
x = 0,
|
1951
|
-
y = 0;
|
1952
|
-
|
1953
|
-
// find the edges
|
1954
|
-
if (axis.direction === "x") {
|
1955
|
-
x = 0;
|
1956
|
-
y = box.top - plotOffset.top + (axis.position === "top" ? box.height : 0);
|
1957
|
-
} else {
|
1958
|
-
y = 0;
|
1959
|
-
x = box.left - plotOffset.left + (axis.position === "left" ? box.width : 0) + axis.boxPosition.centerX;
|
1960
|
-
}
|
1961
|
-
|
1962
|
-
return {
|
1963
|
-
x: x,
|
1964
|
-
y: y
|
1965
|
-
};
|
1966
|
-
};
|
1967
|
-
|
1968
|
-
function alignPosition(lineWidth, pos) {
|
1969
|
-
return ((lineWidth % 2) !== 0) ? Math.floor(pos) + 0.5 : pos;
|
1970
|
-
};
|
1971
|
-
|
1972
|
-
function drawTickBar(axis) {
|
1973
|
-
ctx.lineWidth = 1;
|
1974
|
-
var edges = findEdges(axis),
|
1975
|
-
x = edges.x,
|
1976
|
-
y = edges.y;
|
1977
|
-
|
1978
|
-
// draw tick bar
|
1979
|
-
if (axis.show) {
|
1980
|
-
var xoff = 0,
|
1981
|
-
yoff = 0;
|
1982
|
-
|
1983
|
-
ctx.strokeStyle = axis.options.color;
|
1984
|
-
ctx.beginPath();
|
1985
|
-
if (axis.direction === "x") {
|
1986
|
-
xoff = plotWidth + 1;
|
1987
|
-
} else {
|
1988
|
-
yoff = plotHeight + 1;
|
1989
|
-
}
|
1990
|
-
|
1991
|
-
if (axis.direction === "x") {
|
1992
|
-
y = alignPosition(ctx.lineWidth, y);
|
1993
|
-
} else {
|
1994
|
-
x = alignPosition(ctx.lineWidth, x);
|
1995
|
-
}
|
1996
|
-
|
1997
|
-
ctx.moveTo(x, y);
|
1998
|
-
ctx.lineTo(x + xoff, y + yoff);
|
1999
|
-
ctx.stroke();
|
2000
|
-
}
|
2001
|
-
};
|
2002
|
-
|
2003
|
-
function drawTickMarks(axis) {
|
2004
|
-
var t = axis.tickLength,
|
2005
|
-
minorTicks = axis.showMinorTicks,
|
2006
|
-
minorTicksNr = MINOR_TICKS_COUNT_CONSTANT,
|
2007
|
-
edges = findEdges(axis),
|
2008
|
-
x = edges.x,
|
2009
|
-
y = edges.y,
|
2010
|
-
i = 0;
|
2011
|
-
|
2012
|
-
// draw major tick marks
|
2013
|
-
ctx.strokeStyle = axis.options.color;
|
2014
|
-
ctx.beginPath();
|
2015
|
-
|
2016
|
-
for (i = 0; i < axis.ticks.length; ++i) {
|
2017
|
-
var v = axis.ticks[i].v,
|
2018
|
-
xoff = 0,
|
2019
|
-
yoff = 0,
|
2020
|
-
xminor = 0,
|
2021
|
-
yminor = 0,
|
2022
|
-
j;
|
2023
|
-
|
2024
|
-
if (!isNaN(v) && v >= axis.min && v <= axis.max) {
|
2025
|
-
if (axis.direction === "x") {
|
2026
|
-
x = axis.p2c(v);
|
2027
|
-
yoff = t;
|
2028
|
-
|
2029
|
-
if (axis.position === "top") {
|
2030
|
-
yoff = -yoff;
|
2031
|
-
}
|
2032
|
-
} else {
|
2033
|
-
y = axis.p2c(v);
|
2034
|
-
xoff = t;
|
2035
|
-
|
2036
|
-
if (axis.position === "left") {
|
2037
|
-
xoff = -xoff;
|
2038
|
-
}
|
2039
|
-
}
|
2040
|
-
|
2041
|
-
if (axis.direction === "x") {
|
2042
|
-
x = alignPosition(ctx.lineWidth, x);
|
2043
|
-
} else {
|
2044
|
-
y = alignPosition(ctx.lineWidth, y);
|
2045
|
-
}
|
2046
|
-
|
2047
|
-
ctx.moveTo(x, y);
|
2048
|
-
ctx.lineTo(x + xoff, y + yoff);
|
2049
|
-
}
|
2050
|
-
|
2051
|
-
//draw minor tick marks
|
2052
|
-
if (minorTicks === true && i < axis.ticks.length - 1) {
|
2053
|
-
var v1 = axis.ticks[i].v,
|
2054
|
-
v2 = axis.ticks[i + 1].v,
|
2055
|
-
step = (v2 - v1) / (minorTicksNr + 1);
|
2056
|
-
|
2057
|
-
for (j = 1; j <= minorTicksNr; j++) {
|
2058
|
-
// compute minor tick position
|
2059
|
-
if (axis.direction === "x") {
|
2060
|
-
yminor = t / 2; // minor ticks are half length
|
2061
|
-
x = alignPosition(ctx.lineWidth, axis.p2c(v1 + j * step))
|
2062
|
-
|
2063
|
-
if (axis.position === "top") {
|
2064
|
-
yminor = -yminor;
|
2065
|
-
}
|
2066
|
-
|
2067
|
-
// don't go over the plot borders
|
2068
|
-
if ((x < 0) || (x > plotWidth)) {
|
2069
|
-
continue;
|
2070
|
-
}
|
2071
|
-
} else {
|
2072
|
-
xminor = t / 2; // minor ticks are half length
|
2073
|
-
y = alignPosition(ctx.lineWidth, axis.p2c(v1 + j * step));
|
2074
|
-
|
2075
|
-
if (axis.position === "left") {
|
2076
|
-
xminor = -xminor;
|
2077
|
-
}
|
2078
|
-
|
2079
|
-
// don't go over the plot borders
|
2080
|
-
if ((y < 0) || (y > plotHeight)) {
|
2081
|
-
continue;
|
2082
|
-
}
|
2083
|
-
}
|
2084
|
-
|
2085
|
-
ctx.moveTo(x, y);
|
2086
|
-
ctx.lineTo(x + xminor, y + yminor);
|
2087
|
-
}
|
2088
|
-
}
|
2089
|
-
}
|
2090
|
-
|
2091
|
-
ctx.stroke();
|
2092
|
-
};
|
2093
|
-
|
2094
|
-
function drawGridLines(axis) {
|
2095
|
-
// check if the line will be overlapped with a border
|
2096
|
-
var overlappedWithBorder = function (value) {
|
2097
|
-
var bw = options.grid.borderWidth;
|
2098
|
-
return (((typeof bw === "object" && bw[axis.position] > 0) || bw > 0) && (value === axis.min || value === axis.max));
|
2099
|
-
};
|
2100
|
-
|
2101
|
-
ctx.strokeStyle = options.grid.tickColor;
|
2102
|
-
ctx.beginPath();
|
2103
|
-
var i;
|
2104
|
-
for (i = 0; i < axis.ticks.length; ++i) {
|
2105
|
-
var v = axis.ticks[i].v,
|
2106
|
-
xoff = 0,
|
2107
|
-
yoff = 0,
|
2108
|
-
x = 0,
|
2109
|
-
y = 0;
|
2110
|
-
|
2111
|
-
if (isNaN(v) || v < axis.min || v > axis.max) continue;
|
2112
|
-
|
2113
|
-
// skip those lying on the axes if we got a border
|
2114
|
-
if (overlappedWithBorder(v)) continue;
|
2115
|
-
|
2116
|
-
if (axis.direction === "x") {
|
2117
|
-
x = axis.p2c(v);
|
2118
|
-
y = plotHeight;
|
2119
|
-
yoff = -plotHeight;
|
2120
|
-
} else {
|
2121
|
-
x = 0;
|
2122
|
-
y = axis.p2c(v);
|
2123
|
-
xoff = plotWidth;
|
2124
|
-
}
|
2125
|
-
|
2126
|
-
if (axis.direction === "x") {
|
2127
|
-
x = alignPosition(ctx.lineWidth, x);
|
2128
|
-
} else {
|
2129
|
-
y = alignPosition(ctx.lineWidth, y);
|
2130
|
-
}
|
2131
|
-
|
2132
|
-
ctx.moveTo(x, y);
|
2133
|
-
ctx.lineTo(x + xoff, y + yoff);
|
2134
|
-
}
|
2135
|
-
|
2136
|
-
ctx.stroke();
|
2137
|
-
};
|
2138
|
-
|
2139
|
-
function drawBorder() {
|
2140
|
-
// If either borderWidth or borderColor is an object, then draw the border
|
2141
|
-
// line by line instead of as one rectangle
|
2142
|
-
var bw = options.grid.borderWidth,
|
2143
|
-
bc = options.grid.borderColor;
|
2144
|
-
|
2145
|
-
if (typeof bw === "object" || typeof bc === "object") {
|
2146
|
-
if (typeof bw !== "object") {
|
2147
|
-
bw = {
|
2148
|
-
top: bw,
|
2149
|
-
right: bw,
|
2150
|
-
bottom: bw,
|
2151
|
-
left: bw
|
2152
|
-
};
|
2153
|
-
}
|
2154
|
-
if (typeof bc !== "object") {
|
2155
|
-
bc = {
|
2156
|
-
top: bc,
|
2157
|
-
right: bc,
|
2158
|
-
bottom: bc,
|
2159
|
-
left: bc
|
2160
|
-
};
|
2161
|
-
}
|
2162
|
-
|
2163
|
-
if (bw.top > 0) {
|
2164
|
-
ctx.strokeStyle = bc.top;
|
2165
|
-
ctx.lineWidth = bw.top;
|
2166
|
-
ctx.beginPath();
|
2167
|
-
ctx.moveTo(0 - bw.left, 0 - bw.top / 2);
|
2168
|
-
ctx.lineTo(plotWidth, 0 - bw.top / 2);
|
2169
|
-
ctx.stroke();
|
2170
|
-
}
|
2171
|
-
|
2172
|
-
if (bw.right > 0) {
|
2173
|
-
ctx.strokeStyle = bc.right;
|
2174
|
-
ctx.lineWidth = bw.right;
|
2175
|
-
ctx.beginPath();
|
2176
|
-
ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top);
|
2177
|
-
ctx.lineTo(plotWidth + bw.right / 2, plotHeight);
|
2178
|
-
ctx.stroke();
|
2179
|
-
}
|
2180
|
-
|
2181
|
-
if (bw.bottom > 0) {
|
2182
|
-
ctx.strokeStyle = bc.bottom;
|
2183
|
-
ctx.lineWidth = bw.bottom;
|
2184
|
-
ctx.beginPath();
|
2185
|
-
ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2);
|
2186
|
-
ctx.lineTo(0, plotHeight + bw.bottom / 2);
|
2187
|
-
ctx.stroke();
|
2188
|
-
}
|
2189
|
-
|
2190
|
-
if (bw.left > 0) {
|
2191
|
-
ctx.strokeStyle = bc.left;
|
2192
|
-
ctx.lineWidth = bw.left;
|
2193
|
-
ctx.beginPath();
|
2194
|
-
ctx.moveTo(0 - bw.left / 2, plotHeight + bw.bottom);
|
2195
|
-
ctx.lineTo(0 - bw.left / 2, 0);
|
2196
|
-
ctx.stroke();
|
2197
|
-
}
|
2198
|
-
} else {
|
2199
|
-
ctx.lineWidth = bw;
|
2200
|
-
ctx.strokeStyle = options.grid.borderColor;
|
2201
|
-
ctx.strokeRect(-bw / 2, -bw / 2, plotWidth + bw, plotHeight + bw);
|
2202
|
-
}
|
2203
|
-
};
|
2204
|
-
|
2205
|
-
function drawGrid() {
|
2206
|
-
var axes, bw;
|
2207
|
-
|
2208
|
-
ctx.save();
|
2209
|
-
ctx.translate(plotOffset.left, plotOffset.top);
|
2210
|
-
|
2211
|
-
drawMarkings();
|
2212
|
-
|
2213
|
-
axes = allAxes();
|
2214
|
-
bw = options.grid.borderWidth;
|
2215
|
-
|
2216
|
-
for (var j = 0; j < axes.length; ++j) {
|
2217
|
-
var axis = axes[j];
|
2218
|
-
|
2219
|
-
if (!axis.show) {
|
2220
|
-
continue;
|
2221
|
-
}
|
2222
|
-
|
2223
|
-
drawTickBar(axis);
|
2224
|
-
if (axis.showTicks === true) {
|
2225
|
-
drawTickMarks(axis);
|
2226
|
-
}
|
2227
|
-
|
2228
|
-
if (axis.gridLines === true) {
|
2229
|
-
drawGridLines(axis, bw);
|
2230
|
-
}
|
2231
|
-
}
|
2232
|
-
|
2233
|
-
// draw border
|
2234
|
-
if (bw) {
|
2235
|
-
drawBorder();
|
2236
|
-
}
|
2237
|
-
|
2238
|
-
ctx.restore();
|
2239
|
-
}
|
2240
|
-
|
2241
|
-
function drawAxisLabels() {
|
2242
|
-
$.each(allAxes(), function(_, axis) {
|
2243
|
-
var box = axis.box,
|
2244
|
-
legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
|
2245
|
-
layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
|
2246
|
-
font = axis.options.font || "flot-tick-label tickLabel",
|
2247
|
-
i, x, y, halign, valign, info,
|
2248
|
-
margin = 3,
|
2249
|
-
nullBox = {x: NaN, y: NaN, width: NaN, height: NaN}, newLabelBox, labelBoxes = [],
|
2250
|
-
overlapping = function(x11, y11, x12, y12, x21, y21, x22, y22) {
|
2251
|
-
return ((x11 <= x21 && x21 <= x12) || (x21 <= x11 && x11 <= x22)) &&
|
2252
|
-
((y11 <= y21 && y21 <= y12) || (y21 <= y11 && y11 <= y22));
|
2253
|
-
},
|
2254
|
-
overlapsOtherLabels = function(newLabelBox, previousLabelBoxes) {
|
2255
|
-
return previousLabelBoxes.some(function(labelBox) {
|
2256
|
-
return overlapping(
|
2257
|
-
newLabelBox.x, newLabelBox.y, newLabelBox.x + newLabelBox.width, newLabelBox.y + newLabelBox.height,
|
2258
|
-
labelBox.x, labelBox.y, labelBox.x + labelBox.width, labelBox.y + labelBox.height);
|
2259
|
-
});
|
2260
|
-
},
|
2261
|
-
drawAxisLabel = function (tick, labelBoxes) {
|
2262
|
-
if (!tick || !tick.label || tick.v < axis.min || tick.v > axis.max) {
|
2263
|
-
return nullBox;
|
2264
|
-
}
|
2265
|
-
|
2266
|
-
info = surface.getTextInfo(layer, tick.label, font);
|
2267
|
-
|
2268
|
-
if (axis.direction === "x") {
|
2269
|
-
halign = "center";
|
2270
|
-
x = plotOffset.left + axis.p2c(tick.v);
|
2271
|
-
if (axis.position === "bottom") {
|
2272
|
-
y = box.top + box.padding - axis.boxPosition.centerY;
|
2273
|
-
} else {
|
2274
|
-
y = box.top + box.height - box.padding + axis.boxPosition.centerY;
|
2275
|
-
valign = "bottom";
|
2276
|
-
}
|
2277
|
-
newLabelBox = {x: x - info.width / 2 - margin, y: y - margin, width: info.width + 2 * margin, height: info.height + 2 * margin};
|
2278
|
-
} else {
|
2279
|
-
valign = "middle";
|
2280
|
-
y = plotOffset.top + axis.p2c(tick.v);
|
2281
|
-
if (axis.position === "left") {
|
2282
|
-
x = box.left + box.width - box.padding - axis.boxPosition.centerX;
|
2283
|
-
halign = "right";
|
2284
|
-
} else {
|
2285
|
-
x = box.left + box.padding + axis.boxPosition.centerX;
|
2286
|
-
}
|
2287
|
-
newLabelBox = {x: x - info.width / 2 - margin, y: y - margin, width: info.width + 2 * margin, height: info.height + 2 * margin};
|
2288
|
-
}
|
2289
|
-
|
2290
|
-
if (overlapsOtherLabels(newLabelBox, labelBoxes)) {
|
2291
|
-
return nullBox;
|
2292
|
-
}
|
2293
|
-
|
2294
|
-
surface.addText(layer, x, y, tick.label, font, null, null, halign, valign);
|
2295
|
-
|
2296
|
-
return newLabelBox;
|
2297
|
-
};
|
2298
|
-
|
2299
|
-
// Remove text before checking for axis.show and ticks.length;
|
2300
|
-
// otherwise plugins, like flot-tickrotor, that draw their own
|
2301
|
-
// tick labels will end up with both theirs and the defaults.
|
2302
|
-
|
2303
|
-
surface.removeText(layer);
|
2304
|
-
|
2305
|
-
executeHooks(hooks.drawAxis, [axis, surface]);
|
2306
|
-
|
2307
|
-
if (!axis.show) {
|
2308
|
-
return;
|
2309
|
-
}
|
2310
|
-
|
2311
|
-
switch (axis.options.showTickLabels) {
|
2312
|
-
case 'none':
|
2313
|
-
break;
|
2314
|
-
case 'endpoints':
|
2315
|
-
labelBoxes.push(drawAxisLabel(axis.ticks[0], labelBoxes));
|
2316
|
-
labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes));
|
2317
|
-
break;
|
2318
|
-
case 'major':
|
2319
|
-
labelBoxes.push(drawAxisLabel(axis.ticks[0], labelBoxes));
|
2320
|
-
labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes));
|
2321
|
-
for (i = 1; i < axis.ticks.length - 1; ++i) {
|
2322
|
-
labelBoxes.push(drawAxisLabel(axis.ticks[i], labelBoxes));
|
2323
|
-
}
|
2324
|
-
break;
|
2325
|
-
case 'all':
|
2326
|
-
labelBoxes.push(drawAxisLabel(axis.ticks[0], []));
|
2327
|
-
labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes));
|
2328
|
-
for (i = 1; i < axis.ticks.length - 1; ++i) {
|
2329
|
-
labelBoxes.push(drawAxisLabel(axis.ticks[i], labelBoxes));
|
2330
|
-
}
|
2331
|
-
break;
|
2332
|
-
}
|
2333
|
-
});
|
2334
|
-
}
|
2335
|
-
|
2336
|
-
function drawSeries(series) {
|
2337
|
-
if (series.lines.show) {
|
2338
|
-
$.plot.drawSeries.drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient);
|
2339
|
-
}
|
2340
|
-
|
2341
|
-
if (series.bars.show) {
|
2342
|
-
$.plot.drawSeries.drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient);
|
2343
|
-
}
|
2344
|
-
|
2345
|
-
if (series.points.show) {
|
2346
|
-
$.plot.drawSeries.drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient);
|
2347
|
-
}
|
2348
|
-
}
|
2349
|
-
|
2350
|
-
function computeRangeForDataSeries(series, force, isValid) {
|
2351
|
-
var points = series.datapoints.points,
|
2352
|
-
ps = series.datapoints.pointsize,
|
2353
|
-
format = series.datapoints.format,
|
2354
|
-
topSentry = Number.POSITIVE_INFINITY,
|
2355
|
-
bottomSentry = Number.NEGATIVE_INFINITY,
|
2356
|
-
range = {
|
2357
|
-
xmin: topSentry,
|
2358
|
-
ymin: topSentry,
|
2359
|
-
xmax: bottomSentry,
|
2360
|
-
ymax: bottomSentry
|
2361
|
-
};
|
2362
|
-
|
2363
|
-
for (var j = 0; j < points.length; j += ps) {
|
2364
|
-
if (points[j] === null) {
|
2365
|
-
continue;
|
2366
|
-
}
|
2367
|
-
|
2368
|
-
if (typeof (isValid) === 'function' && !isValid(points[j])) {
|
2369
|
-
continue;
|
2370
|
-
}
|
2371
|
-
|
2372
|
-
for (var m = 0; m < ps; ++m) {
|
2373
|
-
var val = points[j + m],
|
2374
|
-
f = format[m];
|
2375
|
-
if (f === null || f === undefined) {
|
2376
|
-
continue;
|
2377
|
-
}
|
2378
|
-
|
2379
|
-
if (typeof (isValid) === 'function' && !isValid(val)) {
|
2380
|
-
continue;
|
2381
|
-
}
|
2382
|
-
|
2383
|
-
if ((!force && !f.computeRange) || val === Infinity || val === -Infinity) {
|
2384
|
-
continue;
|
2385
|
-
}
|
2386
|
-
|
2387
|
-
if (f.x === true) {
|
2388
|
-
if (val < range.xmin) {
|
2389
|
-
range.xmin = val;
|
2390
|
-
}
|
2391
|
-
|
2392
|
-
if (val > range.xmax) {
|
2393
|
-
range.xmax = val;
|
2394
|
-
}
|
2395
|
-
}
|
2396
|
-
|
2397
|
-
if (f.y === true) {
|
2398
|
-
if (val < range.ymin) {
|
2399
|
-
range.ymin = val;
|
2400
|
-
}
|
2401
|
-
|
2402
|
-
if (val > range.ymax) {
|
2403
|
-
range.ymax = val;
|
2404
|
-
}
|
2405
|
-
}
|
2406
|
-
}
|
2407
|
-
}
|
2408
|
-
|
2409
|
-
return range;
|
2410
|
-
};
|
2411
|
-
|
2412
|
-
function adjustSeriesDataRange(series, range) {
|
2413
|
-
if (series.bars.show) {
|
2414
|
-
// make sure we got room for the bar on the dancing floor
|
2415
|
-
var delta;
|
2416
|
-
|
2417
|
-
// update bar width if needed
|
2418
|
-
var useAbsoluteBarWidth = series.bars.barWidth[1];
|
2419
|
-
if (series.datapoints && series.datapoints.points && !useAbsoluteBarWidth) {
|
2420
|
-
computeBarWidth(series);
|
2421
|
-
}
|
2422
|
-
|
2423
|
-
var barWidth = series.bars.barWidth[0] || series.bars.barWidth;
|
2424
|
-
switch (series.bars.align) {
|
2425
|
-
case "left":
|
2426
|
-
delta = 0;
|
2427
|
-
break;
|
2428
|
-
case "right":
|
2429
|
-
delta = -barWidth;
|
2430
|
-
break;
|
2431
|
-
default:
|
2432
|
-
delta = -barWidth / 2;
|
2433
|
-
}
|
2434
|
-
|
2435
|
-
if (series.bars.horizontal) {
|
2436
|
-
range.ymin += delta;
|
2437
|
-
range.ymax += delta + barWidth;
|
2438
|
-
} else {
|
2439
|
-
range.xmin += delta;
|
2440
|
-
range.xmax += delta + barWidth;
|
2441
|
-
}
|
2442
|
-
}
|
2443
|
-
|
2444
|
-
if ((series.bars.show && series.bars.zero) || (series.lines.show && series.lines.zero)) {
|
2445
|
-
var ps = series.datapoints.pointsize;
|
2446
|
-
|
2447
|
-
// make sure the 0 point is included in the computed y range when requested
|
2448
|
-
if (ps <= 2) {
|
2449
|
-
/*if ps > 0 the points were already taken into account for autoScale */
|
2450
|
-
range.ymin = Math.min(0, range.ymin);
|
2451
|
-
range.ymax = Math.max(0, range.ymax);
|
2452
|
-
}
|
2453
|
-
}
|
2454
|
-
|
2455
|
-
return range;
|
2456
|
-
};
|
2457
|
-
|
2458
|
-
function computeBarWidth(series) {
|
2459
|
-
var xValues = [];
|
2460
|
-
var pointsize = series.datapoints.pointsize, minDistance = Number.MAX_VALUE;
|
2461
|
-
|
2462
|
-
if (series.datapoints.points.length <= pointsize) {
|
2463
|
-
minDistance = 1;
|
2464
|
-
}
|
2465
|
-
|
2466
|
-
var start = series.bars.horizontal ? 1 : 0;
|
2467
|
-
for (let j = start; j < series.datapoints.points.length; j += pointsize) {
|
2468
|
-
if (isFinite(series.datapoints.points[j]) && series.datapoints.points[j] !== null) {
|
2469
|
-
xValues.push(series.datapoints.points[j]);
|
2470
|
-
}
|
2471
|
-
}
|
2472
|
-
|
2473
|
-
function onlyUnique(value, index, self) {
|
2474
|
-
return self.indexOf(value) === index;
|
2475
|
-
}
|
2476
|
-
|
2477
|
-
xValues = xValues.filter(onlyUnique);
|
2478
|
-
xValues.sort(function(a, b) { return a - b });
|
2479
|
-
|
2480
|
-
for (let j = 1; j < xValues.length; j++) {
|
2481
|
-
var distance = Math.abs(xValues[j] - xValues[j - 1]);
|
2482
|
-
if (distance < minDistance && isFinite(distance)) {
|
2483
|
-
minDistance = distance;
|
2484
|
-
}
|
2485
|
-
}
|
2486
|
-
|
2487
|
-
if (typeof series.bars.barWidth === "number") {
|
2488
|
-
series.bars.barWidth = series.bars.barWidth * minDistance;
|
2489
|
-
} else {
|
2490
|
-
series.bars.barWidth[0] = series.bars.barWidth[0] * minDistance;
|
2491
|
-
}
|
2492
|
-
}
|
2493
|
-
|
2494
|
-
function findNearbyItems(mouseX, mouseY, seriesFilter, radius, computeDistance) {
|
2495
|
-
var items = findItems(mouseX, mouseY, seriesFilter, radius, computeDistance);
|
2496
|
-
for (var i = 0; i < series.length; ++i) {
|
2497
|
-
if (seriesFilter(i)) {
|
2498
|
-
executeHooks(hooks.findNearbyItems, [mouseX, mouseY, series, i, radius, computeDistance, items]);
|
2499
|
-
}
|
2500
|
-
}
|
2501
|
-
|
2502
|
-
return items.sort((a, b) => {
|
2503
|
-
if (b.distance === undefined) {
|
2504
|
-
return -1;
|
2505
|
-
} else if (a.distance === undefined && b.distance !== undefined) {
|
2506
|
-
return 1;
|
2507
|
-
}
|
2508
|
-
|
2509
|
-
return a.distance - b.distance;
|
2510
|
-
});
|
2511
|
-
}
|
2512
|
-
|
2513
|
-
function findNearbyItem(mouseX, mouseY, seriesFilter, radius, computeDistance) {
|
2514
|
-
var items = findNearbyItems(mouseX, mouseY, seriesFilter, radius, computeDistance);
|
2515
|
-
return items[0] !== undefined ? items[0] : null;
|
2516
|
-
}
|
2517
|
-
|
2518
|
-
// returns the data item the mouse is over/ the cursor is closest to, or null if none is found
|
2519
|
-
function findItems(mouseX, mouseY, seriesFilter, radius, computeDistance) {
|
2520
|
-
var i, foundItems = [],
|
2521
|
-
items = [],
|
2522
|
-
smallestDistance = radius * radius + 1;
|
2523
|
-
|
2524
|
-
for (i = series.length - 1; i >= 0; --i) {
|
2525
|
-
if (!seriesFilter(i)) continue;
|
2526
|
-
|
2527
|
-
var s = series[i];
|
2528
|
-
if (!s.datapoints) return;
|
2529
|
-
|
2530
|
-
var foundPoint = false;
|
2531
|
-
if (s.lines.show || s.points.show) {
|
2532
|
-
var found = findNearbyPoint(s, mouseX, mouseY, radius, computeDistance);
|
2533
|
-
if (found) {
|
2534
|
-
items.push({ seriesIndex: i, dataIndex: found.dataIndex, distance: found.distance });
|
2535
|
-
foundPoint = true;
|
2536
|
-
}
|
2537
|
-
}
|
2538
|
-
|
2539
|
-
if (s.bars.show && !foundPoint) { // no other point can be nearby
|
2540
|
-
var foundIndex = findNearbyBar(s, mouseX, mouseY);
|
2541
|
-
if (foundIndex >= 0) {
|
2542
|
-
items.push({ seriesIndex: i, dataIndex: foundIndex, distance: smallestDistance });
|
2543
|
-
}
|
2544
|
-
}
|
2545
|
-
}
|
2546
|
-
|
2547
|
-
for (i = 0; i < items.length; i++) {
|
2548
|
-
var seriesIndex = items[i].seriesIndex;
|
2549
|
-
var dataIndex = items[i].dataIndex;
|
2550
|
-
var itemDistance = items[i].distance;
|
2551
|
-
var ps = series[seriesIndex].datapoints.pointsize;
|
2552
|
-
|
2553
|
-
foundItems.push({
|
2554
|
-
datapoint: series[seriesIndex].datapoints.points.slice(dataIndex * ps, (dataIndex + 1) * ps),
|
2555
|
-
dataIndex: dataIndex,
|
2556
|
-
series: series[seriesIndex],
|
2557
|
-
seriesIndex: seriesIndex,
|
2558
|
-
distance: Math.sqrt(itemDistance)
|
2559
|
-
});
|
2560
|
-
}
|
2561
|
-
|
2562
|
-
return foundItems;
|
2563
|
-
}
|
2564
|
-
|
2565
|
-
function findNearbyPoint (series, mouseX, mouseY, maxDistance, computeDistance) {
|
2566
|
-
var mx = series.xaxis.c2p(mouseX),
|
2567
|
-
my = series.yaxis.c2p(mouseY),
|
2568
|
-
maxx = maxDistance / series.xaxis.scale,
|
2569
|
-
maxy = maxDistance / series.yaxis.scale,
|
2570
|
-
points = series.datapoints.points,
|
2571
|
-
ps = series.datapoints.pointsize,
|
2572
|
-
smallestDistance = Number.POSITIVE_INFINITY;
|
2573
|
-
|
2574
|
-
// with inverse transforms, we can't use the maxx/maxy
|
2575
|
-
// optimization, sadly
|
2576
|
-
if (series.xaxis.options.inverseTransform) {
|
2577
|
-
maxx = Number.MAX_VALUE;
|
2578
|
-
}
|
2579
|
-
|
2580
|
-
if (series.yaxis.options.inverseTransform) {
|
2581
|
-
maxy = Number.MAX_VALUE;
|
2582
|
-
}
|
2583
|
-
|
2584
|
-
var found = null;
|
2585
|
-
for (var j = 0; j < points.length; j += ps) {
|
2586
|
-
var x = points[j];
|
2587
|
-
var y = points[j + 1];
|
2588
|
-
if (x == null) {
|
2589
|
-
continue;
|
2590
|
-
}
|
2591
|
-
|
2592
|
-
if (x - mx > maxx || x - mx < -maxx ||
|
2593
|
-
y - my > maxy || y - my < -maxy) {
|
2594
|
-
continue;
|
2595
|
-
}
|
2596
|
-
|
2597
|
-
// We have to calculate distances in pixels, not in
|
2598
|
-
// data units, because the scales of the axes may be different
|
2599
|
-
var dx = Math.abs(series.xaxis.p2c(x) - mouseX);
|
2600
|
-
var dy = Math.abs(series.yaxis.p2c(y) - mouseY);
|
2601
|
-
var dist = computeDistance ? computeDistance(dx, dy) : dx * dx + dy * dy;
|
2602
|
-
|
2603
|
-
// use <= to ensure last point takes precedence
|
2604
|
-
// (last generally means on top of)
|
2605
|
-
if (dist < smallestDistance) {
|
2606
|
-
smallestDistance = dist;
|
2607
|
-
found = { dataIndex: j / ps, distance: dist };
|
2608
|
-
}
|
2609
|
-
}
|
2610
|
-
|
2611
|
-
return found;
|
2612
|
-
}
|
2613
|
-
|
2614
|
-
function findNearbyBar (series, mouseX, mouseY) {
|
2615
|
-
var barLeft, barRight,
|
2616
|
-
barWidth = series.bars.barWidth[0] || series.bars.barWidth,
|
2617
|
-
mx = series.xaxis.c2p(mouseX),
|
2618
|
-
my = series.yaxis.c2p(mouseY),
|
2619
|
-
points = series.datapoints.points,
|
2620
|
-
ps = series.datapoints.pointsize;
|
2621
|
-
|
2622
|
-
switch (series.bars.align) {
|
2623
|
-
case "left":
|
2624
|
-
barLeft = 0;
|
2625
|
-
break;
|
2626
|
-
case "right":
|
2627
|
-
barLeft = -barWidth;
|
2628
|
-
break;
|
2629
|
-
default:
|
2630
|
-
barLeft = -barWidth / 2;
|
2631
|
-
}
|
2632
|
-
|
2633
|
-
barRight = barLeft + barWidth;
|
2634
|
-
|
2635
|
-
var fillTowards = series.bars.fillTowards || 0;
|
2636
|
-
var defaultBottom = fillTowards > series.yaxis.min ? Math.min(series.yaxis.max, fillTowards) : series.yaxis.min;
|
2637
|
-
|
2638
|
-
var foundIndex = -1;
|
2639
|
-
for (var j = 0; j < points.length; j += ps) {
|
2640
|
-
var x = points[j], y = points[j + 1];
|
2641
|
-
if (x == null) {
|
2642
|
-
continue;
|
2643
|
-
}
|
2644
|
-
|
2645
|
-
var bottom = ps === 3 ? points[j + 2] : defaultBottom;
|
2646
|
-
// for a bar graph, the cursor must be inside the bar
|
2647
|
-
if (series.bars.horizontal
|
2648
|
-
? (mx <= Math.max(bottom, x) && mx >= Math.min(bottom, x) &&
|
2649
|
-
my >= y + barLeft && my <= y + barRight)
|
2650
|
-
: (mx >= x + barLeft && mx <= x + barRight &&
|
2651
|
-
my >= Math.min(bottom, y) && my <= Math.max(bottom, y))) {
|
2652
|
-
foundIndex = j / ps;
|
2653
|
-
}
|
2654
|
-
}
|
2655
|
-
|
2656
|
-
return foundIndex;
|
2657
|
-
}
|
2658
|
-
|
2659
|
-
function findNearbyInterpolationPoint(posX, posY, seriesFilter) {
|
2660
|
-
var i, j, dist, dx, dy, ps,
|
2661
|
-
item,
|
2662
|
-
smallestDistance = Number.MAX_VALUE;
|
2663
|
-
|
2664
|
-
for (i = 0; i < series.length; ++i) {
|
2665
|
-
if (!seriesFilter(i)) {
|
2666
|
-
continue;
|
2667
|
-
}
|
2668
|
-
var points = series[i].datapoints.points;
|
2669
|
-
ps = series[i].datapoints.pointsize;
|
2670
|
-
|
2671
|
-
// if the data is coming from positive -> negative, reverse the comparison
|
2672
|
-
const comparer = points[points.length - ps] < points[0]
|
2673
|
-
? function (x1, x2) { return x1 > x2 }
|
2674
|
-
: function (x1, x2) { return x2 > x1 };
|
2675
|
-
|
2676
|
-
// do not interpolate outside the bounds of the data.
|
2677
|
-
if (comparer(posX, points[0])) {
|
2678
|
-
continue;
|
2679
|
-
}
|
2680
|
-
|
2681
|
-
// Find the nearest points, x-wise
|
2682
|
-
for (j = ps; j < points.length; j += ps) {
|
2683
|
-
if (comparer(posX, points[j])) {
|
2684
|
-
break;
|
2685
|
-
}
|
2686
|
-
}
|
2687
|
-
|
2688
|
-
// Now Interpolate
|
2689
|
-
var y,
|
2690
|
-
p1x = points[j - ps],
|
2691
|
-
p1y = points[j - ps + 1],
|
2692
|
-
p2x = points[j],
|
2693
|
-
p2y = points[j + 1];
|
2694
|
-
|
2695
|
-
if ((p1x === undefined) || (p2x === undefined) ||
|
2696
|
-
(p1y === undefined) || (p2y === undefined)) {
|
2697
|
-
continue;
|
2698
|
-
}
|
2699
|
-
|
2700
|
-
if (p1x === p2x) {
|
2701
|
-
y = p2y
|
2702
|
-
} else {
|
2703
|
-
y = p1y + (p2y - p1y) * (posX - p1x) / (p2x - p1x);
|
2704
|
-
}
|
2705
|
-
|
2706
|
-
posY = y;
|
2707
|
-
|
2708
|
-
dx = Math.abs(series[i].xaxis.p2c(p2x) - posX);
|
2709
|
-
dy = Math.abs(series[i].yaxis.p2c(p2y) - posY);
|
2710
|
-
dist = dx * dx + dy * dy;
|
2711
|
-
|
2712
|
-
if (dist < smallestDistance) {
|
2713
|
-
smallestDistance = dist;
|
2714
|
-
item = [posX, posY, i, j];
|
2715
|
-
}
|
2716
|
-
}
|
2717
|
-
|
2718
|
-
if (item) {
|
2719
|
-
i = item[2];
|
2720
|
-
j = item[3];
|
2721
|
-
ps = series[i].datapoints.pointsize;
|
2722
|
-
points = series[i].datapoints.points;
|
2723
|
-
p1x = points[j - ps];
|
2724
|
-
p1y = points[j - ps + 1];
|
2725
|
-
p2x = points[j];
|
2726
|
-
p2y = points[j + 1];
|
2727
|
-
|
2728
|
-
return {
|
2729
|
-
datapoint: [item[0], item[1]],
|
2730
|
-
leftPoint: [p1x, p1y],
|
2731
|
-
rightPoint: [p2x, p2y],
|
2732
|
-
seriesIndex: i
|
2733
|
-
};
|
2734
|
-
}
|
2735
|
-
|
2736
|
-
return null;
|
2737
|
-
}
|
2738
|
-
|
2739
|
-
function triggerRedrawOverlay() {
|
2740
|
-
var t = options.interaction.redrawOverlayInterval;
|
2741
|
-
if (t === -1) { // skip event queue
|
2742
|
-
drawOverlay();
|
2743
|
-
return;
|
2744
|
-
}
|
2745
|
-
|
2746
|
-
if (!redrawTimeout) {
|
2747
|
-
redrawTimeout = setTimeout(function() {
|
2748
|
-
drawOverlay(plot);
|
2749
|
-
}, t);
|
2750
|
-
}
|
2751
|
-
}
|
2752
|
-
|
2753
|
-
function drawOverlay(plot) {
|
2754
|
-
redrawTimeout = null;
|
2755
|
-
|
2756
|
-
if (!octx) {
|
2757
|
-
return;
|
2758
|
-
}
|
2759
|
-
overlay.clear();
|
2760
|
-
executeHooks(hooks.drawOverlay, [octx, overlay]);
|
2761
|
-
var event = new CustomEvent('onDrawingDone');
|
2762
|
-
plot.getEventHolder().dispatchEvent(event);
|
2763
|
-
plot.getPlaceholder().trigger('drawingdone');
|
2764
|
-
}
|
2765
|
-
|
2766
|
-
function getColorOrGradient(spec, bottom, top, defaultColor) {
|
2767
|
-
if (typeof spec === "string") {
|
2768
|
-
return spec;
|
2769
|
-
} else {
|
2770
|
-
// assume this is a gradient spec; IE currently only
|
2771
|
-
// supports a simple vertical gradient properly, so that's
|
2772
|
-
// what we support too
|
2773
|
-
var gradient = ctx.createLinearGradient(0, top, 0, bottom);
|
2774
|
-
|
2775
|
-
for (var i = 0, l = spec.colors.length; i < l; ++i) {
|
2776
|
-
var c = spec.colors[i];
|
2777
|
-
if (typeof c !== "string") {
|
2778
|
-
var co = $.color.parse(defaultColor);
|
2779
|
-
if (c.brightness != null) {
|
2780
|
-
co = co.scale('rgb', c.brightness);
|
2781
|
-
}
|
2782
|
-
|
2783
|
-
if (c.opacity != null) {
|
2784
|
-
co.a *= c.opacity;
|
2785
|
-
}
|
2786
|
-
|
2787
|
-
c = co.toString();
|
2788
|
-
}
|
2789
|
-
gradient.addColorStop(i / (l - 1), c);
|
2790
|
-
}
|
2791
|
-
|
2792
|
-
return gradient;
|
2793
|
-
}
|
2794
|
-
}
|
2795
|
-
}
|
2796
|
-
|
2797
|
-
// Add the plot function to the top level of the jQuery object
|
2798
|
-
|
2799
|
-
$.plot = function(placeholder, data, options) {
|
2800
|
-
var plot = new Plot($(placeholder), data, options, $.plot.plugins);
|
2801
|
-
return plot;
|
2802
|
-
};
|
2803
|
-
|
2804
|
-
$.plot.version = "3.0.0";
|
2805
|
-
|
2806
|
-
$.plot.plugins = [];
|
2807
|
-
|
2808
|
-
// Also add the plot function as a chainable property
|
2809
|
-
$.fn.plot = function(data, options) {
|
2810
|
-
return this.each(function() {
|
2811
|
-
$.plot(this, data, options);
|
2812
|
-
});
|
2813
|
-
};
|
2814
|
-
|
2815
|
-
$.plot.linearTickGenerator = defaultTickGenerator;
|
2816
|
-
$.plot.defaultTickFormatter = defaultTickFormatter;
|
2817
|
-
$.plot.expRepTickFormatter = expRepTickFormatter;
|
2818
|
-
})(jQuery);
|