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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +117 -26
  3. data/.gitignore +2 -1
  4. data/.solr_wrapper.yml +8 -0
  5. data/Gemfile +7 -1
  6. data/README.md +84 -87
  7. data/Rakefile +69 -0
  8. data/VERSION +1 -1
  9. data/app/assets/javascripts/blacklight-range-limit/index.js +345 -0
  10. data/app/components/blacklight_range_limit/range_facet_component.html.erb +29 -27
  11. data/app/components/blacklight_range_limit/range_facet_component.rb +27 -9
  12. data/app/components/blacklight_range_limit/range_form_component.html.erb +23 -5
  13. data/app/components/blacklight_range_limit/range_form_component.rb +8 -20
  14. data/app/presenters/blacklight_range_limit/facet_field_presenter.rb +13 -2
  15. data/app/presenters/blacklight_range_limit/facet_item_presenter.rb +15 -11
  16. data/app/presenters/blacklight_range_limit/filter_field.rb +9 -3
  17. data/blacklight_range_limit.gemspec +1 -1
  18. data/config/importmap.rb +9 -0
  19. data/config/locales/blacklight_range_limit.ar.yml +13 -8
  20. data/config/locales/blacklight_range_limit.de.yml +13 -8
  21. data/config/locales/blacklight_range_limit.en.yml +5 -1
  22. data/config/locales/blacklight_range_limit.es.yml +18 -0
  23. data/config/locales/blacklight_range_limit.it.yml +13 -8
  24. data/doc/example-screenshot.png +0 -0
  25. data/lib/blacklight_range_limit/controller_override.rb +8 -35
  26. data/lib/blacklight_range_limit/engine.rb +48 -0
  27. data/lib/blacklight_range_limit/range_limit_builder.rb +61 -3
  28. data/lib/blacklight_range_limit.rb +10 -30
  29. data/lib/generators/blacklight_range_limit/assets_generator.rb +82 -18
  30. data/lib/generators/blacklight_range_limit/install_generator.rb +5 -2
  31. data/lib/generators/blacklight_range_limit/jsbundling_bl7_fixup_generator.rb +98 -0
  32. data/package-lock.json +8 -7
  33. data/package.json +10 -14
  34. data/spec/components/range_facet_component_spec.rb +101 -32
  35. data/spec/components/range_form_component_spec.rb +2 -2
  36. data/spec/components/range_segments_component_spec.rb +64 -0
  37. data/spec/features/blacklight_range_limit_spec.rb +21 -13
  38. data/spec/features/run_through_spec.rb +210 -0
  39. data/spec/presenters/facet_field_presenter_spec.rb +72 -0
  40. data/spec/presenters/filter_field_spec.rb +36 -1
  41. data/spec/requests/bad_param_requests_spec.rb +61 -0
  42. data/spec/spec_helper.rb +10 -0
  43. data/spec/test_app_templates/Gemfile.extra +11 -1
  44. data/spec/test_app_templates/lib/generators/test_app_generator.rb +37 -6
  45. metadata +27 -51
  46. data/app/assets/javascripts/blacklight_range_limit/blacklight_range_limit.esm.js +0 -534
  47. data/app/assets/javascripts/blacklight_range_limit/blacklight_range_limit.esm.js.map +0 -1
  48. data/app/assets/javascripts/blacklight_range_limit/blacklight_range_limit.umd.js +0 -542
  49. data/app/assets/javascripts/blacklight_range_limit/blacklight_range_limit.umd.js.map +0 -1
  50. data/app/assets/javascripts/blacklight_range_limit.js +0 -27
  51. data/app/assets/stylesheets/blacklight_range_limit/blacklight_range_limit.css +0 -60
  52. data/app/assets/stylesheets/blacklight_range_limit.css +0 -7
  53. data/app/helpers/range_limit_helper.rb +0 -130
  54. data/app/javascript/blacklight_range_limit/index.js +0 -11
  55. data/app/javascript/blacklight_range_limit/range_limit_distro_facets.js +0 -76
  56. data/app/javascript/blacklight_range_limit/range_limit_plotting.js +0 -186
  57. data/app/javascript/blacklight_range_limit/range_limit_shared.js +0 -112
  58. data/app/javascript/blacklight_range_limit/range_limit_slider.js +0 -163
  59. data/app/javascripts/blacklight_range_limit/index.js +0 -13
  60. data/app/views/blacklight_range_limit/_range_segments.html.erb +0 -8
  61. data/app/views/blacklight_range_limit/range_segments.html.erb +0 -1
  62. data/app/views/catalog/range_limit_panel.html.erb +0 -1
  63. data/rollup.config.js +0 -37
  64. data/spec/features/a_javascript_spec.rb +0 -70
  65. data/spec/helpers/blacklight_range_limit_helper_spec.rb +0 -37
  66. data/vendor/assets/javascripts/bootstrap-slider.js +0 -388
  67. data/vendor/assets/javascripts/flot/jquery.canvaswrapper.js +0 -549
  68. data/vendor/assets/javascripts/flot/jquery.colorhelpers.js +0 -199
  69. data/vendor/assets/javascripts/flot/jquery.event.drag.js +0 -145
  70. data/vendor/assets/javascripts/flot/jquery.flot.browser.js +0 -98
  71. data/vendor/assets/javascripts/flot/jquery.flot.drawSeries.js +0 -662
  72. data/vendor/assets/javascripts/flot/jquery.flot.hover.js +0 -359
  73. data/vendor/assets/javascripts/flot/jquery.flot.js +0 -2818
  74. data/vendor/assets/javascripts/flot/jquery.flot.saturated.js +0 -43
  75. data/vendor/assets/javascripts/flot/jquery.flot.selection.js +0 -527
  76. data/vendor/assets/javascripts/flot/jquery.flot.uiConstants.js +0 -10
  77. 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);