maidr 2.0.0 → 2.1.0

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.
package/dist/maidr.js CHANGED
@@ -865,6 +865,13 @@ class ChatLLM {
865
865
  <button type="button">What is the title?</button>
866
866
  <button type="button">What are the high and low values?</button>
867
867
  <button type="button">What is the general shape of the chart?</button>
868
+ <button type="button" id="more_suggestions">More</button>
869
+ </p>
870
+ <p id="more_suggestions_container" class="hidden LLM_suggestions">
871
+ <button type="button">Please provide the title of this visualization, then provide a description for someone who is blind or low vision. Include general overview of axes and the data at a high-level.</button>
872
+ <button type="button">For the visualization I shared, please provide the following (where applicable): mean, standard deviation, extrema, correlations, relational comparisons like greater than OR lesser than.</button>
873
+ <button type="button">Based on the visualization shared, address the following: Do you observe any unforeseen trends? If yes, what? Please convey any complex multi-faceted patterns present. Can you identify any noteworthy exceptions that aren't readily apparent through non-visual methods of analysis?</button>
874
+ <button type="button">Provide context to help explain the data depicted in this visualization based on domain-specific insight.</button>
868
875
  </p>
869
876
  <p><button type="button" id="chatLLM_submit">Submit</button></p>
870
877
  </div>
@@ -941,7 +948,7 @@ class ChatLLM {
941
948
 
942
949
  // ChatLLM suggestion events
943
950
  let suggestions = document.querySelectorAll(
944
- '#chatLLM .LLM_suggestions button'
951
+ '#chatLLM .LLM_suggestions button:not(#more_suggestions)'
945
952
  );
946
953
  for (let i = 0; i < suggestions.length; i++) {
947
954
  constants.events.push([
@@ -954,6 +961,16 @@ class ChatLLM {
954
961
  },
955
962
  ]);
956
963
  }
964
+ constants.events.push([
965
+ document.getElementById('more_suggestions'),
966
+ 'click',
967
+ function (e) {
968
+ document
969
+ .getElementById('more_suggestions_container')
970
+ .classList.toggle('hidden');
971
+ document.getElementById('more_suggestions').classList.toggle('hidden');
972
+ },
973
+ ]);
957
974
 
958
975
  // Delete OpenAI and Gemini keys
959
976
  constants.events.push([
@@ -989,13 +1006,13 @@ class ChatLLM {
989
1006
  }
990
1007
 
991
1008
  let img = null;
992
- if (constants.LLMOpenAiMulti) {
1009
+ if (constants.LLMOpenAiMulti || constants.LLMModel == 'openai') {
993
1010
  if (firsttime) {
994
1011
  img = await this.ConvertSVGtoJPG(singleMaidr.id, 'openai');
995
1012
  }
996
1013
  chatLLM.OpenAIPrompt(text, img);
997
1014
  }
998
- if (constants.LLMGeminiMulti) {
1015
+ if (constants.LLMGeminiMulti || constants.LLMModel == 'gemini') {
999
1016
  if (firsttime) {
1000
1017
  img = await this.ConvertSVGtoJPG(singleMaidr.id, 'gemini');
1001
1018
  }
@@ -2982,8 +2999,8 @@ class Display {
2982
2999
 
2983
3000
  constants.brailleInput.setSelectionRange(adjustedPos, adjustedPos);
2984
3001
  } else if (
2985
- singleMaidr.type == 'point' ||
2986
- singleMaidr.type.includes('point')
3002
+ singleMaidr.type == 'smooth' ||
3003
+ singleMaidr.type.includes('smooth')
2987
3004
  ) {
2988
3005
  constants.brailleInput.setSelectionRange(positionL1.x, positionL1.x);
2989
3006
  }
@@ -3180,8 +3197,8 @@ class Display {
3180
3197
  else if (constants.textMode == 'terse')
3181
3198
  output = '<p>' + textTerse + '</p>\n';
3182
3199
  } else if (
3183
- singleMaidr.type == 'point' ||
3184
- singleMaidr.type.includes('point')
3200
+ [].concat(singleMaidr.type).includes('point') ||
3201
+ [].concat(singleMaidr.type).includes('smooth')
3185
3202
  ) {
3186
3203
  if (constants.chartType == 'point') {
3187
3204
  // point layer
@@ -5493,7 +5510,7 @@ class ScatterPlot {
5493
5510
  }
5494
5511
  } else if (singleMaidr.type == 'smooth') {
5495
5512
  if ('selector' in singleMaidr) {
5496
- this.plotLine = document.querySelectorAll(singleMaidr.selector);
5513
+ this.plotLine = document.querySelectorAll(singleMaidr.selector)[0];
5497
5514
  } else if ('elements' in singleMaidr) {
5498
5515
  this.plotLine = singleMaidr.elements;
5499
5516
  }
@@ -5507,8 +5524,14 @@ class ScatterPlot {
5507
5524
  this.curveX = smoothCurvePoints[0]; // actual values of x
5508
5525
  this.curvePoints = smoothCurvePoints[1]; // actual values of y
5509
5526
 
5510
- this.curveMinY = Math.min(...this.curvePoints);
5511
- this.curveMaxY = Math.max(...this.curvePoints);
5527
+ // if there is only point layer, then curvePoints will be empty
5528
+ if (this.curvePoints && this.curvePoints.length > 0) {
5529
+ this.curveMinY = Math.min(...this.curvePoints);
5530
+ this.curveMaxY = Math.max(...this.curvePoints);
5531
+ } else {
5532
+ this.curveMinY = Number.MAX_VALUE;
5533
+ this.curveMaxY = Number.MIN_VALUE;
5534
+ }
5512
5535
  this.gradient = this.GetGradient();
5513
5536
  }
5514
5537
 
@@ -5521,30 +5544,62 @@ class ScatterPlot {
5521
5544
 
5522
5545
  if (this.plotPoints) {
5523
5546
  for (let i = 0; i < this.plotPoints.length; i++) {
5524
- let x = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'x')); // .toFixed(1);
5525
- let y = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'y'));
5547
+ let x;
5548
+ let y;
5549
+
5550
+ // extract x, y coordinates based on the SVG element type
5551
+ if (this.plotPoints[i] instanceof SVGPathElement) {
5552
+ let pathD = this.plotPoints[i].getAttribute('d');
5553
+ let regex = /M\s*(-?\d+(\.\d+)?)\s+(-?\d+(\.\d+)?)/g;
5554
+
5555
+ let match = regex.exec(pathD);
5556
+ x = parseFloat(match[1]);
5557
+ y = parseFloat(match[3]);
5558
+ } else {
5559
+ x = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'x')); // .toFixed(1);
5560
+ y = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'y'));
5561
+ }
5562
+
5526
5563
  if (!points.has(x)) {
5527
5564
  points.set(x, new Set([y]));
5528
5565
  } else {
5529
5566
  points.get(x).add(y);
5530
5567
  }
5531
5568
  }
5532
- } else {
5569
+ } else if ([].concat(singleMaidr.type).includes('point')) {
5533
5570
  // pull from data instead
5534
5571
  let elIndex = this.GetElementIndex('point');
5535
- for (let i = 0; i < singleMaidr.data[elIndex].length; i++) {
5536
- let x;
5537
- let y;
5538
- if ('x' in singleMaidr.data[elIndex][i]) {
5539
- x = singleMaidr.data[elIndex][i]['x'];
5572
+ let xyFormat = this.GetDataXYFormat(elIndex);
5573
+ let data;
5574
+ if (elIndex > -1) {
5575
+ data = singleMaidr.data[elIndex];
5576
+ } else {
5577
+ data = singleMaidr.data;
5578
+ }
5579
+ let x = [];
5580
+ let y = [];
5581
+ if (xyFormat == 'array') {
5582
+ if ('x' in data) {
5583
+ x = data['x'];
5540
5584
  }
5541
- if ('y' in singleMaidr.data[elIndex][i]) {
5542
- y = singleMaidr.data[elIndex][i]['y'];
5585
+ if ('y' in data) {
5586
+ y = data['y'];
5543
5587
  }
5544
- if (!points.has(x)) {
5545
- points.set(x, new Set([y]));
5588
+ } else if (xyFormat == 'object') {
5589
+ for (let i = 0; i < data.length; i++) {
5590
+ let xValue = data[i]['x'];
5591
+ let yValue = data[i]['y'];
5592
+ x.push(xValue);
5593
+ y.push(yValue);
5594
+ }
5595
+ }
5596
+ for (let i = 0; i < x.length; i++) {
5597
+ let xValue = x[i];
5598
+ let yValue = y[i];
5599
+ if (!points.has(xValue)) {
5600
+ points.set(xValue, new Set([yValue]));
5546
5601
  } else {
5547
- points.get(x).add(y);
5602
+ points.get(xValue).add(yValue);
5548
5603
  }
5549
5604
  }
5550
5605
  }
@@ -5578,7 +5633,7 @@ class ScatterPlot {
5578
5633
  */
5579
5634
  GetElementIndex(elementName = 'point') {
5580
5635
  let elIndex = -1;
5581
- if ('type' in singleMaidr) {
5636
+ if ('type' in singleMaidr && Array.isArray(singleMaidr.type)) {
5582
5637
  elIndex = singleMaidr.type.indexOf(elementName);
5583
5638
  }
5584
5639
  return elIndex;
@@ -5591,12 +5646,20 @@ class ScatterPlot {
5591
5646
  */
5592
5647
  GetDataXYFormat(dataIndex) {
5593
5648
  // detect if data is in form [{x: 1, y: 2}, {x: 2, y: 3}] (object) or {x: [1, 2], y: [2, 3]]} (array)
5594
- let xyFormat = 'array';
5595
- if (singleMaidr.data[dataIndex]) {
5596
- if (Array.isArray(singleMaidr.data[dataIndex])) {
5597
- xyFormat = 'object';
5598
- }
5649
+ let data;
5650
+ if (dataIndex > -1) {
5651
+ data = singleMaidr.data[dataIndex];
5652
+ } else {
5653
+ data = singleMaidr.data;
5599
5654
  }
5655
+
5656
+ let xyFormat;
5657
+ if (Array.isArray(data)) {
5658
+ xyFormat = 'object';
5659
+ } else {
5660
+ xyFormat = 'array';
5661
+ }
5662
+
5600
5663
  return xyFormat;
5601
5664
  }
5602
5665
 
@@ -5689,8 +5752,10 @@ class ScatterPlot {
5689
5752
  GetPointValues() {
5690
5753
  let points = new Map(); // keep track of x and y values
5691
5754
 
5692
- let xValues = [];
5693
- let yValues = [];
5755
+ let X = [];
5756
+ let Y = [];
5757
+ let points_count = [];
5758
+ let max_points;
5694
5759
 
5695
5760
  // prepare to fetch data from the correct index in the correct format
5696
5761
  let elIndex = this.GetElementIndex('point');
@@ -5706,18 +5771,19 @@ class ScatterPlot {
5706
5771
  }
5707
5772
  if (typeof data !== 'undefined') {
5708
5773
  // assuming we got something, loop through the data and extract the x and y values
5709
-
5774
+ let xValues = [];
5775
+ let yValues = [];
5710
5776
  if (xyFormat == 'array') {
5711
- if ('x' in singleMaidr.data[elIndex]) {
5712
- xValues = singleMaidr.data[elIndex]['x'];
5777
+ if ('x' in data) {
5778
+ xValues = data['x'];
5713
5779
  }
5714
- if ('y' in singleMaidr.data[elIndex]) {
5715
- yValues = singleMaidr.data[elIndex]['y'];
5780
+ if ('y' in data) {
5781
+ yValues = data['y'];
5716
5782
  }
5717
5783
  } else if (xyFormat == 'object') {
5718
- for (let i = 0; i < singleMaidr.data[elIndex].length; i++) {
5719
- let x = singleMaidr.data[elIndex][i]['x'];
5720
- let y = singleMaidr.data[elIndex][i]['y'];
5784
+ for (let i = 0; i < data.length; i++) {
5785
+ let x = data[i]['x'];
5786
+ let y = data[i]['y'];
5721
5787
  xValues.push(x);
5722
5788
  yValues.push(y);
5723
5789
  }
@@ -5765,9 +5831,6 @@ class ScatterPlot {
5765
5831
  });
5766
5832
  });
5767
5833
 
5768
- let X = [];
5769
- let Y = [];
5770
- let points_count = [];
5771
5834
  for (const [x_val, y_val] of points) {
5772
5835
  X.push(x_val);
5773
5836
  let y_arr = [];
@@ -5779,12 +5842,10 @@ class ScatterPlot {
5779
5842
  Y.push(y_arr.sort());
5780
5843
  points_count.push(y_count);
5781
5844
  }
5782
- let max_points = Math.max(...points_count.map((a) => Math.max(...a)));
5783
-
5784
- return [X, Y, points_count, max_points];
5785
- } else {
5786
- return;
5845
+ max_points = Math.max(...points_count.map((a) => Math.max(...a)));
5787
5846
  }
5847
+
5848
+ return [X, Y, points_count, max_points];
5788
5849
  }
5789
5850
 
5790
5851
  /**
@@ -5833,28 +5894,54 @@ class ScatterPlot {
5833
5894
  let y_points = [];
5834
5895
 
5835
5896
  if (this.plotLine) {
5836
- // extract all the y coordinates from the point attribute of polyline
5837
- let str = this.plotLine.getAttribute('points');
5838
- let coords = str.split(' ');
5839
- for (let i = 0; i < coords.length; i++) {
5840
- let coord = coords[i].split(',');
5841
- x_points.push(parseFloat(coord[0]));
5842
- y_points.push(parseFloat(coord[1]));
5897
+ // scatterplot SVG containing path element instead of polyline
5898
+ if (this.plotLine instanceof SVGPathElement) {
5899
+ // Assuming the path data is in the format "M x y L x y L x y L x y"
5900
+ const pathD = this.plotLine.getAttribute('d');
5901
+ const regex = /[ML]\s*(-?\d+(\.\d+)?)\s+(-?\d+(\.\d+)?)/g;
5902
+
5903
+ let match;
5904
+ while ((match = regex.exec(pathD)) !== null) {
5905
+ x_points.push(match[1]); // x coordinate
5906
+ y_points.push(match[3]); // y coordinate
5907
+ }
5908
+ } else if (this.plotLine instanceof SVGPolylineElement) {
5909
+ // extract all the y coordinates from the point attribute of polyline
5910
+ let str = this.plotLine.getAttribute('points');
5911
+ let coords = str.split(' ');
5912
+
5913
+ for (let i = 0; i < coords.length; i++) {
5914
+ let coord = coords[i].split(',');
5915
+ x_points.push(parseFloat(coord[0]));
5916
+ y_points.push(parseFloat(coord[1]));
5917
+ }
5843
5918
  }
5844
- } else {
5919
+ } else if ([].concat(singleMaidr.type).includes('smooth')) {
5845
5920
  // fetch from data instead
5846
- let elIndex = this.GetElementIndex('point');
5847
- for (let i = 0; i < singleMaidr.data[elIndex].length; i++) {
5848
- if ('x' in singleMaidr.data[elIndex][i]) {
5849
- x_points.push(singleMaidr.data[elIndex][i]['x']);
5921
+ let elIndex = this.GetElementIndex('smooth');
5922
+ let xyFormat = this.GetDataXYFormat(elIndex);
5923
+ let data;
5924
+ if (elIndex > -1) {
5925
+ data = singleMaidr.data[elIndex];
5926
+ } else {
5927
+ data = singleMaidr.data
5928
+ }
5929
+ if (xyFormat == 'object') {
5930
+ for (let i = 0; i < data.length; i++) {
5931
+ x_points.push(data[i]['x']);
5932
+ y_points.push(data[i]['y']);
5933
+ }
5934
+ } else if (xyFormat == 'array') {
5935
+ if ('x' in data) {
5936
+ x_points = data['x'];
5850
5937
  }
5851
- if ('y' in singleMaidr.data[elIndex][i]) {
5852
- y_points.push(singleMaidr.data[elIndex][i]['y']);
5938
+ if ('y' in data) {
5939
+ y_points = data['y'];
5853
5940
  }
5854
5941
  }
5855
5942
  }
5856
5943
 
5857
- return [x, y];
5944
+ return [x_points, y_points];
5858
5945
  }
5859
5946
 
5860
5947
  /**
@@ -5878,23 +5965,21 @@ class ScatterPlot {
5878
5965
  }
5879
5966
  if (typeof data !== 'undefined') {
5880
5967
  if (xyFormat == 'object') {
5881
- for (let i = 0; i < singleMaidr.data[elIndex].length; i++) {
5882
- x_points.push(singleMaidr.data[elIndex][i]['x']);
5883
- y_points.push(singleMaidr.data[elIndex][i]['y']);
5968
+ for (let i = 0; i < data.length; i++) {
5969
+ x_points.push(data[i]['x']);
5970
+ y_points.push(data[i]['y']);
5884
5971
  }
5885
5972
  } else if (xyFormat == 'array') {
5886
- if ('x' in singleMaidr.data[elIndex]) {
5887
- x_points = singleMaidr.data[elIndex]['x'];
5973
+ if ('x' in data) {
5974
+ x_points = data['x'];
5888
5975
  }
5889
- if ('y' in singleMaidr.data[elIndex]) {
5890
- y_points = singleMaidr.data[elIndex]['y'];
5976
+ if ('y' in data) {
5977
+ y_points = data['y'];
5891
5978
  }
5892
5979
  }
5893
-
5894
- return [x_points, y_points];
5895
- } else {
5896
- return;
5897
5980
  }
5981
+
5982
+ return [x_points, y_points];
5898
5983
  }
5899
5984
 
5900
5985
  /**
@@ -5926,12 +6011,18 @@ class ScatterPlot {
5926
6011
  */
5927
6012
  GetRectStatus(type) {
5928
6013
  let elIndex = this.GetElementIndex(type);
5929
- if ('selector' in singleMaidr) {
5930
- return singleMaidr.selector[elIndex] ? true : false;
5931
- } else if ('elements' in singleMaidr) {
5932
- return singleMaidr.elements[elIndex] ? true : false;
6014
+ if (elIndex > -1) {
6015
+ if ('selector' in singleMaidr) {
6016
+ return !!singleMaidr.selector[elIndex];
6017
+ } else if ('elements' in singleMaidr) {
6018
+ return !!singleMaidr.elements[elIndex];
6019
+ }
5933
6020
  } else {
5934
- return false;
6021
+ if ('selector' in singleMaidr) {
6022
+ return !!singleMaidr.selector;
6023
+ } else if ('elements' in singleMaidr) {
6024
+ return !!singleMaidr.elements;
6025
+ }
5935
6026
  }
5936
6027
  }
5937
6028
  }
@@ -5948,11 +6039,13 @@ class Layer0Point {
5948
6039
  * @constructor
5949
6040
  */
5950
6041
  constructor() {
5951
- this.x = plot.chartPointsX[0];
5952
- this.y = plot.chartPointsY[0];
5953
- this.strokeWidth = 1.35;
5954
- this.circleIndex = [];
5955
- this.hasRect = plot.GetRectStatus('point');
6042
+ if ([].concat(singleMaidr.type).includes('point')) {
6043
+ this.x = plot.chartPointsX[0];
6044
+ this.y = plot.chartPointsY[0];
6045
+ this.strokeWidth = 1.35;
6046
+ this.hasRect = plot.GetRectStatus('point');
6047
+ this.circleIndex = [];
6048
+ }
5956
6049
  }
5957
6050
 
5958
6051
  /**
@@ -5967,10 +6060,25 @@ class Layer0Point {
5967
6060
  this.circleIndex = [];
5968
6061
  for (let j = 0; j < this.y.length; j++) {
5969
6062
  for (let i = 0; i < plot.plotPoints.length; i++) {
5970
- if (
5971
- plot.plotPoints[i].getAttribute(plot.prefix + 'x') == this.x &&
5972
- plot.plotPoints[i].getAttribute(plot.prefix + 'y') == this.y[j]
6063
+ let x;
6064
+ let y;
6065
+
6066
+ if (plot.plotPoints[i] instanceof SVGPathElement) {
6067
+ const pathD = plot.plotPoints[i].getAttribute('d');
6068
+ const regex = /M\s*(-?\d+(\.\d+)?)\s+(-?\d+(\.\d+)?)/g;
6069
+
6070
+ let match = regex.exec(pathD);
6071
+ x = parseFloat(match[1]);
6072
+ y = parseFloat(match[3]);
6073
+ } else if (
6074
+ plot.plotPoints[i] instanceof SVGUseElement ||
6075
+ plot.plotPoints[i] instanceof SVGCircleElement
5973
6076
  ) {
6077
+ x = plot.plotPoints[i].getAttribute(plot.prefix + 'x');
6078
+ y = plot.plotPoints[i].getAttribute(plot.prefix + 'y');
6079
+ }
6080
+
6081
+ if (x == this.x && y == this.y[j]) {
5974
6082
  this.circleIndex.push(i);
5975
6083
  break;
5976
6084
  }
@@ -5998,20 +6106,28 @@ class Layer0Point {
5998
6106
  constants.chart.getBoundingClientRect().height - this.y[i]
5999
6107
  );
6000
6108
  } else {
6001
- point.setAttribute(
6002
- 'cy',
6003
- plot.plotPoints[this.circleIndex[i]].getAttribute('cy')
6004
- );
6109
+ let y;
6110
+
6111
+ if (plot.plotPoints[this.circleIndex[i]] instanceof SVGPathElement) {
6112
+ const pathD = plot.plotPoints[this.circleIndex[i]].getAttribute('d');
6113
+ const regex = /M\s*(-?\d+(\.\d+)?)\s+(-?\d+(\.\d+)?)/g;
6114
+
6115
+ let match = regex.exec(pathD);
6116
+ y = parseFloat(match[3]);
6117
+ } else if (
6118
+ plot.plotPoints[this.circleIndex[i]] instanceof SVGUseElement ||
6119
+ plot.plotPoints[this.circleIndex[i]] instanceof SVGCircleElement
6120
+ ) {
6121
+ y = plot.plotPoints[this.circleIndex[i]].getAttribute(plot.prefix + 'y');
6122
+ }
6123
+
6124
+ point.setAttribute('cy', y);
6005
6125
  }
6006
6126
  point.setAttribute('r', 3.95);
6007
6127
  point.setAttribute('stroke', constants.colorSelected);
6008
6128
  point.setAttribute('stroke-width', this.strokeWidth);
6009
6129
  point.setAttribute('fill', constants.colorSelected);
6010
- if (plot.svgScaler[1] == -1) {
6011
- constants.chart.appendChild(point);
6012
- } else {
6013
- plot.plotPoints[this.circleIndex[i]].parentNode.appendChild(point);
6014
- }
6130
+ constants.chart.appendChild(point);
6015
6131
  }
6016
6132
  }
6017
6133
 
@@ -6049,10 +6165,12 @@ class Layer1Point {
6049
6165
  * @constructor
6050
6166
  */
6051
6167
  constructor() {
6052
- this.x = plot.chartLineX[0];
6053
- this.y = plot.chartLineY[0];
6054
- this.strokeWidth = 1.35;
6055
- this.hasRect = plot.GetRectStatus('point');
6168
+ if ([].concat(singleMaidr.type).includes('smooth')) {
6169
+ this.x = plot.chartLineX[0];
6170
+ this.y = plot.chartLineY[0];
6171
+ this.strokeWidth = 1.35;
6172
+ this.hasRect = plot.GetRectStatus('point');
6173
+ }
6056
6174
  }
6057
6175
 
6058
6176
  /**
@@ -6991,7 +7109,11 @@ class Control {
6991
7109
  }
6992
7110
 
6993
7111
  // switch layer controls
6994
- if (Array.isArray(singleMaidr.type)) {
7112
+ if (
7113
+ Array.isArray(singleMaidr.type) &&
7114
+ [].concat(singleMaidr.type).includes('point') &&
7115
+ [].concat(singleMaidr.type).includes('smooth')
7116
+ ) {
6995
7117
  // page down /(fn+down arrow): change chart type (layer)
6996
7118
  if (e.key == 'PageDown' && constants.brailleMode == 'off') {
6997
7119
  display.changeChartLayer('down');
@@ -8512,7 +8634,7 @@ class Control {
8512
8634
  }
8513
8635
  } else if (
8514
8636
  [].concat(singleMaidr.type).includes('point') ||
8515
- singleMaidr.type == 'point'
8637
+ [].concat(singleMaidr.type).includes('smooth')
8516
8638
  ) {
8517
8639
  // variable initialization
8518
8640
  constants.plotId = 'geom_point.points.12.1';