@kevinburke/flot 5.1.0 → 5.1.2

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.
@@ -1,4 +1,4 @@
1
- /*! @kevinburke/flot v5.1.0 | MIT License | https://github.com/kevinburke/flot */
1
+ /*! @kevinburke/flot v5.1.2 | MIT License | https://github.com/kevinburke/flot */
2
2
  (function ($) {
3
3
  'use strict';
4
4
 
@@ -24,7 +24,7 @@
24
24
  isSafari: function() {
25
25
  var top = window.top;
26
26
  if (!top) return false;
27
- return /constructor/i.test(top.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!top['safari'] || (typeof top.safari !== 'undefined' && top.safari.pushNotification));
27
+ return /constructor/i.test(/** @type {any} */ (top.HTMLElement)) || (function (/** @type {any} */ p) { return p.toString() === "[object SafariRemoteNotification]"; })(!(/** @type {any} */ (top))['safari'] || (typeof (/** @type {any} */ (top)).safari !== 'undefined' && (/** @type {any} */ (top)).safari.pushNotification));
28
28
  },
29
29
 
30
30
  isMobileSafari: function() {
@@ -105,6 +105,9 @@
105
105
  var keys = Object.keys(src);
106
106
  for (var k = 0; k < keys.length; k++) {
107
107
  var key = keys[k];
108
+ if (key === '__proto__' || key === 'constructor') {
109
+ continue;
110
+ }
108
111
  var val = src[key];
109
112
  if (val === undefined) {
110
113
  continue;
@@ -171,10 +174,11 @@
171
174
  }
172
175
  }
173
176
 
174
- // Trigger a custom event on an element. Extra args are passed as the
175
- // event's `detail` property (an array). For jQuery adapter compatibility,
176
- // the adapter re-dispatches these as jQuery events so $(el).on() works.
177
- function trigger(el, type, args) {
177
+ // Default trigger: dispatches a native CustomEvent with extra args stashed
178
+ // on `event.detail` (an array). The jQuery adapter overrides this via
179
+ // setTrigger so handlers bound with $(el).on(type, fn) receive the extra
180
+ // args as positional parameters, matching upstream flot/flot behavior.
181
+ var triggerImpl = function(el, type, args) {
178
182
  var event = new CustomEvent(type, {
179
183
  detail: args || [],
180
184
  bubbles: true,
@@ -182,6 +186,14 @@
182
186
  });
183
187
  el.dispatchEvent(event);
184
188
  return event;
189
+ };
190
+
191
+ function trigger(el, type, args) {
192
+ return triggerImpl(el, type, args);
193
+ }
194
+
195
+ function setTrigger(fn) {
196
+ triggerImpl = fn;
185
197
  }
186
198
 
187
199
  // Bind an event listener, tracking it so unbindAll can remove it later.
@@ -444,7 +456,9 @@
444
456
  layer.style.left = '0px';
445
457
  layer.style.bottom = '0px';
446
458
  layer.style.right = '0px';
447
- svgElement.appendChild(layer);
459
+ if (svgElement) {
460
+ svgElement.appendChild(layer);
461
+ }
448
462
  this.SVG[classes] = layer;
449
463
  }
450
464
 
@@ -538,8 +552,8 @@
538
552
 
539
553
  element.style.position = 'absolute';
540
554
  element.style.maxWidth = width;
541
- element.setAttributeNS(null, 'x', -9999);
542
- element.setAttributeNS(null, 'y', -9999);
555
+ element.setAttributeNS(null, 'x', String(-9999));
556
+ element.setAttributeNS(null, 'y', String(-9999));
543
557
 
544
558
  if (typeof font === 'object') {
545
559
  element.style.font = textStyle;
@@ -563,7 +577,9 @@
563
577
  while (element.firstChild) {
564
578
  element.removeChild(element.firstChild);
565
579
  }
566
- element.parentNode.removeChild(element);
580
+ if (element.parentNode) {
581
+ element.parentNode.removeChild(element);
582
+ }
567
583
  }
568
584
 
569
585
  info.measured = true;
@@ -719,10 +735,16 @@
719
735
  var styleCache = layerCache[styleKey];
720
736
  for (var key in styleCache) {
721
737
  if (Object.prototype.hasOwnProperty.call(styleCache, key)) {
738
+ // styleCache entries can exist without a
739
+ // positions array (e.g. when a Flot plugin
740
+ // populates the cache outside the normal
741
+ // addText path). Upstream flot/flot#1444.
722
742
  var positions = styleCache[key].positions;
723
- positions.forEach(function(position) {
724
- position.active = false;
725
- });
743
+ if (positions != null) {
744
+ positions.forEach(function(position) {
745
+ position.active = false;
746
+ });
747
+ }
726
748
  }
727
749
  }
728
750
  }
@@ -3361,8 +3383,11 @@
3361
3383
  };
3362
3384
 
3363
3385
  // we might need an extra decimal since forced
3364
- // ticks don't necessarily fit naturally
3365
- if (!axis.mode && opts.tickDecimals == null) {
3386
+ // ticks don't necessarily fit naturally.
3387
+ // Guard against axis.delta <= 0 (min == max): Math.log(0)
3388
+ // is -Infinity, so extraDec would be +Infinity and
3389
+ // toFixed(Infinity) throws. Upstream #1869 / PR #1870.
3390
+ if (!axis.mode && opts.tickDecimals == null && axis.delta > 0) {
3366
3391
  var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1),
3367
3392
  ts = axis.tickGenerator(axis, plot);
3368
3393
 
@@ -4269,7 +4294,13 @@
4269
4294
  maxy = maxDistance / series.yaxis.scale,
4270
4295
  points = series.datapoints.points,
4271
4296
  ps = series.datapoints.pointsize,
4272
- smallestDistance = Number.POSITIVE_INFINITY;
4297
+ // Seed with maxDistance (or its square, matching the
4298
+ // default squared-distance metric) so points outside
4299
+ // the hover radius are never selected. Without this,
4300
+ // the maxx/maxy coordinate-space pre-filter is the
4301
+ // only radius check, and it's disabled for axes with
4302
+ // inverseTransform — see upstream flot/flot#1871.
4303
+ smallestDistance = computeDistance ? maxDistance : maxDistance * maxDistance;
4273
4304
 
4274
4305
  // with inverse transforms, we can't use the maxx/maxy
4275
4306
  // optimization, sadly
@@ -4469,7 +4500,16 @@
4469
4500
  } else {
4470
4501
  // assume this is a gradient spec; IE currently only
4471
4502
  // supports a simple vertical gradient properly, so that's
4472
- // what we support too
4503
+ // what we support too.
4504
+ // createLinearGradient throws if any coordinate is NaN or
4505
+ // ±Infinity (e.g. when the plot container has zero size
4506
+ // or the user supplies bogus bounds) — fall back to the
4507
+ // default solid color instead. Global isFinite coerces
4508
+ // null → 0 (finite), matching the drawSeriesPoints path
4509
+ // that passes (null, null). Upstream flot/flot#1867.
4510
+ if (!isFinite(top) || !isFinite(bottom)) {
4511
+ return defaultColor;
4512
+ }
4473
4513
  var gradient = ctx.createLinearGradient(0, top, 0, bottom);
4474
4514
 
4475
4515
  for (var i = 0, l = spec.colors.length; i < l; ++i) {
@@ -4497,7 +4537,7 @@
4497
4537
  // Plugin registry. Plugins push to this array to register themselves.
4498
4538
  var plugins = [];
4499
4539
 
4500
- var version = "5.1.0";
4540
+ var version = "5.1.2";
4501
4541
 
4502
4542
  // The main plot function.
4503
4543
  function plot(placeholder, data, options) {
@@ -5758,6 +5798,17 @@
5758
5798
  var minD = axis.p2c(opts.panRange[0]) - axis.p2c(axis.min);
5759
5799
  // calc max delta (revealing right edge of plot)
5760
5800
  var maxD = axis.p2c(opts.panRange[1]) - axis.p2c(axis.max);
5801
+ // For the y-axis, screen coordinates are inverted
5802
+ // (p2c(smaller v) > p2c(larger v)), so minD/maxD end up
5803
+ // with the opposite signs from the x-axis case. Swap
5804
+ // them so the clamp comparisons below keep their
5805
+ // x-axis semantics. Upstream flot/flot#1789, ports the
5806
+ // minimal form of PR #1793.
5807
+ if (axis.direction === 'y') {
5808
+ var swap = minD;
5809
+ minD = maxD;
5810
+ maxD = swap;
5811
+ }
5761
5812
  // limit delta to min or max if enabled
5762
5813
  if (opts.panRange[0] !== undefined && d >= maxD) d = maxD;
5763
5814
  if (opts.panRange[1] !== undefined && d <= minD) d = minD;
@@ -5922,6 +5973,13 @@
5922
5973
  var minD = p + axis.p2c(opts.panRange[0]) - axis.p2c(axisMin);
5923
5974
  // calc max delta (revealing right edge of plot)
5924
5975
  var maxD = p + axis.p2c(opts.panRange[1]) - axis.p2c(axisMax);
5976
+ // Same y-axis swap as plot.pan — see comment there.
5977
+ // Upstream flot/flot#1789 / PR #1793.
5978
+ if (axis.direction === 'y') {
5979
+ var swap = minD;
5980
+ minD = maxD;
5981
+ maxD = swap;
5982
+ }
5925
5983
  // limit delta to min or max if enabled
5926
5984
  if (opts.panRange[0] !== undefined && d >= maxD) d = maxD;
5927
5985
  if (opts.panRange[1] !== undefined && d <= minD) d = minD;
@@ -5975,7 +6033,7 @@
5975
6033
  ctx.lineJoin = "round";
5976
6034
  var startx = Math.round(panHint.start.x),
5977
6035
  starty = Math.round(panHint.start.y),
5978
- endx, endy;
6036
+ endx = 0, endy = 0;
5979
6037
 
5980
6038
  if (panAxes) {
5981
6039
  if (panAxes[0].direction === 'x') {
@@ -6740,13 +6798,14 @@
6740
6798
  }
6741
6799
 
6742
6800
  function initTouchNavigation$1(plot, options) {
6801
+ /** @type {{ zoomEnable: boolean, prevDistance: number | null, prevTapTime: number, prevPanPosition: {x: number, y: number}, prevTapPosition: {x: number, y: number} }} */
6743
6802
  var gestureState = {
6744
6803
  zoomEnable: false,
6745
6804
  prevDistance: null,
6746
- prevTapTime: 0,
6747
6805
  prevPanPosition: { x: 0, y: 0 },
6748
6806
  prevTapPosition: { x: 0, y: 0 }
6749
6807
  },
6808
+ /** @type {{ prevTouchedAxis: string, currentTouchedAxis: string, touchedAxis: any, navigationConstraint: string, initialState: any }} */
6750
6809
  navigationState = {
6751
6810
  prevTouchedAxis: 'none',
6752
6811
  currentTouchedAxis: 'none',
@@ -6803,7 +6862,7 @@
6803
6862
  drag: function(e) {
6804
6863
  presetNavigationState(e, 'pan');
6805
6864
 
6806
- if (useSmartPan) {
6865
+ if (useSmartPan && navigationState.initialState) {
6807
6866
  var point = getPoint(e, 'pan');
6808
6867
  plot.smartPan({
6809
6868
  x: navigationState.initialState.startPageX - point.x,
@@ -6827,7 +6886,7 @@
6827
6886
  }
6828
6887
 
6829
6888
  if (wasPinchEvent(e, gestureState)) {
6830
- updateprevPanPosition(e, 'pan', gestureState, navigationState);
6889
+ updatePrevPanPosition(e, 'pan', gestureState, navigationState);
6831
6890
  }
6832
6891
  }
6833
6892
  };
@@ -6859,7 +6918,7 @@
6859
6918
 
6860
6919
  var dist = pinchDistance(e);
6861
6920
 
6862
- if (gestureState.zoomEnable || Math.abs(dist - gestureState.prevDistance) > ZOOM_DISTANCE_MARGIN) {
6921
+ if (gestureState.zoomEnable || (gestureState.prevDistance != null && Math.abs(dist - gestureState.prevDistance) > ZOOM_DISTANCE_MARGIN)) {
6863
6922
  zoomPlot(plot, e, gestureState, navigationState);
6864
6923
 
6865
6924
  //activate zoom mode
@@ -7119,10 +7178,10 @@
7119
7178
  newEvent = new CustomEvent('mouseevent');
7120
7179
 
7121
7180
  //transform from touch event to mouse event format
7122
- newEvent.pageX = e.detail.changedTouches[0].pageX;
7123
- newEvent.pageY = e.detail.changedTouches[0].pageY;
7124
- newEvent.clientX = e.detail.changedTouches[0].clientX;
7125
- newEvent.clientY = e.detail.changedTouches[0].clientY;
7181
+ /** @type {any} */ (newEvent).pageX = e.detail.changedTouches[0].pageX;
7182
+ /** @type {any} */ (newEvent).pageY = e.detail.changedTouches[0].pageY;
7183
+ /** @type {any} */ (newEvent).clientX = e.detail.changedTouches[0].clientX;
7184
+ /** @type {any} */ (newEvent).clientY = e.detail.changedTouches[0].clientY;
7126
7185
 
7127
7186
  if (o.grid.hoverable) {
7128
7187
  doTriggerClickHoverEvent(newEvent, eventType.hover, 30);
@@ -7401,6 +7460,7 @@
7401
7460
  }
7402
7461
 
7403
7462
  function initTouchNavigation(plot, options) {
7463
+ /** @type {{ twoTouches: boolean, currentTapStart: {x: number, y: number}, currentTapEnd: {x: number, y: number}, prevTap: {x: number, y: number}, currentTap: {x: number, y: number}, interceptedLongTap: boolean, isUnsupportedGesture: boolean, prevTapTime: number | null, tapStartTime: number | null, longTapTriggerId: ReturnType<typeof setTimeout> | null }} */
7404
7464
  var gestureState = {
7405
7465
  twoTouches: false,
7406
7466
  currentTapStart: { x: 0, y: 0 },
@@ -7554,6 +7614,7 @@
7554
7614
  },
7555
7615
 
7556
7616
  isLongTap: function(e) {
7617
+ if (gestureState.tapStartTime == null) return false;
7557
7618
  var currentTime = new Date().getTime(),
7558
7619
  tapDuration = currentTime - gestureState.tapStartTime;
7559
7620
  if (tapDuration >= minLongTapDuration && !gestureState.interceptedLongTap) {
@@ -7594,6 +7655,7 @@
7594
7655
  },
7595
7656
 
7596
7657
  isTap: function(e) {
7658
+ if (gestureState.tapStartTime == null) return false;
7597
7659
  var currentTime = new Date().getTime(),
7598
7660
  tapDuration = currentTime - gestureState.tapStartTime;
7599
7661
  if (tapDuration <= pressedTapDuration) {
@@ -7642,7 +7704,9 @@
7642
7704
  }
7643
7705
  function isDoubleTap(e) {
7644
7706
  var currentTime = new Date().getTime(),
7645
- intervalBetweenTaps = currentTime - gestureState.prevTapTime;
7707
+ intervalBetweenTaps = gestureState.prevTapTime != null
7708
+ ? currentTime - gestureState.prevTapTime
7709
+ : Infinity;
7646
7710
 
7647
7711
  if (intervalBetweenTaps >= 0 && intervalBetweenTaps < maxIntervalBetweenTaps) {
7648
7712
  if (distance(gestureState.prevTap.x, gestureState.prevTap.y, gestureState.currentTap.x, gestureState.currentTap.y) < maxDistanceBetweenTaps) {
@@ -8029,9 +8093,9 @@
8029
8093
  }
8030
8094
 
8031
8095
  for (var i = 0; i < spec.length - 1; ++i) {
8032
- if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] +
8033
- spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 &&
8034
- spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
8096
+ if (axis.delta < (Number(spec[i][0]) * timeUnitSize[spec[i][1]] +
8097
+ Number(spec[i + 1][0]) * timeUnitSize[spec[i + 1][1]]) / 2 &&
8098
+ Number(spec[i][0]) * timeUnitSize[spec[i][1]] >= minSize) {
8035
8099
  break;
8036
8100
  }
8037
8101
  }
@@ -8694,6 +8758,7 @@
8694
8758
  }
8695
8759
 
8696
8760
  function triggerSelectedEvent() {
8761
+ /** @type {any} */
8697
8762
  var r = getSelection();
8698
8763
 
8699
8764
  trigger(plot.getPlaceholder(), "plotselected", [ r ]);
@@ -8776,7 +8841,7 @@
8776
8841
 
8777
8842
  // function taken from markings support in Flot
8778
8843
  function extractRange(ranges, coord) {
8779
- var axis, from, to, key, axes = plot.getAxes();
8844
+ var axis, from, to, /** @type {string|undefined} */ key, axes = plot.getAxes();
8780
8845
 
8781
8846
  for (var k in axes) {
8782
8847
  axis = axes[k];
@@ -8796,7 +8861,7 @@
8796
8861
  }
8797
8862
 
8798
8863
  // backwards-compat stuff - to be removed in future
8799
- if (!ranges[key]) {
8864
+ if (key && !ranges[key]) {
8800
8865
  axis = coord === "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
8801
8866
  from = ranges[coord + "1"];
8802
8867
  to = ranges[coord + "2"];
@@ -9205,7 +9270,7 @@
9205
9270
  const utf8Array = new Uint8Array(arrayBuffer);
9206
9271
  const blockSize = 16384;
9207
9272
  for (var i = 0; i < utf8Array.length; i = i + blockSize) {
9208
- const binarySubString = String.fromCharCode.apply(null, utf8Array.subarray(i, i + blockSize));
9273
+ const binarySubString = String.fromCharCode.apply(null, /** @type {any} */ (utf8Array.subarray(i, i + blockSize)));
9209
9274
  binaryString = binaryString + binarySubString;
9210
9275
  }
9211
9276
  return binaryString;
@@ -9420,7 +9485,7 @@
9420
9485
 
9421
9486
  var left = 0;
9422
9487
  var columnWidths = [];
9423
- var style = window.getComputedStyle(document.querySelector('body'));
9488
+ var style = window.getComputedStyle(document.body);
9424
9489
  for (i = 0; i < entries.length; ++i) {
9425
9490
  let columnIndex = i % options.legend.noColumns;
9426
9491
  entry = entries[i];
@@ -9485,6 +9550,17 @@
9485
9550
  shape.strokeWidth = entry.options.points.lineWidth;
9486
9551
  iconHtml += getEntryIconHtml(shape);
9487
9552
  }
9553
+ // fallback for plugin-drawn series (pie, errorbars, etc.)
9554
+ // that don't turn on any of lines/bars/points — without
9555
+ // this the legend entry has a label but no icon. Upstream
9556
+ // flot/flot#1641, minus the switch to `else if` so series
9557
+ // that deliberately overlay (e.g. lines + points) keep
9558
+ // rendering both icons.
9559
+ if (iconHtml === '') {
9560
+ shape.name = 'box';
9561
+ shape.fillColor = entry.color;
9562
+ iconHtml += getEntryIconHtml(shape);
9563
+ }
9488
9564
 
9489
9565
  labelHtml = '<text x="' + shape.xPos + '" y="' + shape.yPos + '" text-anchor="start"><tspan dx="2em" dy="1.2em">' + shape.label + '</tspan></text>';
9490
9566
  html[j++] = '<g>' + iconHtml + labelHtml + '</g>';
@@ -9524,9 +9600,17 @@
9524
9600
  legendEl.style.pointerEvents = 'none';
9525
9601
  placeholder.appendChild(legendEl);
9526
9602
  } else {
9527
- options.legend.container.innerHTML = html.join('');
9528
- options.legend.container.style.width = width + 'px';
9529
- options.legend.container.style.height = height + 'em';
9603
+ // Accept either a DOM Element or a jQuery-wrapped container.
9604
+ // Upstream flot/flot#1750 switched to `$(container).get(0)` to
9605
+ // always land on the underlying element; since this fork is
9606
+ // jQuery-optional, do the unwrap inline.
9607
+ var container = options.legend.container;
9608
+ if (container && typeof container.get === 'function' && container[0]) {
9609
+ container = container[0];
9610
+ }
9611
+ container.innerHTML = html.join('');
9612
+ container.style.width = width + 'px';
9613
+ container.style.height = height + 'em';
9530
9614
  }
9531
9615
  }
9532
9616
 
@@ -9620,6 +9704,14 @@
9620
9704
  'width="1.5em" height="1.5em"' +
9621
9705
  '/>';
9622
9706
  break;
9707
+ case 'box':
9708
+ html = '<use xlink:href="#box" class="legendIcon" ' +
9709
+ 'x="' + x + '" ' +
9710
+ 'y="' + y + '" ' +
9711
+ 'fill="' + fill + '" ' +
9712
+ 'width="1.5em" height="1.5em"' +
9713
+ '/>';
9714
+ break;
9623
9715
  default:
9624
9716
  // default is circle
9625
9717
  html = '<use xlink:href="#circle" class="legendIcon" ' +
@@ -9642,6 +9734,12 @@
9642
9734
  '<polyline points="0,15 5,5 10,10 15,0"/>' +
9643
9735
  '</symbol>' +
9644
9736
 
9737
+ // Fallback icon for plugin-drawn series that don't turn on
9738
+ // any of lines / bars / points. Upstream flot/flot#1641.
9739
+ '<symbol id="box" stroke-width="1" viewBox="-5 -5 25 25">' +
9740
+ '<rect x="0" y="0" width="15" height="15"/>' +
9741
+ '</symbol>' +
9742
+
9645
9743
  '<symbol id="area" stroke-width="1" viewBox="-5 -5 25 25">' +
9646
9744
  '<polyline points="0,15 5,5 10,10 15,0, 15,15, 0,15"/>' +
9647
9745
  '</symbol>' +
@@ -9824,6 +9922,24 @@
9824
9922
  // $.plot("#placeholder", data, options);
9825
9923
 
9826
9924
 
9925
+ // Route flot's internal trigger() through jQuery so plugin handlers bound
9926
+ // via $(el).on(type, fn) see the same data shape as upstream flot/flot.
9927
+ // Two conventions coexist in the plugins, matching upstream:
9928
+ // - trigger(el, type, [arg0, arg1, ...]) → $(el).trigger(type, [args])
9929
+ // jQuery spreads the array into handler positional params, so
9930
+ // function(event, pos, item, items) works (plothover, plotzoom, ...).
9931
+ // - trigger(el, type, <non-array>) → $.Event(type, { detail: x })
9932
+ // Handlers read event.detail, matching upstream's re-center contract
9933
+ // where a plain object (or Event) is attached to event.detail.
9934
+ setTrigger(function(el, type, args) {
9935
+ if (args === undefined || Array.isArray(args)) {
9936
+ $(el).trigger(type, args || []);
9937
+ return;
9938
+ }
9939
+ var event = $.Event(type, { detail: args });
9940
+ $(el).trigger(event);
9941
+ });
9942
+
9827
9943
  // Register $.plot and $.color on the jQuery object.
9828
9944
  $.plot = function(placeholder, data, options) {
9829
9945
  var el = typeof placeholder === 'string'