iobroker.ebus 3.3.6 → 3.3.8

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