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