iobroker.ebus 3.2.4 → 3.2.5

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 (47) hide show
  1. package/.eslintrc.json +34 -34
  2. package/.releaseconfig.json +3 -0
  3. package/LICENSE +20 -20
  4. package/README.md +143 -130
  5. package/admin/index_m.html +419 -419
  6. package/admin/style.css +18 -18
  7. package/admin/words.js +27 -27
  8. package/io-package.json +205 -192
  9. package/lib/support_tools.js +370 -370
  10. package/lib/tools.js +99 -99
  11. package/main.js +1232 -1232
  12. package/package.json +12 -10
  13. package/widgets/ebus/lib/js/flot/jquery.canvaswrapper.js +549 -549
  14. package/widgets/ebus/lib/js/flot/jquery.colorhelpers.js +199 -199
  15. package/widgets/ebus/lib/js/flot/jquery.flot.axislabels.js +212 -212
  16. package/widgets/ebus/lib/js/flot/jquery.flot.browser.js +98 -98
  17. package/widgets/ebus/lib/js/flot/jquery.flot.categories.js +202 -202
  18. package/widgets/ebus/lib/js/flot/jquery.flot.composeImages.js +330 -330
  19. package/widgets/ebus/lib/js/flot/jquery.flot.crosshair.js +202 -202
  20. package/widgets/ebus/lib/js/flot/jquery.flot.drawSeries.js +662 -662
  21. package/widgets/ebus/lib/js/flot/jquery.flot.errorbars.js +375 -375
  22. package/widgets/ebus/lib/js/flot/jquery.flot.fillbetween.js +254 -254
  23. package/widgets/ebus/lib/js/flot/jquery.flot.flatdata.js +47 -47
  24. package/widgets/ebus/lib/js/flot/jquery.flot.hover.js +361 -361
  25. package/widgets/ebus/lib/js/flot/jquery.flot.image.js +249 -249
  26. package/widgets/ebus/lib/js/flot/jquery.flot.js +2953 -2953
  27. package/widgets/ebus/lib/js/flot/jquery.flot.legend.js +437 -437
  28. package/widgets/ebus/lib/js/flot/jquery.flot.logaxis.js +298 -298
  29. package/widgets/ebus/lib/js/flot/jquery.flot.navigate.js +834 -834
  30. package/widgets/ebus/lib/js/flot/jquery.flot.pie.js +794 -794
  31. package/widgets/ebus/lib/js/flot/jquery.flot.resize.js +60 -60
  32. package/widgets/ebus/lib/js/flot/jquery.flot.saturated.js +43 -43
  33. package/widgets/ebus/lib/js/flot/jquery.flot.selection.js +527 -527
  34. package/widgets/ebus/lib/js/flot/jquery.flot.stack.js +220 -220
  35. package/widgets/ebus/lib/js/flot/jquery.flot.symbol.js +98 -98
  36. package/widgets/ebus/lib/js/flot/jquery.flot.threshold.js +143 -143
  37. package/widgets/ebus/lib/js/flot/jquery.flot.time.js +586 -586
  38. package/widgets/ebus/lib/js/flot/jquery.flot.touch.js +320 -320
  39. package/widgets/ebus/lib/js/flot/jquery.flot.touchNavigate.js +360 -360
  40. package/widgets/ebus/lib/js/flot/jquery.flot.uiConstants.js +10 -10
  41. package/widgets/ebus/lib/js/flot/jquery.js +9473 -9473
  42. package/widgets/ebus/lib/js/lib/globalize.culture.en-US.js +33 -33
  43. package/widgets/ebus/lib/js/lib/globalize.js +1601 -1601
  44. package/widgets/ebus/lib/js/lib/jquery.event.drag.js +145 -145
  45. package/widgets/ebus/lib/js/lib/jquery.mousewheel.js +86 -86
  46. package/widgets/ebus.html +2395 -2395
  47. package/readme.txt +0 -297
@@ -1,834 +1,834 @@
1
- /* Flot plugin for adding the ability to pan and zoom the plot.
2
-
3
- Copyright (c) 2007-2014 IOLA and Ole Laursen.
4
- Copyright (c) 2016 Ciprian Ceteras.
5
- Copyright (c) 2017 Raluca Portase.
6
- Licensed under the MIT license.
7
-
8
- */
9
-
10
- /**
11
- ## jquery.flot.navigate.js
12
-
13
- This flot plugin is used for adding the ability to pan and zoom the plot.
14
- A higher level overview is available at [interactions](interactions.md) documentation.
15
-
16
- The default behaviour is scrollwheel up/down to zoom in, drag
17
- to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
18
- plot.pan( offset ) so you easily can add custom controls. It also fires
19
- "plotpan" and "plotzoom" events, useful for synchronizing plots.
20
-
21
- The plugin supports these options:
22
- ```js
23
- zoom: {
24
- interactive: false,
25
- active: false,
26
- amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
27
- }
28
-
29
- pan: {
30
- interactive: false,
31
- active: false,
32
- cursor: "move", // CSS mouse cursor value used when dragging, e.g. "pointer"
33
- frameRate: 60,
34
- mode: "smart" // enable smart pan mode
35
- }
36
-
37
- xaxis: {
38
- axisZoom: true, //zoom axis when mouse over it is allowed
39
- plotZoom: true, //zoom axis is allowed for plot zoom
40
- axisPan: true, //pan axis when mouse over it is allowed
41
- plotPan: true, //pan axis is allowed for plot pan
42
- panRange: [undefined, undefined], // no limit on pan range, or [min, max] in axis units
43
- zoomRange: [undefined, undefined], // no limit on zoom range, or [closest zoom, furthest zoom] in axis units
44
- }
45
-
46
- yaxis: {
47
- axisZoom: true, //zoom axis when mouse over it is allowed
48
- plotZoom: true, //zoom axis is allowed for plot zoom
49
- axisPan: true, //pan axis when mouse over it is allowed
50
- plotPan: true //pan axis is allowed for plot pan
51
- panRange: [undefined, undefined], // no limit on pan range, or [min, max] in axis units
52
- zoomRange: [undefined, undefined], // no limit on zoom range, or [closest zoom, furthest zoom] in axis units
53
- }
54
- ```
55
- **interactive** enables the built-in drag/click behaviour. If you enable
56
- interactive for pan, then you'll have a basic plot that supports moving
57
- around; the same for zoom.
58
-
59
- **active** is true after a touch tap on plot. This enables plot navigation.
60
- Once activated, zoom and pan cannot be deactivated. When the plot becomes active,
61
- "plotactivated" event is triggered.
62
-
63
- **amount** specifies the default amount to zoom in (so 1.5 = 150%) relative to
64
- the current viewport.
65
-
66
- **cursor** is a standard CSS mouse cursor string used for visual feedback to the
67
- user when dragging.
68
-
69
- **frameRate** specifies the maximum number of times per second the plot will
70
- update itself while the user is panning around on it (set to null to disable
71
- intermediate pans, the plot will then not update until the mouse button is
72
- released).
73
-
74
- **mode** a string specifies the pan mode for mouse interaction. Accepted values:
75
- 'manual': no pan hint or direction snapping;
76
- 'smart': The graph shows pan hint bar and the pan movement will snap
77
- to one direction when the drag direction is close to it;
78
- 'smartLock'. The graph shows pan hint bar and the pan movement will always
79
- snap to a direction that the drag diorection started with.
80
-
81
- Example API usage:
82
- ```js
83
- plot = $.plot(...);
84
-
85
- // zoom default amount in on the pixel ( 10, 20 )
86
- plot.zoom({ center: { left: 10, top: 20 } });
87
-
88
- // zoom out again
89
- plot.zoomOut({ center: { left: 10, top: 20 } });
90
-
91
- // zoom 200% in on the pixel (10, 20)
92
- plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
93
-
94
- // pan 100 pixels to the left (changing x-range in a positive way) and 20 down
95
- plot.pan({ left: -100, top: 20 })
96
- ```
97
-
98
- Here, "center" specifies where the center of the zooming should happen. Note
99
- that this is defined in pixel space, not the space of the data points (you can
100
- use the p2c helpers on the axes in Flot to help you convert between these).
101
-
102
- **amount** is the amount to zoom the viewport relative to the current range, so
103
- 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You
104
- can set the default in the options.
105
- */
106
-
107
- /* eslint-enable */
108
- (function($) {
109
- 'use strict';
110
-
111
- var options = {
112
- zoom: {
113
- interactive: false,
114
- active: false,
115
- amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
116
- },
117
- pan: {
118
- interactive: false,
119
- active: false,
120
- cursor: "move",
121
- frameRate: 60,
122
- mode: 'smart'
123
- },
124
- recenter: {
125
- interactive: true
126
- },
127
- xaxis: {
128
- axisZoom: true, //zoom axis when mouse over it is allowed
129
- plotZoom: true, //zoom axis is allowed for plot zoom
130
- axisPan: true, //pan axis when mouse over it is allowed
131
- plotPan: true, //pan axis is allowed for plot pan
132
- panRange: [undefined, undefined], // no limit on pan range, or [min, max] in axis units
133
- zoomRange: [undefined, undefined] // no limit on zoom range, or [closest zoom, furthest zoom] in axis units
134
- },
135
- yaxis: {
136
- axisZoom: true,
137
- plotZoom: true,
138
- axisPan: true,
139
- plotPan: true,
140
- panRange: [undefined, undefined], // no limit on pan range, or [min, max] in axis units
141
- zoomRange: [undefined, undefined] // no limit on zoom range, or [closest zoom, furthest zoom] in axis units
142
- }
143
- };
144
-
145
- var saturated = $.plot.saturated;
146
- var browser = $.plot.browser;
147
- var SNAPPING_CONSTANT = $.plot.uiConstants.SNAPPING_CONSTANT;
148
- var PANHINT_LENGTH_CONSTANT = $.plot.uiConstants.PANHINT_LENGTH_CONSTANT;
149
-
150
- function init(plot) {
151
- plot.hooks.processOptions.push(initNevigation);
152
- }
153
-
154
- function initNevigation(plot, options) {
155
- var panAxes = null;
156
- var canDrag = false;
157
- var useManualPan = options.pan.mode === 'manual',
158
- smartPanLock = options.pan.mode === 'smartLock',
159
- useSmartPan = smartPanLock || options.pan.mode === 'smart';
160
-
161
- function onZoomClick(e, zoomOut, amount) {
162
- var page = browser.getPageXY(e);
163
-
164
- var c = plot.offset();
165
- c.left = page.X - c.left;
166
- c.top = page.Y - c.top;
167
-
168
- var ec = plot.getPlaceholder().offset();
169
- ec.left = page.X - ec.left;
170
- ec.top = page.Y - ec.top;
171
-
172
- var axes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
173
- var box = axis.box;
174
- if (box !== undefined) {
175
- return (ec.left > box.left) && (ec.left < box.left + box.width) &&
176
- (ec.top > box.top) && (ec.top < box.top + box.height);
177
- }
178
- });
179
-
180
- if (axes.length === 0) {
181
- axes = undefined;
182
- }
183
-
184
- if (zoomOut) {
185
- plot.zoomOut({
186
- center: c,
187
- axes: axes,
188
- amount: amount
189
- });
190
- } else {
191
- plot.zoom({
192
- center: c,
193
- axes: axes,
194
- amount: amount
195
- });
196
- }
197
- }
198
-
199
- var prevCursor = 'default',
200
- panHint = null,
201
- panTimeout = null,
202
- plotState,
203
- prevDragPosition = { x: 0, y: 0 },
204
- isPanAction = false;
205
-
206
- function onMouseWheel(e, delta) {
207
- var maxAbsoluteDeltaOnMac = 1,
208
- isMacScroll = Math.abs(e.originalEvent.deltaY) <= maxAbsoluteDeltaOnMac,
209
- defaultNonMacScrollAmount = null,
210
- macMagicRatio = 50,
211
- amount = isMacScroll ? 1 + Math.abs(e.originalEvent.deltaY) / macMagicRatio : defaultNonMacScrollAmount;
212
-
213
- if (isPanAction) {
214
- onDragEnd(e);
215
- }
216
-
217
- if (plot.getOptions().zoom.active) {
218
- e.preventDefault();
219
- onZoomClick(e, delta < 0, amount);
220
- return false;
221
- }
222
- }
223
-
224
- plot.navigationState = function(startPageX, startPageY) {
225
- var axes = this.getAxes();
226
- var result = {};
227
- Object.keys(axes).forEach(function(axisName) {
228
- var axis = axes[axisName];
229
- result[axisName] = {
230
- navigationOffset: { below: axis.options.offset.below || 0,
231
- above: axis.options.offset.above || 0},
232
- axisMin: axis.min,
233
- axisMax: axis.max,
234
- diagMode: false
235
- }
236
- });
237
-
238
- result.startPageX = startPageX || 0;
239
- result.startPageY = startPageY || 0;
240
- return result;
241
- }
242
-
243
- function onMouseDown(e) {
244
- canDrag = true;
245
- }
246
-
247
- function onMouseUp(e) {
248
- canDrag = false;
249
- }
250
-
251
- function isLeftMouseButtonPressed(e) {
252
- return e.button === 0;
253
- }
254
-
255
- function onDragStart(e) {
256
- if (!canDrag || !isLeftMouseButtonPressed(e)) {
257
- return false;
258
- }
259
-
260
- isPanAction = true;
261
- var page = browser.getPageXY(e);
262
-
263
- var ec = plot.getPlaceholder().offset();
264
- ec.left = page.X - ec.left;
265
- ec.top = page.Y - ec.top;
266
-
267
- panAxes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
268
- var box = axis.box;
269
- if (box !== undefined) {
270
- return (ec.left > box.left) && (ec.left < box.left + box.width) &&
271
- (ec.top > box.top) && (ec.top < box.top + box.height);
272
- }
273
- });
274
-
275
- if (panAxes.length === 0) {
276
- panAxes = undefined;
277
- }
278
-
279
- var c = plot.getPlaceholder().css('cursor');
280
- if (c) {
281
- prevCursor = c;
282
- }
283
-
284
- plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
285
-
286
- if (useSmartPan) {
287
- plotState = plot.navigationState(page.X, page.Y);
288
- } else if (useManualPan) {
289
- prevDragPosition.x = page.X;
290
- prevDragPosition.y = page.Y;
291
- }
292
- }
293
-
294
- function onDrag(e) {
295
- if (!isPanAction) {
296
- return;
297
- }
298
-
299
- var page = browser.getPageXY(e);
300
- var frameRate = plot.getOptions().pan.frameRate;
301
-
302
- if (frameRate === -1) {
303
- if (useSmartPan) {
304
- plot.smartPan({
305
- x: plotState.startPageX - page.X,
306
- y: plotState.startPageY - page.Y
307
- }, plotState, panAxes, false, smartPanLock);
308
- } else if (useManualPan) {
309
- plot.pan({
310
- left: prevDragPosition.x - page.X,
311
- top: prevDragPosition.y - page.Y,
312
- axes: panAxes
313
- });
314
- prevDragPosition.x = page.X;
315
- prevDragPosition.y = page.Y;
316
- }
317
- return;
318
- }
319
-
320
- if (panTimeout || !frameRate) return;
321
-
322
- panTimeout = setTimeout(function() {
323
- if (useSmartPan) {
324
- plot.smartPan({
325
- x: plotState.startPageX - page.X,
326
- y: plotState.startPageY - page.Y
327
- }, plotState, panAxes, false, smartPanLock);
328
- } else if (useManualPan) {
329
- plot.pan({
330
- left: prevDragPosition.x - page.X,
331
- top: prevDragPosition.y - page.Y,
332
- axes: panAxes
333
- });
334
- prevDragPosition.x = page.X;
335
- prevDragPosition.y = page.Y;
336
- }
337
-
338
- panTimeout = null;
339
- }, 1 / frameRate * 1000);
340
- }
341
-
342
- function onDragEnd(e) {
343
- if (!isPanAction) {
344
- return;
345
- }
346
-
347
- if (panTimeout) {
348
- clearTimeout(panTimeout);
349
- panTimeout = null;
350
- }
351
-
352
- isPanAction = false;
353
- var page = browser.getPageXY(e);
354
-
355
- plot.getPlaceholder().css('cursor', prevCursor);
356
-
357
- if (useSmartPan) {
358
- plot.smartPan({
359
- x: plotState.startPageX - page.X,
360
- y: plotState.startPageY - page.Y
361
- }, plotState, panAxes, false, smartPanLock);
362
- plot.smartPan.end();
363
- } else if (useManualPan) {
364
- plot.pan({
365
- left: prevDragPosition.x - page.X,
366
- top: prevDragPosition.y - page.Y,
367
- axes: panAxes
368
- });
369
- prevDragPosition.x = 0;
370
- prevDragPosition.y = 0;
371
- }
372
- }
373
-
374
- function onDblClick(e) {
375
- plot.activate();
376
- var o = plot.getOptions()
377
-
378
- if (!o.recenter.interactive) { return; }
379
-
380
- var axes = plot.getTouchedAxis(e.clientX, e.clientY),
381
- event;
382
-
383
- plot.recenter({ axes: axes[0] ? axes : null });
384
-
385
- if (axes[0]) {
386
- event = new $.Event('re-center', { detail: {
387
- axisTouched: axes[0]
388
- }});
389
- } else {
390
- event = new $.Event('re-center', { detail: e });
391
- }
392
- plot.getPlaceholder().trigger(event);
393
- }
394
-
395
- function onClick(e) {
396
- plot.activate();
397
-
398
- if (isPanAction) {
399
- onDragEnd(e);
400
- }
401
-
402
- return false;
403
- }
404
-
405
- plot.activate = function() {
406
- var o = plot.getOptions();
407
- if (!o.pan.active || !o.zoom.active) {
408
- o.pan.active = true;
409
- o.zoom.active = true;
410
- plot.getPlaceholder().trigger("plotactivated", [plot]);
411
- }
412
- }
413
-
414
- function bindEvents(plot, eventHolder) {
415
- var o = plot.getOptions();
416
- if (o.zoom.interactive) {
417
- eventHolder.mousewheel(onMouseWheel);
418
- }
419
-
420
- if (o.pan.interactive) {
421
- plot.addEventHandler("dragstart", onDragStart, eventHolder, 0);
422
- plot.addEventHandler("drag", onDrag, eventHolder, 0);
423
- plot.addEventHandler("dragend", onDragEnd, eventHolder, 0);
424
- eventHolder.bind("mousedown", onMouseDown);
425
- eventHolder.bind("mouseup", onMouseUp);
426
- }
427
-
428
- eventHolder.dblclick(onDblClick);
429
- eventHolder.click(onClick);
430
- }
431
-
432
- plot.zoomOut = function(args) {
433
- if (!args) {
434
- args = {};
435
- }
436
-
437
- if (!args.amount) {
438
- args.amount = plot.getOptions().zoom.amount;
439
- }
440
-
441
- args.amount = 1 / args.amount;
442
- plot.zoom(args);
443
- };
444
-
445
- plot.zoom = function(args) {
446
- if (!args) {
447
- args = {};
448
- }
449
-
450
- var c = args.center,
451
- amount = args.amount || plot.getOptions().zoom.amount,
452
- w = plot.width(),
453
- h = plot.height(),
454
- axes = args.axes || plot.getAxes();
455
-
456
- if (!c) {
457
- c = {
458
- left: w / 2,
459
- top: h / 2
460
- };
461
- }
462
-
463
- var xf = c.left / w,
464
- yf = c.top / h,
465
- minmax = {
466
- x: {
467
- min: c.left - xf * w / amount,
468
- max: c.left + (1 - xf) * w / amount
469
- },
470
- y: {
471
- min: c.top - yf * h / amount,
472
- max: c.top + (1 - yf) * h / amount
473
- }
474
- };
475
-
476
- for (var key in axes) {
477
- if (!axes.hasOwnProperty(key)) {
478
- continue;
479
- }
480
-
481
- var axis = axes[key],
482
- opts = axis.options,
483
- min = minmax[axis.direction].min,
484
- max = minmax[axis.direction].max,
485
- navigationOffset = axis.options.offset;
486
-
487
- //skip axis without axisZoom when zooming only on certain axis or axis without plotZoom for zoom on entire plot
488
- if ((!opts.axisZoom && args.axes) || (!args.axes && !opts.plotZoom)) {
489
- continue;
490
- }
491
-
492
- min = $.plot.saturated.saturate(axis.c2p(min));
493
- max = $.plot.saturated.saturate(axis.c2p(max));
494
- if (min > max) {
495
- // make sure min < max
496
- var tmp = min;
497
- min = max;
498
- max = tmp;
499
- }
500
-
501
- // test for zoom limits zoomRange: [min,max]
502
- if (opts.zoomRange) {
503
- // zoomed in too far
504
- if (max - min < opts.zoomRange[0]) {
505
- continue;
506
- }
507
- // zoomed out to far
508
- if (max - min > opts.zoomRange[1]) {
509
- continue;
510
- }
511
- }
512
-
513
- var offsetBelow = $.plot.saturated.saturate(navigationOffset.below - (axis.min - min));
514
- var offsetAbove = $.plot.saturated.saturate(navigationOffset.above - (axis.max - max));
515
- opts.offset = { below: offsetBelow, above: offsetAbove };
516
- };
517
-
518
- plot.setupGrid(true);
519
- plot.draw();
520
-
521
- if (!args.preventEvent) {
522
- plot.getPlaceholder().trigger("plotzoom", [plot, args]);
523
- }
524
- };
525
-
526
- plot.pan = function(args) {
527
- var delta = {
528
- x: +args.left,
529
- y: +args.top
530
- };
531
-
532
- if (isNaN(delta.x)) delta.x = 0;
533
- if (isNaN(delta.y)) delta.y = 0;
534
-
535
- $.each(args.axes || plot.getAxes(), function(_, axis) {
536
- var opts = axis.options,
537
- d = delta[axis.direction];
538
-
539
- //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot
540
- if ((!opts.axisPan && args.axes) || (!opts.plotPan && !args.axes)) {
541
- return;
542
- }
543
-
544
- // calc min delta (revealing left edge of plot)
545
- var minD = axis.p2c(opts.panRange[0]) - axis.p2c(axis.min);
546
- // calc max delta (revealing right edge of plot)
547
- var maxD = axis.p2c(opts.panRange[1]) - axis.p2c(axis.max);
548
- // limit delta to min or max if enabled
549
- if (opts.panRange[0] !== undefined && d >= maxD) d = maxD;
550
- if (opts.panRange[1] !== undefined && d <= minD) d = minD;
551
-
552
- if (d !== 0) {
553
- var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axis.min) + d) - axis.c2p(axis.p2c(axis.min))),
554
- navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axis.max) + d) - axis.c2p(axis.p2c(axis.max)));
555
-
556
- if (!isFinite(navigationOffsetBelow)) {
557
- navigationOffsetBelow = 0;
558
- }
559
-
560
- if (!isFinite(navigationOffsetAbove)) {
561
- navigationOffsetAbove = 0;
562
- }
563
-
564
- opts.offset = {
565
- below: saturated.saturate(navigationOffsetBelow + (opts.offset.below || 0)),
566
- above: saturated.saturate(navigationOffsetAbove + (opts.offset.above || 0))
567
- };
568
- }
569
- });
570
-
571
- plot.setupGrid(true);
572
- plot.draw();
573
- if (!args.preventEvent) {
574
- plot.getPlaceholder().trigger("plotpan", [plot, args]);
575
- }
576
- };
577
-
578
- plot.recenter = function(args) {
579
- $.each(args.axes || plot.getAxes(), function(_, axis) {
580
- if (args.axes) {
581
- if (this.direction === 'x') {
582
- axis.options.offset = { below: 0 };
583
- } else if (this.direction === 'y') {
584
- axis.options.offset = { above: 0 };
585
- }
586
- } else {
587
- axis.options.offset = { below: 0, above: 0 };
588
- }
589
- });
590
- plot.setupGrid(true);
591
- plot.draw();
592
- };
593
-
594
- var shouldSnap = function(delta) {
595
- return (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) ||
596
- (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT);
597
- }
598
-
599
- // adjust delta so the pan action is constrained on the vertical or horizontal direction
600
- // it the movements in the other direction are small
601
- var adjustDeltaToSnap = function(delta) {
602
- if (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT) {
603
- return {x: 0, y: delta.y};
604
- }
605
-
606
- if (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) {
607
- return {x: delta.x, y: 0};
608
- }
609
-
610
- return delta;
611
- }
612
-
613
- var lockedDirection = null;
614
- var lockDeltaDirection = function(delta) {
615
- if (!lockedDirection && Math.max(Math.abs(delta.x), Math.abs(delta.y)) >= SNAPPING_CONSTANT) {
616
- lockedDirection = Math.abs(delta.x) < Math.abs(delta.y) ? 'y' : 'x';
617
- }
618
-
619
- switch (lockedDirection) {
620
- case 'x':
621
- return { x: delta.x, y: 0 };
622
- case 'y':
623
- return { x: 0, y: delta.y };
624
- default:
625
- return { x: 0, y: 0 };
626
- }
627
- }
628
-
629
- var isDiagonalMode = function(delta) {
630
- if (Math.abs(delta.x) > 0 && Math.abs(delta.y) > 0) {
631
- return true;
632
- }
633
- return false;
634
- }
635
-
636
- var restoreAxisOffset = function(axes, initialState, delta) {
637
- var axis;
638
- Object.keys(axes).forEach(function(axisName) {
639
- axis = axes[axisName];
640
- if (delta[axis.direction] === 0) {
641
- axis.options.offset.below = initialState[axisName].navigationOffset.below;
642
- axis.options.offset.above = initialState[axisName].navigationOffset.above;
643
- }
644
- });
645
- }
646
-
647
- var prevDelta = { x: 0, y: 0 };
648
- plot.smartPan = function(delta, initialState, panAxes, preventEvent, smartLock) {
649
- var snap = smartLock ? true : shouldSnap(delta),
650
- axes = plot.getAxes(),
651
- opts;
652
- delta = smartLock ? lockDeltaDirection(delta) : adjustDeltaToSnap(delta);
653
-
654
- if (isDiagonalMode(delta)) {
655
- initialState.diagMode = true;
656
- }
657
-
658
- if (snap && initialState.diagMode === true) {
659
- initialState.diagMode = false;
660
- restoreAxisOffset(axes, initialState, delta);
661
- }
662
-
663
- if (snap) {
664
- panHint = {
665
- start: {
666
- x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left,
667
- y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top
668
- },
669
- end: {
670
- x: initialState.startPageX - delta.x - plot.offset().left + plot.getPlotOffset().left,
671
- y: initialState.startPageY - delta.y - plot.offset().top + plot.getPlotOffset().top
672
- }
673
- }
674
- } else {
675
- panHint = {
676
- start: {
677
- x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left,
678
- y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top
679
- },
680
- end: false
681
- }
682
- }
683
-
684
- if (isNaN(delta.x)) delta.x = 0;
685
- if (isNaN(delta.y)) delta.y = 0;
686
-
687
- if (panAxes) {
688
- axes = panAxes;
689
- }
690
-
691
- var axis, axisMin, axisMax, p, d;
692
- Object.keys(axes).forEach(function(axisName) {
693
- axis = axes[axisName];
694
- axisMin = axis.min;
695
- axisMax = axis.max;
696
- opts = axis.options;
697
-
698
- d = delta[axis.direction];
699
- p = prevDelta[axis.direction];
700
-
701
- //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot
702
- if ((!opts.axisPan && panAxes) || (!panAxes && !opts.plotPan)) {
703
- return;
704
- }
705
-
706
- // calc min delta (revealing left edge of plot)
707
- var minD = p + axis.p2c(opts.panRange[0]) - axis.p2c(axisMin);
708
- // calc max delta (revealing right edge of plot)
709
- var maxD = p + axis.p2c(opts.panRange[1]) - axis.p2c(axisMax);
710
- // limit delta to min or max if enabled
711
- if (opts.panRange[0] !== undefined && d >= maxD) d = maxD;
712
- if (opts.panRange[1] !== undefined && d <= minD) d = minD;
713
-
714
- if (d !== 0) {
715
- var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axisMin) - (p - d)) - axis.c2p(axis.p2c(axisMin))),
716
- navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axisMax) - (p - d)) - axis.c2p(axis.p2c(axisMax)));
717
-
718
- if (!isFinite(navigationOffsetBelow)) {
719
- navigationOffsetBelow = 0;
720
- }
721
-
722
- if (!isFinite(navigationOffsetAbove)) {
723
- navigationOffsetAbove = 0;
724
- }
725
-
726
- axis.options.offset.below = saturated.saturate(navigationOffsetBelow + (axis.options.offset.below || 0));
727
- axis.options.offset.above = saturated.saturate(navigationOffsetAbove + (axis.options.offset.above || 0));
728
- }
729
- });
730
-
731
- prevDelta = delta;
732
- plot.setupGrid(true);
733
- plot.draw();
734
-
735
- if (!preventEvent) {
736
- plot.getPlaceholder().trigger("plotpan", [plot, delta, panAxes, initialState]);
737
- }
738
- };
739
-
740
- plot.smartPan.end = function() {
741
- panHint = null;
742
- lockedDirection = null;
743
- prevDelta = { x: 0, y: 0 };
744
- plot.triggerRedrawOverlay();
745
- }
746
-
747
- function shutdown(plot, eventHolder) {
748
- eventHolder.unbind("mousewheel", onMouseWheel);
749
- eventHolder.unbind("mousedown", onMouseDown);
750
- eventHolder.unbind("mouseup", onMouseUp);
751
- eventHolder.unbind("dragstart", onDragStart);
752
- eventHolder.unbind("drag", onDrag);
753
- eventHolder.unbind("dragend", onDragEnd);
754
- eventHolder.unbind("dblclick", onDblClick);
755
- eventHolder.unbind("click", onClick);
756
-
757
- if (panTimeout) clearTimeout(panTimeout);
758
- }
759
-
760
- function drawOverlay(plot, ctx) {
761
- if (panHint) {
762
- ctx.strokeStyle = 'rgba(96, 160, 208, 0.7)';
763
- ctx.lineWidth = 2;
764
- ctx.lineJoin = "round";
765
- var startx = Math.round(panHint.start.x),
766
- starty = Math.round(panHint.start.y),
767
- endx, endy;
768
-
769
- if (panAxes) {
770
- if (panAxes[0].direction === 'x') {
771
- endy = Math.round(panHint.start.y);
772
- endx = Math.round(panHint.end.x);
773
- } else if (panAxes[0].direction === 'y') {
774
- endx = Math.round(panHint.start.x);
775
- endy = Math.round(panHint.end.y);
776
- }
777
- } else {
778
- endx = Math.round(panHint.end.x);
779
- endy = Math.round(panHint.end.y);
780
- }
781
-
782
- ctx.beginPath();
783
-
784
- if (panHint.end === false) {
785
- ctx.moveTo(startx, starty - PANHINT_LENGTH_CONSTANT);
786
- ctx.lineTo(startx, starty + PANHINT_LENGTH_CONSTANT);
787
-
788
- ctx.moveTo(startx + PANHINT_LENGTH_CONSTANT, starty);
789
- ctx.lineTo(startx - PANHINT_LENGTH_CONSTANT, starty);
790
- } else {
791
- var dirX = starty === endy;
792
-
793
- ctx.moveTo(startx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty - (dirX ? PANHINT_LENGTH_CONSTANT : 0));
794
- ctx.lineTo(startx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty + (dirX ? PANHINT_LENGTH_CONSTANT : 0));
795
-
796
- ctx.moveTo(startx, starty);
797
- ctx.lineTo(endx, endy);
798
-
799
- ctx.moveTo(endx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy - (dirX ? PANHINT_LENGTH_CONSTANT : 0));
800
- ctx.lineTo(endx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy + (dirX ? PANHINT_LENGTH_CONSTANT : 0));
801
- }
802
-
803
- ctx.stroke();
804
- }
805
- }
806
-
807
- plot.getTouchedAxis = function(touchPointX, touchPointY) {
808
- var ec = plot.getPlaceholder().offset();
809
- ec.left = touchPointX - ec.left;
810
- ec.top = touchPointY - ec.top;
811
-
812
- var axis = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
813
- var box = axis.box;
814
- if (box !== undefined) {
815
- return (ec.left > box.left) && (ec.left < box.left + box.width) &&
816
- (ec.top > box.top) && (ec.top < box.top + box.height);
817
- }
818
- });
819
-
820
- return axis;
821
- }
822
-
823
- plot.hooks.drawOverlay.push(drawOverlay);
824
- plot.hooks.bindEvents.push(bindEvents);
825
- plot.hooks.shutdown.push(shutdown);
826
- }
827
-
828
- $.plot.plugins.push({
829
- init: init,
830
- options: options,
831
- name: 'navigate',
832
- version: '1.3'
833
- });
834
- })(jQuery);
1
+ /* Flot plugin for adding the ability to pan and zoom the plot.
2
+
3
+ Copyright (c) 2007-2014 IOLA and Ole Laursen.
4
+ Copyright (c) 2016 Ciprian Ceteras.
5
+ Copyright (c) 2017 Raluca Portase.
6
+ Licensed under the MIT license.
7
+
8
+ */
9
+
10
+ /**
11
+ ## jquery.flot.navigate.js
12
+
13
+ This flot plugin is used for adding the ability to pan and zoom the plot.
14
+ A higher level overview is available at [interactions](interactions.md) documentation.
15
+
16
+ The default behaviour is scrollwheel up/down to zoom in, drag
17
+ to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
18
+ plot.pan( offset ) so you easily can add custom controls. It also fires
19
+ "plotpan" and "plotzoom" events, useful for synchronizing plots.
20
+
21
+ The plugin supports these options:
22
+ ```js
23
+ zoom: {
24
+ interactive: false,
25
+ active: false,
26
+ amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
27
+ }
28
+
29
+ pan: {
30
+ interactive: false,
31
+ active: false,
32
+ cursor: "move", // CSS mouse cursor value used when dragging, e.g. "pointer"
33
+ frameRate: 60,
34
+ mode: "smart" // enable smart pan mode
35
+ }
36
+
37
+ xaxis: {
38
+ axisZoom: true, //zoom axis when mouse over it is allowed
39
+ plotZoom: true, //zoom axis is allowed for plot zoom
40
+ axisPan: true, //pan axis when mouse over it is allowed
41
+ plotPan: true, //pan axis is allowed for plot pan
42
+ panRange: [undefined, undefined], // no limit on pan range, or [min, max] in axis units
43
+ zoomRange: [undefined, undefined], // no limit on zoom range, or [closest zoom, furthest zoom] in axis units
44
+ }
45
+
46
+ yaxis: {
47
+ axisZoom: true, //zoom axis when mouse over it is allowed
48
+ plotZoom: true, //zoom axis is allowed for plot zoom
49
+ axisPan: true, //pan axis when mouse over it is allowed
50
+ plotPan: true //pan axis is allowed for plot pan
51
+ panRange: [undefined, undefined], // no limit on pan range, or [min, max] in axis units
52
+ zoomRange: [undefined, undefined], // no limit on zoom range, or [closest zoom, furthest zoom] in axis units
53
+ }
54
+ ```
55
+ **interactive** enables the built-in drag/click behaviour. If you enable
56
+ interactive for pan, then you'll have a basic plot that supports moving
57
+ around; the same for zoom.
58
+
59
+ **active** is true after a touch tap on plot. This enables plot navigation.
60
+ Once activated, zoom and pan cannot be deactivated. When the plot becomes active,
61
+ "plotactivated" event is triggered.
62
+
63
+ **amount** specifies the default amount to zoom in (so 1.5 = 150%) relative to
64
+ the current viewport.
65
+
66
+ **cursor** is a standard CSS mouse cursor string used for visual feedback to the
67
+ user when dragging.
68
+
69
+ **frameRate** specifies the maximum number of times per second the plot will
70
+ update itself while the user is panning around on it (set to null to disable
71
+ intermediate pans, the plot will then not update until the mouse button is
72
+ released).
73
+
74
+ **mode** a string specifies the pan mode for mouse interaction. Accepted values:
75
+ 'manual': no pan hint or direction snapping;
76
+ 'smart': The graph shows pan hint bar and the pan movement will snap
77
+ to one direction when the drag direction is close to it;
78
+ 'smartLock'. The graph shows pan hint bar and the pan movement will always
79
+ snap to a direction that the drag diorection started with.
80
+
81
+ Example API usage:
82
+ ```js
83
+ plot = $.plot(...);
84
+
85
+ // zoom default amount in on the pixel ( 10, 20 )
86
+ plot.zoom({ center: { left: 10, top: 20 } });
87
+
88
+ // zoom out again
89
+ plot.zoomOut({ center: { left: 10, top: 20 } });
90
+
91
+ // zoom 200% in on the pixel (10, 20)
92
+ plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
93
+
94
+ // pan 100 pixels to the left (changing x-range in a positive way) and 20 down
95
+ plot.pan({ left: -100, top: 20 })
96
+ ```
97
+
98
+ Here, "center" specifies where the center of the zooming should happen. Note
99
+ that this is defined in pixel space, not the space of the data points (you can
100
+ use the p2c helpers on the axes in Flot to help you convert between these).
101
+
102
+ **amount** is the amount to zoom the viewport relative to the current range, so
103
+ 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You
104
+ can set the default in the options.
105
+ */
106
+
107
+ /* eslint-enable */
108
+ (function($) {
109
+ 'use strict';
110
+
111
+ var options = {
112
+ zoom: {
113
+ interactive: false,
114
+ active: false,
115
+ amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
116
+ },
117
+ pan: {
118
+ interactive: false,
119
+ active: false,
120
+ cursor: "move",
121
+ frameRate: 60,
122
+ mode: 'smart'
123
+ },
124
+ recenter: {
125
+ interactive: true
126
+ },
127
+ xaxis: {
128
+ axisZoom: true, //zoom axis when mouse over it is allowed
129
+ plotZoom: true, //zoom axis is allowed for plot zoom
130
+ axisPan: true, //pan axis when mouse over it is allowed
131
+ plotPan: true, //pan axis is allowed for plot pan
132
+ panRange: [undefined, undefined], // no limit on pan range, or [min, max] in axis units
133
+ zoomRange: [undefined, undefined] // no limit on zoom range, or [closest zoom, furthest zoom] in axis units
134
+ },
135
+ yaxis: {
136
+ axisZoom: true,
137
+ plotZoom: true,
138
+ axisPan: true,
139
+ plotPan: true,
140
+ panRange: [undefined, undefined], // no limit on pan range, or [min, max] in axis units
141
+ zoomRange: [undefined, undefined] // no limit on zoom range, or [closest zoom, furthest zoom] in axis units
142
+ }
143
+ };
144
+
145
+ var saturated = $.plot.saturated;
146
+ var browser = $.plot.browser;
147
+ var SNAPPING_CONSTANT = $.plot.uiConstants.SNAPPING_CONSTANT;
148
+ var PANHINT_LENGTH_CONSTANT = $.plot.uiConstants.PANHINT_LENGTH_CONSTANT;
149
+
150
+ function init(plot) {
151
+ plot.hooks.processOptions.push(initNevigation);
152
+ }
153
+
154
+ function initNevigation(plot, options) {
155
+ var panAxes = null;
156
+ var canDrag = false;
157
+ var useManualPan = options.pan.mode === 'manual',
158
+ smartPanLock = options.pan.mode === 'smartLock',
159
+ useSmartPan = smartPanLock || options.pan.mode === 'smart';
160
+
161
+ function onZoomClick(e, zoomOut, amount) {
162
+ var page = browser.getPageXY(e);
163
+
164
+ var c = plot.offset();
165
+ c.left = page.X - c.left;
166
+ c.top = page.Y - c.top;
167
+
168
+ var ec = plot.getPlaceholder().offset();
169
+ ec.left = page.X - ec.left;
170
+ ec.top = page.Y - ec.top;
171
+
172
+ var axes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
173
+ var box = axis.box;
174
+ if (box !== undefined) {
175
+ return (ec.left > box.left) && (ec.left < box.left + box.width) &&
176
+ (ec.top > box.top) && (ec.top < box.top + box.height);
177
+ }
178
+ });
179
+
180
+ if (axes.length === 0) {
181
+ axes = undefined;
182
+ }
183
+
184
+ if (zoomOut) {
185
+ plot.zoomOut({
186
+ center: c,
187
+ axes: axes,
188
+ amount: amount
189
+ });
190
+ } else {
191
+ plot.zoom({
192
+ center: c,
193
+ axes: axes,
194
+ amount: amount
195
+ });
196
+ }
197
+ }
198
+
199
+ var prevCursor = 'default',
200
+ panHint = null,
201
+ panTimeout = null,
202
+ plotState,
203
+ prevDragPosition = { x: 0, y: 0 },
204
+ isPanAction = false;
205
+
206
+ function onMouseWheel(e, delta) {
207
+ var maxAbsoluteDeltaOnMac = 1,
208
+ isMacScroll = Math.abs(e.originalEvent.deltaY) <= maxAbsoluteDeltaOnMac,
209
+ defaultNonMacScrollAmount = null,
210
+ macMagicRatio = 50,
211
+ amount = isMacScroll ? 1 + Math.abs(e.originalEvent.deltaY) / macMagicRatio : defaultNonMacScrollAmount;
212
+
213
+ if (isPanAction) {
214
+ onDragEnd(e);
215
+ }
216
+
217
+ if (plot.getOptions().zoom.active) {
218
+ e.preventDefault();
219
+ onZoomClick(e, delta < 0, amount);
220
+ return false;
221
+ }
222
+ }
223
+
224
+ plot.navigationState = function(startPageX, startPageY) {
225
+ var axes = this.getAxes();
226
+ var result = {};
227
+ Object.keys(axes).forEach(function(axisName) {
228
+ var axis = axes[axisName];
229
+ result[axisName] = {
230
+ navigationOffset: { below: axis.options.offset.below || 0,
231
+ above: axis.options.offset.above || 0},
232
+ axisMin: axis.min,
233
+ axisMax: axis.max,
234
+ diagMode: false
235
+ }
236
+ });
237
+
238
+ result.startPageX = startPageX || 0;
239
+ result.startPageY = startPageY || 0;
240
+ return result;
241
+ }
242
+
243
+ function onMouseDown(e) {
244
+ canDrag = true;
245
+ }
246
+
247
+ function onMouseUp(e) {
248
+ canDrag = false;
249
+ }
250
+
251
+ function isLeftMouseButtonPressed(e) {
252
+ return e.button === 0;
253
+ }
254
+
255
+ function onDragStart(e) {
256
+ if (!canDrag || !isLeftMouseButtonPressed(e)) {
257
+ return false;
258
+ }
259
+
260
+ isPanAction = true;
261
+ var page = browser.getPageXY(e);
262
+
263
+ var ec = plot.getPlaceholder().offset();
264
+ ec.left = page.X - ec.left;
265
+ ec.top = page.Y - ec.top;
266
+
267
+ panAxes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
268
+ var box = axis.box;
269
+ if (box !== undefined) {
270
+ return (ec.left > box.left) && (ec.left < box.left + box.width) &&
271
+ (ec.top > box.top) && (ec.top < box.top + box.height);
272
+ }
273
+ });
274
+
275
+ if (panAxes.length === 0) {
276
+ panAxes = undefined;
277
+ }
278
+
279
+ var c = plot.getPlaceholder().css('cursor');
280
+ if (c) {
281
+ prevCursor = c;
282
+ }
283
+
284
+ plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
285
+
286
+ if (useSmartPan) {
287
+ plotState = plot.navigationState(page.X, page.Y);
288
+ } else if (useManualPan) {
289
+ prevDragPosition.x = page.X;
290
+ prevDragPosition.y = page.Y;
291
+ }
292
+ }
293
+
294
+ function onDrag(e) {
295
+ if (!isPanAction) {
296
+ return;
297
+ }
298
+
299
+ var page = browser.getPageXY(e);
300
+ var frameRate = plot.getOptions().pan.frameRate;
301
+
302
+ if (frameRate === -1) {
303
+ if (useSmartPan) {
304
+ plot.smartPan({
305
+ x: plotState.startPageX - page.X,
306
+ y: plotState.startPageY - page.Y
307
+ }, plotState, panAxes, false, smartPanLock);
308
+ } else if (useManualPan) {
309
+ plot.pan({
310
+ left: prevDragPosition.x - page.X,
311
+ top: prevDragPosition.y - page.Y,
312
+ axes: panAxes
313
+ });
314
+ prevDragPosition.x = page.X;
315
+ prevDragPosition.y = page.Y;
316
+ }
317
+ return;
318
+ }
319
+
320
+ if (panTimeout || !frameRate) return;
321
+
322
+ panTimeout = setTimeout(function() {
323
+ if (useSmartPan) {
324
+ plot.smartPan({
325
+ x: plotState.startPageX - page.X,
326
+ y: plotState.startPageY - page.Y
327
+ }, plotState, panAxes, false, smartPanLock);
328
+ } else if (useManualPan) {
329
+ plot.pan({
330
+ left: prevDragPosition.x - page.X,
331
+ top: prevDragPosition.y - page.Y,
332
+ axes: panAxes
333
+ });
334
+ prevDragPosition.x = page.X;
335
+ prevDragPosition.y = page.Y;
336
+ }
337
+
338
+ panTimeout = null;
339
+ }, 1 / frameRate * 1000);
340
+ }
341
+
342
+ function onDragEnd(e) {
343
+ if (!isPanAction) {
344
+ return;
345
+ }
346
+
347
+ if (panTimeout) {
348
+ clearTimeout(panTimeout);
349
+ panTimeout = null;
350
+ }
351
+
352
+ isPanAction = false;
353
+ var page = browser.getPageXY(e);
354
+
355
+ plot.getPlaceholder().css('cursor', prevCursor);
356
+
357
+ if (useSmartPan) {
358
+ plot.smartPan({
359
+ x: plotState.startPageX - page.X,
360
+ y: plotState.startPageY - page.Y
361
+ }, plotState, panAxes, false, smartPanLock);
362
+ plot.smartPan.end();
363
+ } else if (useManualPan) {
364
+ plot.pan({
365
+ left: prevDragPosition.x - page.X,
366
+ top: prevDragPosition.y - page.Y,
367
+ axes: panAxes
368
+ });
369
+ prevDragPosition.x = 0;
370
+ prevDragPosition.y = 0;
371
+ }
372
+ }
373
+
374
+ function onDblClick(e) {
375
+ plot.activate();
376
+ var o = plot.getOptions()
377
+
378
+ if (!o.recenter.interactive) { return; }
379
+
380
+ var axes = plot.getTouchedAxis(e.clientX, e.clientY),
381
+ event;
382
+
383
+ plot.recenter({ axes: axes[0] ? axes : null });
384
+
385
+ if (axes[0]) {
386
+ event = new $.Event('re-center', { detail: {
387
+ axisTouched: axes[0]
388
+ }});
389
+ } else {
390
+ event = new $.Event('re-center', { detail: e });
391
+ }
392
+ plot.getPlaceholder().trigger(event);
393
+ }
394
+
395
+ function onClick(e) {
396
+ plot.activate();
397
+
398
+ if (isPanAction) {
399
+ onDragEnd(e);
400
+ }
401
+
402
+ return false;
403
+ }
404
+
405
+ plot.activate = function() {
406
+ var o = plot.getOptions();
407
+ if (!o.pan.active || !o.zoom.active) {
408
+ o.pan.active = true;
409
+ o.zoom.active = true;
410
+ plot.getPlaceholder().trigger("plotactivated", [plot]);
411
+ }
412
+ }
413
+
414
+ function bindEvents(plot, eventHolder) {
415
+ var o = plot.getOptions();
416
+ if (o.zoom.interactive) {
417
+ eventHolder.mousewheel(onMouseWheel);
418
+ }
419
+
420
+ if (o.pan.interactive) {
421
+ plot.addEventHandler("dragstart", onDragStart, eventHolder, 0);
422
+ plot.addEventHandler("drag", onDrag, eventHolder, 0);
423
+ plot.addEventHandler("dragend", onDragEnd, eventHolder, 0);
424
+ eventHolder.bind("mousedown", onMouseDown);
425
+ eventHolder.bind("mouseup", onMouseUp);
426
+ }
427
+
428
+ eventHolder.dblclick(onDblClick);
429
+ eventHolder.click(onClick);
430
+ }
431
+
432
+ plot.zoomOut = function(args) {
433
+ if (!args) {
434
+ args = {};
435
+ }
436
+
437
+ if (!args.amount) {
438
+ args.amount = plot.getOptions().zoom.amount;
439
+ }
440
+
441
+ args.amount = 1 / args.amount;
442
+ plot.zoom(args);
443
+ };
444
+
445
+ plot.zoom = function(args) {
446
+ if (!args) {
447
+ args = {};
448
+ }
449
+
450
+ var c = args.center,
451
+ amount = args.amount || plot.getOptions().zoom.amount,
452
+ w = plot.width(),
453
+ h = plot.height(),
454
+ axes = args.axes || plot.getAxes();
455
+
456
+ if (!c) {
457
+ c = {
458
+ left: w / 2,
459
+ top: h / 2
460
+ };
461
+ }
462
+
463
+ var xf = c.left / w,
464
+ yf = c.top / h,
465
+ minmax = {
466
+ x: {
467
+ min: c.left - xf * w / amount,
468
+ max: c.left + (1 - xf) * w / amount
469
+ },
470
+ y: {
471
+ min: c.top - yf * h / amount,
472
+ max: c.top + (1 - yf) * h / amount
473
+ }
474
+ };
475
+
476
+ for (var key in axes) {
477
+ if (!axes.hasOwnProperty(key)) {
478
+ continue;
479
+ }
480
+
481
+ var axis = axes[key],
482
+ opts = axis.options,
483
+ min = minmax[axis.direction].min,
484
+ max = minmax[axis.direction].max,
485
+ navigationOffset = axis.options.offset;
486
+
487
+ //skip axis without axisZoom when zooming only on certain axis or axis without plotZoom for zoom on entire plot
488
+ if ((!opts.axisZoom && args.axes) || (!args.axes && !opts.plotZoom)) {
489
+ continue;
490
+ }
491
+
492
+ min = $.plot.saturated.saturate(axis.c2p(min));
493
+ max = $.plot.saturated.saturate(axis.c2p(max));
494
+ if (min > max) {
495
+ // make sure min < max
496
+ var tmp = min;
497
+ min = max;
498
+ max = tmp;
499
+ }
500
+
501
+ // test for zoom limits zoomRange: [min,max]
502
+ if (opts.zoomRange) {
503
+ // zoomed in too far
504
+ if (max - min < opts.zoomRange[0]) {
505
+ continue;
506
+ }
507
+ // zoomed out to far
508
+ if (max - min > opts.zoomRange[1]) {
509
+ continue;
510
+ }
511
+ }
512
+
513
+ var offsetBelow = $.plot.saturated.saturate(navigationOffset.below - (axis.min - min));
514
+ var offsetAbove = $.plot.saturated.saturate(navigationOffset.above - (axis.max - max));
515
+ opts.offset = { below: offsetBelow, above: offsetAbove };
516
+ };
517
+
518
+ plot.setupGrid(true);
519
+ plot.draw();
520
+
521
+ if (!args.preventEvent) {
522
+ plot.getPlaceholder().trigger("plotzoom", [plot, args]);
523
+ }
524
+ };
525
+
526
+ plot.pan = function(args) {
527
+ var delta = {
528
+ x: +args.left,
529
+ y: +args.top
530
+ };
531
+
532
+ if (isNaN(delta.x)) delta.x = 0;
533
+ if (isNaN(delta.y)) delta.y = 0;
534
+
535
+ $.each(args.axes || plot.getAxes(), function(_, axis) {
536
+ var opts = axis.options,
537
+ d = delta[axis.direction];
538
+
539
+ //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot
540
+ if ((!opts.axisPan && args.axes) || (!opts.plotPan && !args.axes)) {
541
+ return;
542
+ }
543
+
544
+ // calc min delta (revealing left edge of plot)
545
+ var minD = axis.p2c(opts.panRange[0]) - axis.p2c(axis.min);
546
+ // calc max delta (revealing right edge of plot)
547
+ var maxD = axis.p2c(opts.panRange[1]) - axis.p2c(axis.max);
548
+ // limit delta to min or max if enabled
549
+ if (opts.panRange[0] !== undefined && d >= maxD) d = maxD;
550
+ if (opts.panRange[1] !== undefined && d <= minD) d = minD;
551
+
552
+ if (d !== 0) {
553
+ var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axis.min) + d) - axis.c2p(axis.p2c(axis.min))),
554
+ navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axis.max) + d) - axis.c2p(axis.p2c(axis.max)));
555
+
556
+ if (!isFinite(navigationOffsetBelow)) {
557
+ navigationOffsetBelow = 0;
558
+ }
559
+
560
+ if (!isFinite(navigationOffsetAbove)) {
561
+ navigationOffsetAbove = 0;
562
+ }
563
+
564
+ opts.offset = {
565
+ below: saturated.saturate(navigationOffsetBelow + (opts.offset.below || 0)),
566
+ above: saturated.saturate(navigationOffsetAbove + (opts.offset.above || 0))
567
+ };
568
+ }
569
+ });
570
+
571
+ plot.setupGrid(true);
572
+ plot.draw();
573
+ if (!args.preventEvent) {
574
+ plot.getPlaceholder().trigger("plotpan", [plot, args]);
575
+ }
576
+ };
577
+
578
+ plot.recenter = function(args) {
579
+ $.each(args.axes || plot.getAxes(), function(_, axis) {
580
+ if (args.axes) {
581
+ if (this.direction === 'x') {
582
+ axis.options.offset = { below: 0 };
583
+ } else if (this.direction === 'y') {
584
+ axis.options.offset = { above: 0 };
585
+ }
586
+ } else {
587
+ axis.options.offset = { below: 0, above: 0 };
588
+ }
589
+ });
590
+ plot.setupGrid(true);
591
+ plot.draw();
592
+ };
593
+
594
+ var shouldSnap = function(delta) {
595
+ return (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) ||
596
+ (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT);
597
+ }
598
+
599
+ // adjust delta so the pan action is constrained on the vertical or horizontal direction
600
+ // it the movements in the other direction are small
601
+ var adjustDeltaToSnap = function(delta) {
602
+ if (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT) {
603
+ return {x: 0, y: delta.y};
604
+ }
605
+
606
+ if (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) {
607
+ return {x: delta.x, y: 0};
608
+ }
609
+
610
+ return delta;
611
+ }
612
+
613
+ var lockedDirection = null;
614
+ var lockDeltaDirection = function(delta) {
615
+ if (!lockedDirection && Math.max(Math.abs(delta.x), Math.abs(delta.y)) >= SNAPPING_CONSTANT) {
616
+ lockedDirection = Math.abs(delta.x) < Math.abs(delta.y) ? 'y' : 'x';
617
+ }
618
+
619
+ switch (lockedDirection) {
620
+ case 'x':
621
+ return { x: delta.x, y: 0 };
622
+ case 'y':
623
+ return { x: 0, y: delta.y };
624
+ default:
625
+ return { x: 0, y: 0 };
626
+ }
627
+ }
628
+
629
+ var isDiagonalMode = function(delta) {
630
+ if (Math.abs(delta.x) > 0 && Math.abs(delta.y) > 0) {
631
+ return true;
632
+ }
633
+ return false;
634
+ }
635
+
636
+ var restoreAxisOffset = function(axes, initialState, delta) {
637
+ var axis;
638
+ Object.keys(axes).forEach(function(axisName) {
639
+ axis = axes[axisName];
640
+ if (delta[axis.direction] === 0) {
641
+ axis.options.offset.below = initialState[axisName].navigationOffset.below;
642
+ axis.options.offset.above = initialState[axisName].navigationOffset.above;
643
+ }
644
+ });
645
+ }
646
+
647
+ var prevDelta = { x: 0, y: 0 };
648
+ plot.smartPan = function(delta, initialState, panAxes, preventEvent, smartLock) {
649
+ var snap = smartLock ? true : shouldSnap(delta),
650
+ axes = plot.getAxes(),
651
+ opts;
652
+ delta = smartLock ? lockDeltaDirection(delta) : adjustDeltaToSnap(delta);
653
+
654
+ if (isDiagonalMode(delta)) {
655
+ initialState.diagMode = true;
656
+ }
657
+
658
+ if (snap && initialState.diagMode === true) {
659
+ initialState.diagMode = false;
660
+ restoreAxisOffset(axes, initialState, delta);
661
+ }
662
+
663
+ if (snap) {
664
+ panHint = {
665
+ start: {
666
+ x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left,
667
+ y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top
668
+ },
669
+ end: {
670
+ x: initialState.startPageX - delta.x - plot.offset().left + plot.getPlotOffset().left,
671
+ y: initialState.startPageY - delta.y - plot.offset().top + plot.getPlotOffset().top
672
+ }
673
+ }
674
+ } else {
675
+ panHint = {
676
+ start: {
677
+ x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left,
678
+ y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top
679
+ },
680
+ end: false
681
+ }
682
+ }
683
+
684
+ if (isNaN(delta.x)) delta.x = 0;
685
+ if (isNaN(delta.y)) delta.y = 0;
686
+
687
+ if (panAxes) {
688
+ axes = panAxes;
689
+ }
690
+
691
+ var axis, axisMin, axisMax, p, d;
692
+ Object.keys(axes).forEach(function(axisName) {
693
+ axis = axes[axisName];
694
+ axisMin = axis.min;
695
+ axisMax = axis.max;
696
+ opts = axis.options;
697
+
698
+ d = delta[axis.direction];
699
+ p = prevDelta[axis.direction];
700
+
701
+ //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot
702
+ if ((!opts.axisPan && panAxes) || (!panAxes && !opts.plotPan)) {
703
+ return;
704
+ }
705
+
706
+ // calc min delta (revealing left edge of plot)
707
+ var minD = p + axis.p2c(opts.panRange[0]) - axis.p2c(axisMin);
708
+ // calc max delta (revealing right edge of plot)
709
+ var maxD = p + axis.p2c(opts.panRange[1]) - axis.p2c(axisMax);
710
+ // limit delta to min or max if enabled
711
+ if (opts.panRange[0] !== undefined && d >= maxD) d = maxD;
712
+ if (opts.panRange[1] !== undefined && d <= minD) d = minD;
713
+
714
+ if (d !== 0) {
715
+ var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axisMin) - (p - d)) - axis.c2p(axis.p2c(axisMin))),
716
+ navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axisMax) - (p - d)) - axis.c2p(axis.p2c(axisMax)));
717
+
718
+ if (!isFinite(navigationOffsetBelow)) {
719
+ navigationOffsetBelow = 0;
720
+ }
721
+
722
+ if (!isFinite(navigationOffsetAbove)) {
723
+ navigationOffsetAbove = 0;
724
+ }
725
+
726
+ axis.options.offset.below = saturated.saturate(navigationOffsetBelow + (axis.options.offset.below || 0));
727
+ axis.options.offset.above = saturated.saturate(navigationOffsetAbove + (axis.options.offset.above || 0));
728
+ }
729
+ });
730
+
731
+ prevDelta = delta;
732
+ plot.setupGrid(true);
733
+ plot.draw();
734
+
735
+ if (!preventEvent) {
736
+ plot.getPlaceholder().trigger("plotpan", [plot, delta, panAxes, initialState]);
737
+ }
738
+ };
739
+
740
+ plot.smartPan.end = function() {
741
+ panHint = null;
742
+ lockedDirection = null;
743
+ prevDelta = { x: 0, y: 0 };
744
+ plot.triggerRedrawOverlay();
745
+ }
746
+
747
+ function shutdown(plot, eventHolder) {
748
+ eventHolder.unbind("mousewheel", onMouseWheel);
749
+ eventHolder.unbind("mousedown", onMouseDown);
750
+ eventHolder.unbind("mouseup", onMouseUp);
751
+ eventHolder.unbind("dragstart", onDragStart);
752
+ eventHolder.unbind("drag", onDrag);
753
+ eventHolder.unbind("dragend", onDragEnd);
754
+ eventHolder.unbind("dblclick", onDblClick);
755
+ eventHolder.unbind("click", onClick);
756
+
757
+ if (panTimeout) clearTimeout(panTimeout);
758
+ }
759
+
760
+ function drawOverlay(plot, ctx) {
761
+ if (panHint) {
762
+ ctx.strokeStyle = 'rgba(96, 160, 208, 0.7)';
763
+ ctx.lineWidth = 2;
764
+ ctx.lineJoin = "round";
765
+ var startx = Math.round(panHint.start.x),
766
+ starty = Math.round(panHint.start.y),
767
+ endx, endy;
768
+
769
+ if (panAxes) {
770
+ if (panAxes[0].direction === 'x') {
771
+ endy = Math.round(panHint.start.y);
772
+ endx = Math.round(panHint.end.x);
773
+ } else if (panAxes[0].direction === 'y') {
774
+ endx = Math.round(panHint.start.x);
775
+ endy = Math.round(panHint.end.y);
776
+ }
777
+ } else {
778
+ endx = Math.round(panHint.end.x);
779
+ endy = Math.round(panHint.end.y);
780
+ }
781
+
782
+ ctx.beginPath();
783
+
784
+ if (panHint.end === false) {
785
+ ctx.moveTo(startx, starty - PANHINT_LENGTH_CONSTANT);
786
+ ctx.lineTo(startx, starty + PANHINT_LENGTH_CONSTANT);
787
+
788
+ ctx.moveTo(startx + PANHINT_LENGTH_CONSTANT, starty);
789
+ ctx.lineTo(startx - PANHINT_LENGTH_CONSTANT, starty);
790
+ } else {
791
+ var dirX = starty === endy;
792
+
793
+ ctx.moveTo(startx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty - (dirX ? PANHINT_LENGTH_CONSTANT : 0));
794
+ ctx.lineTo(startx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty + (dirX ? PANHINT_LENGTH_CONSTANT : 0));
795
+
796
+ ctx.moveTo(startx, starty);
797
+ ctx.lineTo(endx, endy);
798
+
799
+ ctx.moveTo(endx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy - (dirX ? PANHINT_LENGTH_CONSTANT : 0));
800
+ ctx.lineTo(endx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy + (dirX ? PANHINT_LENGTH_CONSTANT : 0));
801
+ }
802
+
803
+ ctx.stroke();
804
+ }
805
+ }
806
+
807
+ plot.getTouchedAxis = function(touchPointX, touchPointY) {
808
+ var ec = plot.getPlaceholder().offset();
809
+ ec.left = touchPointX - ec.left;
810
+ ec.top = touchPointY - ec.top;
811
+
812
+ var axis = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) {
813
+ var box = axis.box;
814
+ if (box !== undefined) {
815
+ return (ec.left > box.left) && (ec.left < box.left + box.width) &&
816
+ (ec.top > box.top) && (ec.top < box.top + box.height);
817
+ }
818
+ });
819
+
820
+ return axis;
821
+ }
822
+
823
+ plot.hooks.drawOverlay.push(drawOverlay);
824
+ plot.hooks.bindEvents.push(bindEvents);
825
+ plot.hooks.shutdown.push(shutdown);
826
+ }
827
+
828
+ $.plot.plugins.push({
829
+ init: init,
830
+ options: options,
831
+ name: 'navigate',
832
+ version: '1.3'
833
+ });
834
+ })(jQuery);