maidr 1.3.0 → 1.3.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.
package/dist/maidr.js CHANGED
@@ -74,13 +74,12 @@ class Constants {
74
74
  visualBraille = false; // do we want to represent braille based on what's visually there or actually there. Like if we have 2 outliers with the same position, do we show 1 (visualBraille true) or 2 (false)
75
75
  globalMinMax = true;
76
76
  ariaMode = 'assertive'; // assertive (default) / polite
77
- playLLMWaitingSound = true;
78
77
 
79
78
  // LLM settings
80
- LLMDebugMode = 0; // 0 = use real data, 1 = all fake, 2 = real data but no image
81
79
  openAIAuthKey = null; // OpenAI authentication key, set in menu
82
80
  geminiAuthKey = null; // Gemini authentication key, set in menu
83
81
  LLMmaxResponseTokens = 1000; // max tokens to send to LLM, 20 for testing, 1000 ish for real
82
+ playLLMWaitingSound = true;
84
83
  LLMDetail = 'high'; // low (default for testing, like 100 tokens) / high (default for real, like 1000 tokens)
85
84
  LLMModel = 'openai'; // openai (default) / gemini
86
85
  LLMSystemMessage =
@@ -301,7 +300,7 @@ class Menu {
301
300
  <div class="modal-dialog" role="document" tabindex="0">
302
301
  <div class="modal-content">
303
302
  <div class="modal-header">
304
- <h4 class="modal-title">Menu</h4>
303
+ <h2 class="modal-title">Menu</h2>
305
304
  <button type="button" class="close" data-dismiss="modal" aria-label="Close">
306
305
  <span aria-hidden="true">&times;</span>
307
306
  </button>
@@ -428,7 +427,7 @@ class Menu {
428
427
  </select>
429
428
  <label for="skill_level">Level of skill in statistical charts</label>
430
429
  </p>
431
- <p id="skill_level_other_container" class="hidden"><input type="text" id="skill_level_other"> <label for="skill_level_other">"I have a(n) [X] understanding of statistical charts"</label></p>
430
+ <p id="skill_level_other_container" class="hidden"><input type="text" placeholder="Very basic" id="skill_level_other"> <label for="skill_level_other">Describe your level of skill in statistical charts</label></p>
432
431
  </div>
433
432
  </div>
434
433
  <div class="modal-footer">
@@ -595,7 +594,6 @@ class Menu {
595
594
  */
596
595
  PopulateData() {
597
596
  document.getElementById('vol').value = constants.vol;
598
- //document.getElementById('show_rect').checked = constants.showRect;
599
597
  document.getElementById('autoplay_rate').value = constants.autoPlayRate;
600
598
  document.getElementById('braille_display_length').value =
601
599
  constants.brailleDisplayLength;
@@ -619,7 +617,6 @@ class Menu {
619
617
  }
620
618
  document.getElementById('LLM_model').value = constants.LLMModel;
621
619
 
622
-
623
620
  // aria mode
624
621
  if (constants.ariaMode == 'assertive') {
625
622
  document.getElementById('aria_mode_assertive').checked = true;
@@ -657,7 +654,6 @@ class Menu {
657
654
  */
658
655
  SaveData() {
659
656
  constants.vol = document.getElementById('vol').value;
660
- //constants.showRect = document.getElementById('show_rect').checked;
661
657
  constants.autoPlayRate = document.getElementById('autoplay_rate').value;
662
658
  constants.brailleDisplayLength = document.getElementById(
663
659
  'braille_display_length'
@@ -710,7 +706,6 @@ class Menu {
710
706
  SaveDataToLocalStorage() {
711
707
  let data = {};
712
708
  data.vol = constants.vol;
713
- //data.showRect = constants.showRect;
714
709
  data.autoPlayRate = constants.autoPlayRate;
715
710
  data.brailleDisplayLength = constants.brailleDisplayLength;
716
711
  data.colorSelected = constants.colorSelected;
@@ -732,7 +727,6 @@ class Menu {
732
727
  let data = JSON.parse(localStorage.getItem('settings_data'));
733
728
  if (data) {
734
729
  constants.vol = data.vol;
735
- //constants.showRect = data.showRect;
736
730
  constants.autoPlayRate = data.autoPlayRate;
737
731
  constants.brailleDisplayLength = data.brailleDisplayLength;
738
732
  constants.colorSelected = data.colorSelected;
@@ -773,7 +767,7 @@ class ChatLLM {
773
767
  <div class="modal-dialog" role="document" tabindex="0">
774
768
  <div class="modal-content">
775
769
  <div class="modal-header">
776
- <h4 id="chatLLM_title" class="modal-title">Ask a Question</h4>
770
+ <h2 id="chatLLM_title" class="modal-title">Ask a Question</h2>
777
771
  <button type="button" class="close" data-dismiss="modal" aria-label="Close">
778
772
  <span aria-hidden="true">&times;</span>
779
773
  </button>
@@ -833,7 +827,7 @@ class ChatLLM {
833
827
  document,
834
828
  'keyup',
835
829
  function (e) {
836
- if (e.key == '?') {
830
+ if (e.key == '?' && (e.ctrlKey || e.metaKey)) {
837
831
  chatLLM.Toggle(true);
838
832
  }
839
833
  },
@@ -893,13 +887,7 @@ class ChatLLM {
893
887
  chatLLM.WaitingSound(true);
894
888
  }
895
889
 
896
- if (constants.LLMDebugMode == 1) {
897
- // do the below with a 5 sec delay
898
- setTimeout(function () {
899
- chatLLM.ProcessLLMResponse(chatLLM.fakeLLMResponseData());
900
- }, 5000);
901
- return;
902
- } else if (constants.LLMModel == 'gemini') {
890
+ if (constants.LLMModel == 'gemini') {
903
891
  chatLLM.GeminiPrompt(text, img);
904
892
  } else if (constants.LLMModel == 'openai') {
905
893
  chatLLM.OpenAIPrompt(text, img);
@@ -1100,10 +1088,7 @@ class ChatLLM {
1100
1088
  let i = this.requestJson.messages.length;
1101
1089
  this.requestJson.messages[i] = {};
1102
1090
  this.requestJson.messages[i].role = 'user';
1103
- if (constants.LLMDebugMode == 2) {
1104
- // backup message only, no image
1105
- this.requestJson.messages[i].content = backupMessage;
1106
- } else if (img) {
1091
+ if (img) {
1107
1092
  // first message, include the img
1108
1093
  this.requestJson.messages[i].content = [
1109
1094
  {
@@ -1176,7 +1161,7 @@ class ChatLLM {
1176
1161
  <div class="chatLLM_message ${
1177
1162
  user == 'User' ? 'chatLLM_message_self' : 'chatLLM_message_other'
1178
1163
  }">
1179
- <p class="chatLLM_message_user">${user}</p>
1164
+ <h3 class="chatLLM_message_user">${user}</h3>
1180
1165
  <p class="chatLLM_message_text">${text}</p>
1181
1166
  </div>
1182
1167
  `;
@@ -1352,7 +1337,7 @@ class Description {
1352
1337
  <div class="modal-dialog" role="document" tabindex="0">
1353
1338
  <div class="modal-content">
1354
1339
  <div class="modal-header">
1355
- <h4 id="desc_title" class="modal-title">Description</h4>
1340
+ <h2 id="desc_title" class="modal-title">Description</h2>
1356
1341
  <button type="button" class="close" data-dismiss="modal" aria-label="Close">
1357
1342
  <span aria-hidden="true">&times;</span>
1358
1343
  </button>
@@ -2144,7 +2129,7 @@ class Audio {
2144
2129
  panning = 0;
2145
2130
  }
2146
2131
  } else if (constants.chartType == 'heat') {
2147
- rawFreq = plot.values[position.y][position.x];
2132
+ rawFreq = plot.data[position.y][position.x];
2148
2133
  rawPanning = position.x;
2149
2134
  frequency = this.SlideBetween(
2150
2135
  rawFreq,
@@ -2940,7 +2925,7 @@ class Display {
2940
2925
  plot.fill +
2941
2926
  ' is ';
2942
2927
  // if (constants.hasRect) {
2943
- verboseText += plot.plotData[2][position.y][position.x];
2928
+ verboseText += plot.data[position.y][position.x];
2944
2929
  // }
2945
2930
  } else {
2946
2931
  verboseText +=
@@ -2955,7 +2940,7 @@ class Display {
2955
2940
  plot.fill +
2956
2941
  ' is ';
2957
2942
  // if (constants.hasRect) {
2958
- verboseText += plot.plotData[2][position.y][position.x];
2943
+ verboseText += plot.data[position.y][position.x];
2959
2944
  // }
2960
2945
  }
2961
2946
  // terse and verbose alternate between columns and rows
@@ -2969,7 +2954,7 @@ class Display {
2969
2954
  '<p>' +
2970
2955
  plot.x_labels[position.x] +
2971
2956
  ', ' +
2972
- plot.plotData[2][position.y][position.x] +
2957
+ plot.data[position.y][position.x] +
2973
2958
  '</p>\n';
2974
2959
  } else {
2975
2960
  // row navigation
@@ -2977,7 +2962,7 @@ class Display {
2977
2962
  '<p>' +
2978
2963
  plot.y_labels[position.y] +
2979
2964
  ', ' +
2980
- plot.plotData[2][position.y][position.x] +
2965
+ plot.data[position.y][position.x] +
2981
2966
  '</p>\n';
2982
2967
  }
2983
2968
  } else if (constants.textMode == 'verbose') {
@@ -3154,11 +3139,11 @@ class Display {
3154
3139
  } else if (constants.chartType == 'line') {
3155
3140
  // line layer
3156
3141
  verboseText +=
3157
- plot.x_group_label +
3142
+ plot.plotLegend.x +
3158
3143
  ' is ' +
3159
3144
  plot.pointValuesX[position.x] +
3160
3145
  ', ' +
3161
- plot.y_group_label +
3146
+ plot.plotLegend.y +
3162
3147
  ' is ' +
3163
3148
  plot.pointValuesY[position.x];
3164
3149
 
@@ -3831,6 +3816,8 @@ class BarChart {
3831
3816
  let elements = null;
3832
3817
  if ('selector' in singleMaidr) {
3833
3818
  elements = document.querySelectorAll(singleMaidr.selector);
3819
+ } else if ('elements' in singleMaidr) {
3820
+ elements = singleMaidr.elements;
3834
3821
  }
3835
3822
 
3836
3823
  if (xlevel && data && elements) {
@@ -3867,15 +3854,6 @@ class BarChart {
3867
3854
  logError.LogAbsentElement('elements');
3868
3855
  }
3869
3856
 
3870
- // bars. The actual bar elements in the SVG. Used to highlight visually
3871
- // if ('elements' in singleMaidr) {
3872
- // this.bars = singleMaidr.elements;
3873
- // constants.hasRect = 1;
3874
- // } else {
3875
- // // this.bars = constants.chart.querySelectorAll('g[id^="geom_rect"] > rect'); // if we use plot.plotData.length instead of plot.bars.length, we don't have to include this
3876
- // constants.hasRect = 0;
3877
- // }
3878
-
3879
3857
  // column labels, both legend and tick
3880
3858
  this.columnLabels = [];
3881
3859
  let legendX = '';
@@ -3951,10 +3929,8 @@ class BarChart {
3951
3929
 
3952
3930
  if (Array.isArray(singleMaidr)) {
3953
3931
  this.plotData = singleMaidr;
3954
- } else {
3955
- if ('data' in singleMaidr) {
3956
- this.plotData = singleMaidr.data;
3957
- }
3932
+ } else if ('data' in singleMaidr) {
3933
+ this.plotData = singleMaidr.data;
3958
3934
  }
3959
3935
 
3960
3936
  // set the max and min values for the plot
@@ -4145,8 +4121,6 @@ class BoxPlot {
4145
4121
  * @constructor
4146
4122
  */
4147
4123
  constructor() {
4148
- constants.plotOrientation = 'horz'; // default
4149
-
4150
4124
  // the default sections for all boxplots
4151
4125
  this.sections = [
4152
4126
  'lower_outlier',
@@ -4158,6 +4132,8 @@ class BoxPlot {
4158
4132
  'upper_outlier',
4159
4133
  ];
4160
4134
 
4135
+ // set orientation
4136
+ constants.plotOrientation = 'horz';
4161
4137
  if ('axes' in singleMaidr) {
4162
4138
  if ('x' in singleMaidr.axes) {
4163
4139
  if ('level' in singleMaidr.axes.x) {
@@ -4238,7 +4214,11 @@ class BoxPlot {
4238
4214
 
4239
4215
  // bounds data
4240
4216
  if ('selector' in singleMaidr) {
4241
- this.plotBounds = this.GetPlotBounds();
4217
+ let elements = document.querySelector(singleMaidr.selector);
4218
+ this.plotBounds = this.GetPlotBounds(elements);
4219
+ constants.hasRect = true;
4220
+ } else if ('elements' in singleMaidr) {
4221
+ this.plotBounds = this.GetPlotBounds(singleMaidr.elements);
4242
4222
  constants.hasRect = true;
4243
4223
  } else {
4244
4224
  constants.hasRect = false;
@@ -4324,7 +4304,7 @@ class BoxPlot {
4324
4304
  * Calculates the bounding boxes for all elements in the parent element, including outliers, whiskers, and range.
4325
4305
  * @returns {Array} An array of bounding boxes for all elements.
4326
4306
  */
4327
- GetPlotBounds() {
4307
+ GetPlotBounds(elements) {
4328
4308
  // we fetch the elements in our parent,
4329
4309
  // and similar to old GetData we run through and get bounding boxes (or blanks) for everything,
4330
4310
  // and store in an identical structure
@@ -4332,7 +4312,6 @@ class BoxPlot {
4332
4312
  let plotBounds = [];
4333
4313
  let allWeNeed = this.GetAllSegmentTypes();
4334
4314
  let re = /(?:\d+(?:\.\d*)?|\.\d+)/g;
4335
- let elements = document.querySelector(singleMaidr.selector);
4336
4315
 
4337
4316
  // get initial set of elements, a parent element for all outliers, whiskers, and range
4338
4317
  let initialElemSet = [];
@@ -4903,100 +4882,36 @@ class HeatMap {
4903
4882
  }
4904
4883
  }
4905
4884
  }
4906
- let data = null;
4907
- let dataLength = 0;
4908
4885
  if ('data' in singleMaidr) {
4909
- data = singleMaidr.data;
4910
- for (let i = 0; i < data.length; i++) {
4911
- dataLength += data[i].length;
4912
- }
4886
+ this.data = singleMaidr.data;
4887
+ this.num_rows = this.data.length;
4888
+ this.num_cols = this.data[0].length;
4889
+ } else {
4890
+ // critical error, no data
4891
+ console.error('No data found in singleMaidr object');
4913
4892
  }
4914
- let elements = null;
4915
4893
  if ('selector' in singleMaidr) {
4916
- elements = document.querySelectorAll(singleMaidr.selector);
4894
+ this.elements = document.querySelectorAll(singleMaidr.selector);
4895
+ constants.hasRect = 1;
4896
+ } else if ('elements' in singleMaidr) {
4897
+ this.elements = singleMaidr.elements;
4898
+ constants.hasRect = 1;
4899
+ } else {
4900
+ this.elements = null;
4901
+ constants.hasRect = 0;
4917
4902
  }
4918
4903
 
4919
- // if (xlevel && ylevel && data && elements) {
4920
- // if (elements.length != dataLength) {
4921
- // // I didn't throw an error but give a warning
4922
- // constants.hasRect = 0;
4923
- // logError.LogDifferentLengths('data', 'elements');
4924
- // } else if (ylevel.length != data.length) {
4925
- // constants.hasRect = 0;
4926
- // logError.logDifferentLengths('y level', 'rows');
4927
- // } else if (data[0].length != xlevel.length) {
4928
- // constants.hasRect = 0;
4929
- // logError.logDifferentLengths('x level', 'columns');
4930
- // } else {
4931
- // this.plots = elements;
4932
- // constants.hasRect = 1;
4933
- // }
4934
- // } else if (ylevel && data && elements) {
4935
- // if (dataLength != elements.length) {
4936
- // constants.hasRect = 0;
4937
- // logError.logDifferentLengths('data', 'elements');
4938
- // } else if (ylevel.length != data.length) {
4939
- // constants.hasRect = 0;
4940
- // logError.logDifferentLengths('y level', 'rows');
4941
- // } else {
4942
- // this.plots = elements;
4943
- // constants.hasRect = 1;
4944
- // }
4945
- // } else if (xlevel && data && elements) {
4946
- // if (dataLength != elements.length) {
4947
- // constants.hasRect = 0;
4948
- // logError.logDifferentLengths('data', 'elements');
4949
- // } else if (xlevel.length != data[0].length) {
4950
- // constants.hasRect = 0;
4951
- // logError.logDifferentLengths('x level', 'columns');
4952
- // } else {
4953
- // this.plots = elements;
4954
- // constants.hasRect = 1;
4955
- // }
4956
- // }
4957
- // else if (xlevel && ylevel && data) {
4958
- // constants.hasRect = 0;
4959
- // if (ylevel.length != data.length) {
4960
- // logError.logDifferentLengths('y level', 'rows');
4961
- // } else if (data[0].length != xlevel.length) {
4962
- // logError.logDifferentLengths('x level', 'columns');
4963
- // }
4964
- // logError.LogAbsentElement('elements');
4965
- // }
4966
- // else if (data && elements) {
4967
- // if (dataLength != elements.length) {
4968
- // constants.hasRect = 0;
4969
- // logError.logDifferentLengths('data', 'elements');
4970
- // } else {
4971
- // this.plots = elements;
4972
- // constants.hasRect = 1;
4973
- // }
4974
- // } else if (data) {
4975
- // constants.hasRect = 0;
4976
- // if (!xlevel) logError.LogAbsentElement('x level');
4977
- // if (!ylevel) logError.LogAbsentElement('y level');
4978
- // if (!elements) logError.LogAbsentElement('elements');
4979
- // }
4980
-
4981
- this.plots = elements;
4982
- constants.hasRect = 1;
4983
-
4984
4904
  this.group_labels = this.getGroupLabels();
4985
- // this.x_labels = this.getXLabels();
4986
- // this.y_labels = this.getYLabels();
4987
4905
  this.x_labels = xlevel;
4988
4906
  this.y_labels = ylevel;
4989
4907
  this.title = this.getTitle();
4990
4908
  this.fill = this.getFill();
4991
4909
 
4992
- this.plotData = this.getHeatMapData();
4993
- this.updateConstants();
4910
+ if (constants.hasRect) {
4911
+ this.SetHeatmapRectData();
4912
+ }
4994
4913
 
4995
- this.x_coord = this.plotData[0];
4996
- this.y_coord = this.plotData[1];
4997
- this.values = this.plotData[2];
4998
- this.num_rows = this.plotData[3];
4999
- this.num_cols = this.plotData[4];
4914
+ this.updateConstants();
5000
4915
 
5001
4916
  this.x_group_label = this.group_labels[0].trim();
5002
4917
  this.y_group_label = this.group_labels[1].trim();
@@ -5007,113 +4922,77 @@ class HeatMap {
5007
4922
  * If 'data' exists in singleMaidr, it returns the norms from the data. Otherwise, it calculates the norms from the unique x and y coordinates.
5008
4923
  * @returns {Array} An array of heatmap data containing unique x and y coordinates, norms, number of rows, and number of columns.
5009
4924
  */
5010
- getHeatMapData() {
4925
+ SetHeatmapRectData() {
4926
+ // We get a set of x and y coordinates from the heatmap squares,
4927
+ // which is different and only sometimes connected to the actual data
4928
+ // note, only runs if constants.hasRect is true
4929
+
5011
4930
  // get the x_coord and y_coord to check if a square exists at the coordinates
5012
4931
  let x_coord_check = [];
5013
4932
  let y_coord_check = [];
5014
-
5015
4933
  let unique_x_coord = [];
5016
4934
  let unique_y_coord = [];
5017
- if (constants.hasRect) {
5018
- for (let i = 0; i < this.plots.length; i++) {
5019
- if (this.plots[i]) {
5020
- // heatmap SVG containing path element instead of rect
5021
- if (this.plots[i] instanceof SVGPathElement) {
5022
- // Assuming the path data is in the format "M x y L x y L x y L x y"
5023
- const path_d = this.plots[i].getAttribute('d');
5024
- const regex = /[ML]\s*(-?\d+(\.\d+)?)\s+(-?\d+(\.\d+)?)/g;
5025
- const match = regex.exec(path_d);
5026
-
5027
- const coords = [Number(match[1]), Number(match[3])];
5028
- const x = coords[0];
5029
- const y = coords[1];
5030
-
5031
- x_coord_check.push(parseFloat(x));
5032
- y_coord_check.push(parseFloat(y));
5033
- } else {
5034
- x_coord_check.push(parseFloat(this.plots[i].getAttribute('x')));
5035
- y_coord_check.push(parseFloat(this.plots[i].getAttribute('y')));
5036
- }
4935
+ for (let i = 0; i < this.elements.length; i++) {
4936
+ if (this.elements[i]) {
4937
+ // heatmap SVG containing path element instead of rect
4938
+ if (this.elements[i] instanceof SVGPathElement) {
4939
+ // Assuming the path data is in the format "M x y L x y L x y L x y"
4940
+ const path_d = this.elements[i].getAttribute('d');
4941
+ const regex = /[ML]\s*(-?\d+(\.\d+)?)\s+(-?\d+(\.\d+)?)/g;
4942
+ const match = regex.exec(path_d);
4943
+
4944
+ const coords = [Number(match[1]), Number(match[3])];
4945
+ const x = coords[0];
4946
+ const y = coords[1];
4947
+
4948
+ x_coord_check.push(parseFloat(x));
4949
+ y_coord_check.push(parseFloat(y));
4950
+ } else {
4951
+ x_coord_check.push(parseFloat(this.elements[i].getAttribute('x')));
4952
+ y_coord_check.push(parseFloat(this.elements[i].getAttribute('y')));
5037
4953
  }
5038
4954
  }
5039
-
5040
- // sort the squares to access from left to right, up to down
5041
- x_coord_check.sort(function (a, b) {
5042
- return a - b;
5043
- }); // ascending
5044
- y_coord_check.sort(function (a, b) {
5045
- return a - b;
5046
- });
5047
-
5048
- let svgScaler = this.GetSVGScaler();
5049
- // inverse scale if svg is scaled
5050
- if (svgScaler[0] == -1) {
5051
- x_coord_check = x_coord_check.reverse();
5052
- }
5053
- if (svgScaler[1] == -1) {
5054
- y_coord_check = y_coord_check.reverse();
5055
- }
5056
-
5057
- // get unique elements from x_coord and y_coord
5058
- unique_x_coord = [...new Set(x_coord_check)];
5059
- unique_y_coord = [...new Set(y_coord_check)];
5060
4955
  }
5061
4956
 
5062
- // get num of rows, num of cols, and total numbers of squares
5063
- let num_rows = 0;
5064
- let num_cols = 0;
5065
- let num_squares = 0;
5066
- if ('data' in singleMaidr) {
5067
- num_rows = singleMaidr.data.length;
5068
- num_cols = singleMaidr.data[0].length;
5069
- } else {
5070
- num_rows = unique_y_coord.length;
5071
- num_cols = unique_x_coord.length;
5072
- }
5073
- num_squares = num_rows * num_cols;
5074
-
5075
- let norms = [];
5076
- if ('data' in singleMaidr) {
5077
- norms = [...singleMaidr.data];
5078
- } else {
5079
- norms = Array(num_rows)
5080
- .fill()
5081
- .map(() => Array(num_cols).fill(0));
5082
- let min_norm = 3 * Math.pow(255, 2);
5083
- let max_norm = 0;
5084
-
5085
- for (var i = 0; i < this.plots.length; i++) {
5086
- var x_index = unique_x_coord.indexOf(x_coord_check[i]);
5087
- var y_index = unique_y_coord.indexOf(y_coord_check[i]);
5088
- let norm = this.getRGBNorm(i);
5089
- norms[y_index][x_index] = norm;
4957
+ // sort the squares to access from left to right, up to down
4958
+ x_coord_check.sort(function (a, b) {
4959
+ return a - b;
4960
+ }); // ascending
4961
+ y_coord_check.sort(function (a, b) {
4962
+ return a - b;
4963
+ });
5090
4964
 
5091
- if (norm < min_norm) min_norm = norm;
5092
- if (norm > max_norm) max_norm = norm;
5093
- }
4965
+ let svgScaler = this.GetSVGScaler();
4966
+ // inverse scale if svg has a negative scale in the actual svg
4967
+ if (svgScaler[0] == -1) {
4968
+ x_coord_check = x_coord_check.reverse();
4969
+ }
4970
+ if (svgScaler[1] == -1) {
4971
+ y_coord_check = y_coord_check.reverse();
5094
4972
  }
5095
4973
 
5096
- let plotData = [unique_x_coord, unique_y_coord, norms, num_rows, num_cols];
4974
+ // get unique elements from x_coord and y_coord
4975
+ unique_x_coord = [...new Set(x_coord_check)];
4976
+ unique_y_coord = [...new Set(y_coord_check)];
5097
4977
 
5098
- return plotData;
4978
+ this.x_coord = unique_x_coord;
4979
+ this.y_coord = unique_y_coord;
5099
4980
  }
5100
4981
 
5101
4982
  /**
5102
4983
  * Updates the constants used in the heatmap.
4984
+ * minX: 0, always
4985
+ * maxX: the x length of the data array
4986
+ * minY: the minimum value of the data array
4987
+ * maxY: the maximum value of the data array
4988
+ * autoPlayRate: the rate at which the heatmap will autoplay, based on the number of columns
4989
+ *
5103
4990
  */
5104
4991
  updateConstants() {
5105
4992
  constants.minX = 0;
5106
- constants.maxX = this.plotData[4];
5107
- constants.minY = this.plotData[2][0][0]; // initial val
5108
- constants.maxY = this.plotData[2][0][0]; // initial val
5109
- for (let i = 0; i < this.plotData[2].length; i++) {
5110
- for (let j = 0; j < this.plotData[2][i].length; j++) {
5111
- if (this.plotData[2][i][j] < constants.minY)
5112
- constants.minY = this.plotData[2][i][j];
5113
- if (this.plotData[2][i][j] > constants.maxY)
5114
- constants.maxY = this.plotData[2][i][j];
5115
- }
5116
- }
4993
+ constants.maxX = this.data[0].length - 1;
4994
+ constants.minY = Math.min(...this.data.map((row) => Math.min(...row)));
4995
+ constants.maxY = Math.max(...this.data.map((row) => Math.max(...row)));
5117
4996
  constants.autoPlayRate = Math.min(
5118
4997
  Math.ceil(constants.AUTOPLAY_DURATION / (constants.maxX + 1)),
5119
4998
  constants.MAX_SPEED
@@ -5132,7 +5011,7 @@ class HeatMap {
5132
5011
  }
5133
5012
 
5134
5013
  /**
5135
- * Returns an array of the X and Y scales of the first SVG element found in the plots array.
5014
+ * Returns an array of the X and Y scales of the first SVG element found in the elements array.
5136
5015
  * @returns {Array<number>} An array containing the X and Y scales of the SVG element.
5137
5016
  */
5138
5017
  GetSVGScaler() {
@@ -5142,7 +5021,7 @@ class HeatMap {
5142
5021
 
5143
5022
  // but first, are we even in an svg that can be scaled?
5144
5023
  let isSvg = false;
5145
- let element = this.plots[0]; // a random start, may as well be the first
5024
+ let element = this.elements[0]; // a random start, may as well be the first
5146
5025
  while (element) {
5147
5026
  if (element.tagName.toLowerCase() == 'body') {
5148
5027
  break;
@@ -5154,7 +5033,7 @@ class HeatMap {
5154
5033
  }
5155
5034
 
5156
5035
  if (isSvg) {
5157
- let element = this.plots[0]; // a random start, may as well be the first
5036
+ let element = this.elements[0]; // a random start, may as well be the first
5158
5037
  while (element) {
5159
5038
  if (element.tagName.toLowerCase() == 'body') {
5160
5039
  break;
@@ -5186,7 +5065,7 @@ class HeatMap {
5186
5065
  * @returns {number} The sum of squared values of the RGB color.
5187
5066
  */
5188
5067
  getRGBNorm(i) {
5189
- let rgb_string = this.plots[i].getAttribute('fill');
5068
+ let rgb_string = this.elements[i].getAttribute('fill');
5190
5069
  let rgb_array = rgb_string.slice(4, -1).split(',');
5191
5070
  // just get the sum of squared value of rgb, similar without sqrt, save computation
5192
5071
  return rgb_array
@@ -5344,10 +5223,10 @@ class HeatMapRect {
5344
5223
  this.x = plot.x_coord[position.x];
5345
5224
  this.y = plot.y_coord[position.y];
5346
5225
  // find which square we're on by searching for the x and y coordinates
5347
- for (let i = 0; i < plot.plots.length; i++) {
5226
+ for (let i = 0; i < plot.elements.length; i++) {
5348
5227
  if (
5349
- plot.plots[i].getAttribute('x') == this.x &&
5350
- plot.plots[i].getAttribute('y') == this.y
5228
+ plot.elements[i].getAttribute('x') == this.x &&
5229
+ plot.elements[i].getAttribute('y') == this.y
5351
5230
  ) {
5352
5231
  this.squareIndex = i;
5353
5232
  break;
@@ -5375,7 +5254,7 @@ class HeatMapRect {
5375
5254
  rect.setAttribute('stroke', constants.colorSelected);
5376
5255
  rect.setAttribute('stroke-width', this.rectStrokeWidth);
5377
5256
  rect.setAttribute('fill', 'none');
5378
- plot.plots[this.squareIndex].parentNode.appendChild(rect);
5257
+ plot.elements[this.squareIndex].parentNode.appendChild(rect);
5379
5258
  //constants.chart.appendChild(rect);
5380
5259
  }
5381
5260
  }
@@ -5391,87 +5270,12 @@ class ScatterPlot {
5391
5270
  */
5392
5271
  constructor() {
5393
5272
  this.prefix = this.GetPrefix();
5394
- // this.SetVisualHighlight();
5395
5273
  this.SetScatterLayer();
5396
5274
  this.SetLineLayer();
5397
5275
  this.SetAxes();
5398
5276
  this.svgScaler = this.GetSVGScaler();
5399
5277
  }
5400
5278
 
5401
- // SetVisualHighlight() {
5402
- // let point_index = this.GetElementIndex('point');
5403
- // let smooth_index = this.GetElementIndex('smooth');
5404
- // if (point_index && smooth_index && elements < 2) {
5405
- // logError.LogAbsentElement('point or/and smooth line elements');
5406
- // }
5407
- // if (point_index != -1) {
5408
- // this.CheckData(point_index);
5409
- // }
5410
-
5411
- // if (smooth_index != -1) {
5412
- // this.CheckData(smooth_index);
5413
- // }
5414
- // }
5415
-
5416
- // CheckData(i) {
5417
- // let elements = 'elements' in singleMaidr ? singleMaidr.elements : null;
5418
-
5419
- // // elements does not exist at all
5420
- // if (elements == null) {
5421
- // logError.LogAbsentElement('elements');
5422
- // if (i == 0) constants.hasRect = 0;
5423
- // if (i == 1) constants.hasSmooth = 0;
5424
- // return;
5425
- // }
5426
-
5427
- // // elements exists but is empty
5428
- // if (elements.length == 0) {
5429
- // logError.LogAbsentElement('elements');
5430
- // if (i == 0) constants.hasRect = 0;
5431
- // if (i == 1) constants.hasSmooth = 0;
5432
- // return;
5433
- // }
5434
-
5435
- // // elements exists but is not an array
5436
- // if (!Array.isArray(elements)) {
5437
- // logError.LogNotArray('elements');
5438
- // if (i == 0) constants.hasRect = 0;
5439
- // if (i == 1) constants.hasSmooth = 0;
5440
- // return;
5441
- // }
5442
-
5443
- // // elements.length is more than 2
5444
- // if (elements.length > 2) {
5445
- // logError.LogTooManyElements('elements', 2);
5446
- // }
5447
-
5448
- // if ('data' in singleMaidr) {
5449
- // if (i == 0) {
5450
- // // check point elements
5451
- // if (
5452
- // singleMaidr.data[i] == null ||
5453
- // singleMaidr.data[i].length != singleMaidr.elements[i].length
5454
- // ) {
5455
- // constants.hasRect = 0;
5456
- // logError.LogDifferentLengths('point data', 'point elements');
5457
- // }
5458
- // } else if (i == 1) {
5459
- // // check smooth line elements
5460
- // if (
5461
- // singleMaidr.data[i] == null ||
5462
- // (!Array.isArray(singleMaidr.data[i]) &&
5463
- // singleMaidr.data[i].length != this.chartLineX.length)
5464
- // ) {
5465
- // constants.hasSmooth = 0;
5466
- // logError.LogDifferentLengths(
5467
- // 'smooth line data',
5468
- // 'smooth line elements'
5469
- // );
5470
- // }
5471
- // }
5472
- // }
5473
- // }
5474
-
5475
5279
  /**
5476
5280
  * Sets the x and y group labels and title for the scatterplot based on the data in singleMaidr.
5477
5281
  */
@@ -5517,28 +5321,34 @@ class ScatterPlot {
5517
5321
  */
5518
5322
  SetScatterLayer() {
5519
5323
  // initially set as smooth layer (layer 2), if possible
5520
- let elIndex = this.GetElementIndex('point');
5324
+ let elIndex = this.GetElementIndex('point'); // check if we have it
5521
5325
  if (elIndex != -1) {
5522
- this.plotPoints = document.querySelectorAll(
5523
- singleMaidr.selector[elIndex]
5524
- );
5326
+ if ('selector' in singleMaidr) {
5327
+ this.plotPoints = document.querySelectorAll(
5328
+ singleMaidr.selector[elIndex]
5329
+ );
5330
+ } else if ('elements' in singleMaidr) {
5331
+ this.plotPoints = singleMaidr.elements[elIndex];
5332
+ }
5525
5333
  } else if (singleMaidr.type == 'point') {
5526
- this.plotPoints = document.querySelectorAll(singleMaidr.selector);
5334
+ if ('selector' in singleMaidr) {
5335
+ this.plotPoints = document.querySelectorAll(singleMaidr.selector);
5336
+ } else if ('elements' in singleMaidr) {
5337
+ this.plotPoints = singleMaidr.elements;
5338
+ }
5527
5339
  }
5528
- if (typeof this.plotPoints !== 'undefined') {
5529
- let svgPointCoords = this.GetSvgPointCoords();
5530
- let pointValues = this.GetPointValues();
5340
+ let svgPointCoords = this.GetSvgPointCoords();
5341
+ let pointValues = this.GetPointValues();
5531
5342
 
5532
- this.chartPointsX = svgPointCoords[0]; // x coordinates of points
5533
- this.chartPointsY = svgPointCoords[1]; // y coordinates of points
5343
+ this.chartPointsX = svgPointCoords[0]; // x coordinates of points
5344
+ this.chartPointsY = svgPointCoords[1]; // y coordinates of points
5534
5345
 
5535
- this.x = pointValues[0]; // actual values of x
5536
- this.y = pointValues[1]; // actual values of y
5346
+ this.x = pointValues[0]; // actual values of x
5347
+ this.y = pointValues[1]; // actual values of y
5537
5348
 
5538
- // for sound weight use
5539
- this.points_count = pointValues[2]; // number of each points
5540
- this.max_count = pointValues[3];
5541
- }
5349
+ // for sound weight use
5350
+ this.points_count = pointValues[2]; // number of each points
5351
+ this.max_count = pointValues[3];
5542
5352
  }
5543
5353
 
5544
5354
  /**
@@ -5546,28 +5356,34 @@ class ScatterPlot {
5546
5356
  */
5547
5357
  SetLineLayer() {
5548
5358
  // layer = 2, smooth layer (from singleMaidr types)
5549
- let elIndex = this.GetElementIndex('smooth');
5359
+ let elIndex = this.GetElementIndex('smooth'); // check if we have it
5550
5360
  if (elIndex != -1) {
5551
- this.plotLine = document.querySelectorAll(
5552
- singleMaidr.selector[elIndex]
5553
- )[0];
5361
+ if ('selector' in singleMaidr) {
5362
+ this.plotLine = document.querySelectorAll(
5363
+ singleMaidr.selector[elIndex]
5364
+ )[0];
5365
+ } else if ('elements' in singleMaidr) {
5366
+ this.plotLine = singleMaidr.elements[elIndex][0];
5367
+ }
5554
5368
  } else if (singleMaidr.type == 'smooth') {
5555
- this.plotLine = document.querySelectorAll(singleMaidr.selector);
5369
+ if ('selector' in singleMaidr) {
5370
+ this.plotLine = document.querySelectorAll(singleMaidr.selector);
5371
+ } else if ('elements' in singleMaidr) {
5372
+ this.plotLine = singleMaidr.elements;
5373
+ }
5556
5374
  }
5557
- if (typeof this.plotLine !== 'undefined') {
5558
- let svgLineCoords = this.GetSvgLineCoords();
5559
- let smoothCurvePoints = this.GetSmoothCurvePoints();
5375
+ let svgLineCoords = this.GetSvgLineCoords();
5376
+ let smoothCurvePoints = this.GetSmoothCurvePoints();
5560
5377
 
5561
- this.chartLineX = svgLineCoords[0]; // x coordinates of curve
5562
- this.chartLineY = svgLineCoords[1]; // y coordinates of curve
5378
+ this.chartLineX = svgLineCoords[0]; // x coordinates of curve
5379
+ this.chartLineY = svgLineCoords[1]; // y coordinates of curve
5563
5380
 
5564
- this.curveX = smoothCurvePoints[0]; // actual values of x
5565
- this.curvePoints = smoothCurvePoints[1]; // actual values of y
5381
+ this.curveX = smoothCurvePoints[0]; // actual values of x
5382
+ this.curvePoints = smoothCurvePoints[1]; // actual values of y
5566
5383
 
5567
- this.curveMinY = Math.min(...this.curvePoints);
5568
- this.curveMaxY = Math.max(...this.curvePoints);
5569
- this.gradient = this.GetGradient();
5570
- }
5384
+ this.curveMinY = Math.min(...this.curvePoints);
5385
+ this.curveMaxY = Math.max(...this.curvePoints);
5386
+ this.gradient = this.GetGradient();
5571
5387
  }
5572
5388
 
5573
5389
  /**
@@ -5577,13 +5393,33 @@ class ScatterPlot {
5577
5393
  GetSvgPointCoords() {
5578
5394
  let points = new Map();
5579
5395
 
5580
- for (let i = 0; i < this.plotPoints.length; i++) {
5581
- let x = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'x')); // .toFixed(1);
5582
- let y = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'y'));
5583
- if (!points.has(x)) {
5584
- points.set(x, new Set([y]));
5585
- } else {
5586
- points.get(x).add(y);
5396
+ if (this.plotPoints) {
5397
+ for (let i = 0; i < this.plotPoints.length; i++) {
5398
+ let x = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'x')); // .toFixed(1);
5399
+ let y = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'y'));
5400
+ if (!points.has(x)) {
5401
+ points.set(x, new Set([y]));
5402
+ } else {
5403
+ points.get(x).add(y);
5404
+ }
5405
+ }
5406
+ } else {
5407
+ // pull from data instead
5408
+ let elIndex = this.GetElementIndex('point');
5409
+ for (let i = 0; i < singleMaidr.data[elIndex].length; i++) {
5410
+ let x;
5411
+ let y;
5412
+ if ('x' in singleMaidr.data[elIndex][i]) {
5413
+ x = singleMaidr.data[elIndex][i]['x'];
5414
+ }
5415
+ if ('y' in singleMaidr.data[elIndex][i]) {
5416
+ y = singleMaidr.data[elIndex][i]['y'];
5417
+ }
5418
+ if (!points.has(x)) {
5419
+ points.set(x, new Set([y]));
5420
+ } else {
5421
+ points.get(x).add(y);
5422
+ }
5587
5423
  }
5588
5424
  }
5589
5425
 
@@ -5649,38 +5485,40 @@ class ScatterPlot {
5649
5485
 
5650
5486
  // but first, are we even in an svg that can be scaled?
5651
5487
  let isSvg = false;
5652
- let element = this.plotPoints[0]; // a random start, may as well be the first
5653
- while (element) {
5654
- if (element.tagName.toLowerCase() == 'body') {
5655
- break;
5656
- }
5657
- if (element.tagName && element.tagName.toLowerCase() === 'svg') {
5658
- isSvg = true;
5659
- }
5660
- element = element.parentNode;
5661
- }
5662
-
5663
- if (isSvg) {
5488
+ if (this.plotPoints) {
5664
5489
  let element = this.plotPoints[0]; // a random start, may as well be the first
5665
5490
  while (element) {
5666
5491
  if (element.tagName.toLowerCase() == 'body') {
5667
5492
  break;
5668
5493
  }
5669
- if (element.getAttribute('transform')) {
5670
- let transform = element.getAttribute('transform');
5671
- let match = transform.match(
5672
- /scale\((-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)\)/
5673
- );
5674
- if (match) {
5675
- if (!isNaN(match[1])) {
5676
- scaleX *= parseFloat(match[1]);
5677
- }
5678
- if (!isNaN(match[3])) {
5679
- scaleY *= parseFloat(match[3]);
5494
+ if (element.tagName && element.tagName.toLowerCase() === 'svg') {
5495
+ isSvg = true;
5496
+ }
5497
+ element = element.parentNode;
5498
+ }
5499
+
5500
+ if (isSvg) {
5501
+ let element = this.plotPoints[0]; // a random start, may as well be the first
5502
+ while (element) {
5503
+ if (element.tagName.toLowerCase() == 'body') {
5504
+ break;
5505
+ }
5506
+ if (element.getAttribute('transform')) {
5507
+ let transform = element.getAttribute('transform');
5508
+ let match = transform.match(
5509
+ /scale\((-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)\)/
5510
+ );
5511
+ if (match) {
5512
+ if (!isNaN(match[1])) {
5513
+ scaleX *= parseFloat(match[1]);
5514
+ }
5515
+ if (!isNaN(match[3])) {
5516
+ scaleY *= parseFloat(match[3]);
5517
+ }
5680
5518
  }
5681
5519
  }
5520
+ element = element.parentNode;
5682
5521
  }
5683
- element = element.parentNode;
5684
5522
  }
5685
5523
  }
5686
5524
 
@@ -5689,22 +5527,30 @@ class ScatterPlot {
5689
5527
 
5690
5528
  /**
5691
5529
  * Returns a prefix based on the element type.
5530
+ * This helps manipulate svg stuff, as the attribute info is slightly different depending on svg source
5692
5531
  * @returns {string} The prefix.
5693
5532
  */
5694
5533
  GetPrefix() {
5695
5534
  let pointIndex = this.GetElementIndex('point');
5696
5535
 
5697
- let element;
5536
+ let element = null;
5698
5537
  if (pointIndex != -1) {
5699
- element = document.querySelectorAll(singleMaidr.selector[pointIndex])[0];
5538
+ if ('selector' in singleMaidr) {
5539
+ element = document.querySelectorAll(
5540
+ singleMaidr.selector[pointIndex]
5541
+ )[0];
5542
+ } else if ('elements' in singleMaidr) {
5543
+ element = singleMaidr.elements[pointIndex][0];
5544
+ }
5700
5545
  } else if (singleMaidr.type == 'point') {
5701
- element = document.querySelectorAll(singleMaidr.selector)[0];
5546
+ if ('selector' in singleMaidr) {
5547
+ element = document.querySelectorAll(singleMaidr.selector)[0];
5548
+ } else if ('elements' in singleMaidr) {
5549
+ element = singleMaidr.elements[0];
5550
+ }
5702
5551
  }
5703
5552
  let prefix = '';
5704
- if (
5705
- 'selector' in singleMaidr &&
5706
- element.tagName.toLowerCase() === 'circle'
5707
- ) {
5553
+ if (element && element.tagName.toLowerCase() === 'circle') {
5708
5554
  prefix = 'c';
5709
5555
  }
5710
5556
  return prefix;
@@ -5857,20 +5703,32 @@ class ScatterPlot {
5857
5703
  * @returns {Array<Array<number>>} An array containing two arrays: the x-coordinates and y-coordinates.
5858
5704
  */
5859
5705
  GetSvgLineCoords() {
5860
- // extract all the y coordinates from the point attribute of polyline
5861
- let str = this.plotLine.getAttribute('points');
5862
- let coords = str.split(' ');
5863
-
5864
- let X = [];
5865
- let Y = [];
5706
+ let x_points = [];
5707
+ let y_points = [];
5866
5708
 
5867
- for (let i = 0; i < coords.length; i++) {
5868
- let coord = coords[i].split(',');
5869
- X.push(parseFloat(coord[0]));
5870
- Y.push(parseFloat(coord[1]));
5709
+ if (this.plotLine) {
5710
+ // extract all the y coordinates from the point attribute of polyline
5711
+ let str = this.plotLine.getAttribute('points');
5712
+ let coords = str.split(' ');
5713
+ for (let i = 0; i < coords.length; i++) {
5714
+ let coord = coords[i].split(',');
5715
+ x_points.push(parseFloat(coord[0]));
5716
+ y_points.push(parseFloat(coord[1]));
5717
+ }
5718
+ } else {
5719
+ // fetch from data instead
5720
+ let elIndex = this.GetElementIndex('point');
5721
+ for (let i = 0; i < singleMaidr.data[elIndex].length; i++) {
5722
+ if ('x' in singleMaidr.data[elIndex][i]) {
5723
+ x_points.push(singleMaidr.data[elIndex][i]['x']);
5724
+ }
5725
+ if ('y' in singleMaidr.data[elIndex][i]) {
5726
+ y_points.push(singleMaidr.data[elIndex][i]['y']);
5727
+ }
5728
+ }
5871
5729
  }
5872
5730
 
5873
- return [X, Y];
5731
+ return [x, y];
5874
5732
  }
5875
5733
 
5876
5734
  /**
@@ -5932,6 +5790,24 @@ class ScatterPlot {
5932
5790
 
5933
5791
  return gradients;
5934
5792
  }
5793
+
5794
+ /**
5795
+ * Returns whether or not we have elements / selectors for the given type.
5796
+ * @param {string} type - The type of element to check for. eg, 'point' or 'smooth'.
5797
+ * @returns {boolean} - True if we have elements / selectors for the given type, false otherwise.
5798
+ * @function
5799
+ * @memberof scatterplot
5800
+ */
5801
+ GetRectStatus(type) {
5802
+ let elIndex = this.GetElementIndex(type);
5803
+ if ('selector' in singleMaidr) {
5804
+ return singleMaidr.selector[elIndex] ? true : false;
5805
+ } else if ('elements' in singleMaidr) {
5806
+ return singleMaidr.elements[elIndex] ? true : false;
5807
+ } else {
5808
+ return false;
5809
+ }
5810
+ }
5935
5811
  }
5936
5812
 
5937
5813
  /**
@@ -5950,6 +5826,7 @@ class Layer0Point {
5950
5826
  this.y = plot.chartPointsY[0];
5951
5827
  this.strokeWidth = 1.35;
5952
5828
  this.circleIndex = [];
5829
+ this.hasRect = plot.GetRectStatus('point');
5953
5830
  }
5954
5831
 
5955
5832
  /**
@@ -6049,6 +5926,7 @@ class Layer1Point {
6049
5926
  this.x = plot.chartLineX[0];
6050
5927
  this.y = plot.chartLineY[0];
6051
5928
  this.strokeWidth = 1.35;
5929
+ this.hasRect = plot.GetRectStatus('point');
6052
5930
  }
6053
5931
 
6054
5932
  /**
@@ -6139,6 +6017,8 @@ class Histogram {
6139
6017
  this.bars = null;
6140
6018
  if ('selector' in singleMaidr) {
6141
6019
  this.bars = document.querySelectorAll(singleMaidr.selector);
6020
+ } else if ('elements' in singleMaidr) {
6021
+ this.bars = singleMaidr.elements;
6142
6022
  }
6143
6023
 
6144
6024
  // labels (optional)
@@ -6252,9 +6132,31 @@ class Histogram {
6252
6132
  if (this.bars) {
6253
6133
  this.activeElement = this.bars[position.x];
6254
6134
  if (this.activeElement) {
6255
- this.activeElementColor = this.activeElement.getAttribute('fill');
6256
- let newColor = constants.GetBetterColor(this.activeElementColor);
6257
- this.activeElement.setAttribute('fill', newColor);
6135
+ // Case where fill is a direct attribute
6136
+ if (this.activeElement.hasAttribute('fill')) {
6137
+ this.activeElementColor = this.activeElement.getAttribute('fill');
6138
+ // Get new color to highlight and replace fill value
6139
+ this.activeElement.setAttribute(
6140
+ 'fill',
6141
+ constants.GetBetterColor(this.activeElementColor)
6142
+ );
6143
+ // Case where fill is within the style attribute
6144
+ } else if (
6145
+ this.activeElement.hasAttribute('style') &&
6146
+ this.activeElement.getAttribute('style').indexOf('fill') !== -1
6147
+ ) {
6148
+ let styleString = this.activeElement.getAttribute('style');
6149
+ // Extract all style attributes and values
6150
+ let styleArray = constants.GetStyleArrayFromString(styleString);
6151
+ this.activeElementColor = styleArray[styleArray.indexOf('fill') + 1];
6152
+ // Get new color to highlight and replace fill value in style array
6153
+ styleArray[styleArray.indexOf('fill') + 1] = constants.GetBetterColor(
6154
+ this.activeElementColor
6155
+ );
6156
+ // Recreate style string and set style attribute
6157
+ styleString = constants.GetStyleStringFromArray(styleArray);
6158
+ this.activeElement.setAttribute('style', styleString);
6159
+ }
6258
6160
  }
6259
6161
  }
6260
6162
  }
@@ -6270,8 +6172,21 @@ class Histogram {
6270
6172
  UnSelectPrevious() {
6271
6173
  if (this.activeElement) {
6272
6174
  // set fill attribute to the original color
6273
- this.activeElement.setAttribute('fill', this.activeElementColor);
6274
- this.activeElement = null;
6175
+ if (this.activeElement.hasAttribute('fill')) {
6176
+ this.activeElement.setAttribute('fill', this.activeElementColor);
6177
+ this.activeElement = null;
6178
+ } else if (
6179
+ this.activeElement.hasAttribute('style') &&
6180
+ this.activeElement.getAttribute('style').indexOf('fill') !== -1
6181
+ ) {
6182
+ let styleString = this.activeElement.getAttribute('style');
6183
+ let styleArray = constants.GetStyleArrayFromString(styleString);
6184
+ styleArray[styleArray.indexOf('fill') + 1] = this.activeElementColor;
6185
+ // Recreate style string and set style attribute
6186
+ styleString = constants.GetStyleStringFromArray(styleArray);
6187
+ this.activeElement.setAttribute('style', styleString);
6188
+ this.activeElement = null;
6189
+ }
6275
6190
  }
6276
6191
  }
6277
6192
  }
@@ -6288,57 +6203,7 @@ class LinePlot {
6288
6203
  constructor() {
6289
6204
  this.SetLineLayer();
6290
6205
  this.SetAxes();
6291
-
6292
- let legendX = '';
6293
- let legendY = '';
6294
- if ('axes' in singleMaidr) {
6295
- // legend labels
6296
- if (singleMaidr.axes.x) {
6297
- if (singleMaidr.axes.x.label) {
6298
- if (legendX == '') {
6299
- legendX = singleMaidr.axes.x.label;
6300
- }
6301
- }
6302
- }
6303
- if (singleMaidr.axes.y) {
6304
- if (singleMaidr.axes.y.label) {
6305
- if (legendY == '') {
6306
- legendY = singleMaidr.axes.y.label;
6307
- }
6308
- }
6309
- }
6310
- }
6311
-
6312
- this.plotLegend = {
6313
- x: legendX,
6314
- y: legendY,
6315
- };
6316
-
6317
- // title
6318
- this.title = '';
6319
- if ('labels' in singleMaidr) {
6320
- if ('title' in singleMaidr.labels) {
6321
- this.title = singleMaidr.labels.title;
6322
- }
6323
- }
6324
- if (this.title == '') {
6325
- if ('title' in singleMaidr) {
6326
- this.title = singleMaidr.title;
6327
- }
6328
- }
6329
-
6330
- // subtitle
6331
- if ('labels' in singleMaidr) {
6332
- if ('subtitle' in singleMaidr.labels) {
6333
- this.subtitle = singleMaidr.labels.subtitle;
6334
- }
6335
- }
6336
- // caption
6337
- if ('labels' in singleMaidr) {
6338
- if ('caption' in singleMaidr.labels) {
6339
- this.caption = singleMaidr.labels.caption;
6340
- }
6341
- }
6206
+ this.UpdateConstants();
6342
6207
  }
6343
6208
 
6344
6209
  /**
@@ -6348,12 +6213,13 @@ class LinePlot {
6348
6213
  let elements;
6349
6214
  if ('selector' in singleMaidr) {
6350
6215
  elements = document.querySelectorAll(singleMaidr.selector);
6216
+ } else if ('elements' in singleMaidr) {
6217
+ elements = singleMaidr.elements;
6351
6218
  }
6352
6219
 
6353
- let len = elements.length;
6354
- this.plotLine = elements[len - 1];
6220
+ if (elements) {
6221
+ this.plotLine = elements[elements.length - 1];
6355
6222
 
6356
- if (typeof this.plotLine !== 'undefined') {
6357
6223
  let pointCoords = this.GetPointCoords();
6358
6224
  let pointValues = this.GetPoints();
6359
6225
 
@@ -6365,35 +6231,33 @@ class LinePlot {
6365
6231
 
6366
6232
  this.curveMinY = Math.min(...this.pointValuesY);
6367
6233
  this.curveMaxY = Math.max(...this.pointValuesY);
6368
- constants.minX = 0;
6369
- constants.maxX = this.pointValuesX.length - 1;
6370
- constants.minY = this.curveMinY;
6371
- constants.maxY = this.curveMaxY;
6372
-
6373
- constants.autoPlayRate = Math.min(
6374
- Math.ceil(constants.AUTOPLAY_DURATION / (constants.maxX + 1)),
6375
- constants.MAX_SPEED
6376
- );
6377
- constants.DEFAULT_SPEED = constants.autoPlayRate;
6378
- if (constants.autoPlayRate < constants.MIN_SPEED) {
6379
- constants.MIN_SPEED = constants.autoPlayRate;
6380
- }
6381
-
6382
- // this.gradient = this.GetGradient();
6383
6234
  }
6384
6235
  }
6385
6236
 
6386
6237
  /**
6387
- * Sets the minimum and maximum values for the x and y axes of a line plot.
6238
+ * Updates the constants for the line plot.
6239
+ * This includes the minimum and maximum x and y values, the autoplay rate, and the default speed.
6388
6240
  */
6389
- SetMinMax() {
6241
+ UpdateConstants() {
6390
6242
  constants.minX = 0;
6391
- constants.maxX = this.pointValuesX.length - 1;
6392
- constants.minY = this.curveMinY;
6393
- constants.maxY = this.curveMaxY;
6394
- constants.autoPlayRate = Math.ceil(
6395
- constants.AUTOPLAY_DURATION / (constants.maxX + 1)
6243
+ constants.maxX = singleMaidr.data.length - 1;
6244
+ constants.minY = singleMaidr.data.reduce(
6245
+ (min, item) => (item.y < min ? item.y : min),
6246
+ singleMaidr.data[0].y
6396
6247
  );
6248
+ constants.maxY = singleMaidr.data.reduce(
6249
+ (max, item) => (item.y > max ? item.y : max),
6250
+ singleMaidr.data[0].y
6251
+ );
6252
+
6253
+ constants.autoPlayRate = Math.min(
6254
+ Math.ceil(constants.AUTOPLAY_DURATION / (constants.maxX + 1)),
6255
+ constants.MAX_SPEED
6256
+ );
6257
+ constants.DEFAULT_SPEED = constants.autoPlayRate;
6258
+ if (constants.autoPlayRate < constants.MIN_SPEED) {
6259
+ constants.MIN_SPEED = constants.autoPlayRate;
6260
+ }
6397
6261
  }
6398
6262
 
6399
6263
  /**
@@ -6449,46 +6313,60 @@ class LinePlot {
6449
6313
  }
6450
6314
  }
6451
6315
 
6452
- // GetGradient() {
6453
- // let gradients = [];
6454
-
6455
- // for (let i = 0; i < this.pointValuesY.length - 1; i++) {
6456
- // let abs_grad = Math.abs(
6457
- // (this.pointValuesY[i + 1] - this.pointValuesY[i]) /
6458
- // (this.pointValuesX[i + 1] - this.pointValuesX[i])
6459
- // ).toFixed(3);
6460
- // gradients.push(abs_grad);
6461
- // }
6462
-
6463
- // gradients.push('end');
6464
-
6465
- // return gradients;
6466
- // }
6467
-
6468
6316
  /**
6469
6317
  * Sets the x and y group labels and title for the line plot based on the axes and title properties of the singleMaidr object.
6470
6318
  */
6471
6319
  SetAxes() {
6472
- this.x_group_label = '';
6473
- this.y_group_label = '';
6474
- this.title = '';
6320
+ let legendX = '';
6321
+ let legendY = '';
6475
6322
  if ('axes' in singleMaidr) {
6476
- if ('x' in singleMaidr.axes) {
6477
- if (this.x_group_label == '') {
6478
- this.x_group_label = singleMaidr.axes.x.label;
6323
+ // legend labels
6324
+ if (singleMaidr.axes.x) {
6325
+ if (singleMaidr.axes.x.label) {
6326
+ if (legendX == '') {
6327
+ legendX = singleMaidr.axes.x.label;
6328
+ }
6479
6329
  }
6480
6330
  }
6481
- if ('y' in singleMaidr.axes) {
6482
- if (this.y_group_label == '') {
6483
- this.y_group_label = singleMaidr.axes.y.label;
6331
+ if (singleMaidr.axes.y) {
6332
+ if (singleMaidr.axes.y.label) {
6333
+ if (legendY == '') {
6334
+ legendY = singleMaidr.axes.y.label;
6335
+ }
6484
6336
  }
6485
6337
  }
6486
6338
  }
6487
- if ('title' in singleMaidr) {
6488
- if (this.title == '') {
6339
+
6340
+ this.plotLegend = {
6341
+ x: legendX,
6342
+ y: legendY,
6343
+ };
6344
+
6345
+ // title
6346
+ this.title = '';
6347
+ if ('labels' in singleMaidr) {
6348
+ if ('title' in singleMaidr.labels) {
6349
+ this.title = singleMaidr.labels.title;
6350
+ }
6351
+ }
6352
+ if (this.title == '') {
6353
+ if ('title' in singleMaidr) {
6489
6354
  this.title = singleMaidr.title;
6490
6355
  }
6491
6356
  }
6357
+
6358
+ // subtitle
6359
+ if ('labels' in singleMaidr) {
6360
+ if ('subtitle' in singleMaidr.labels) {
6361
+ this.subtitle = singleMaidr.labels.subtitle;
6362
+ }
6363
+ }
6364
+ // caption
6365
+ if ('labels' in singleMaidr) {
6366
+ if ('caption' in singleMaidr.labels) {
6367
+ this.caption = singleMaidr.labels.caption;
6368
+ }
6369
+ }
6492
6370
  }
6493
6371
 
6494
6372
  /**
@@ -6579,7 +6457,6 @@ class Segmented {
6579
6457
  */
6580
6458
  constructor() {
6581
6459
  // initialize variables level, data, and elements
6582
- let level = null;
6583
6460
  let fill = null;
6584
6461
  let data = null;
6585
6462
  let elements = null;
@@ -6587,17 +6464,17 @@ class Segmented {
6587
6464
  //axes.x.level
6588
6465
  if ('x' in singleMaidr.axes) {
6589
6466
  if ('level' in singleMaidr.axes.x) {
6590
- level = singleMaidr.axes.x.level;
6467
+ this.level = singleMaidr.axes.x.level;
6591
6468
  }
6592
6469
  } else if ('y' in singleMaidr.axes) {
6593
6470
  if ('level' in singleMaidr.axes.y) {
6594
- level = singleMaidr.axes.y.level;
6471
+ this.level = singleMaidr.axes.y.level;
6595
6472
  }
6596
6473
  }
6597
6474
  // axes.fill
6598
6475
  if ('fill' in singleMaidr.axes) {
6599
6476
  if ('level' in singleMaidr.axes.fill) {
6600
- fill = singleMaidr.axes.fill.level;
6477
+ this.fill = singleMaidr.axes.fill.level;
6601
6478
  }
6602
6479
  }
6603
6480
  }
@@ -6606,6 +6483,8 @@ class Segmented {
6606
6483
  }
6607
6484
  if ('selector' in singleMaidr) {
6608
6485
  elements = document.querySelectorAll(singleMaidr.selector);
6486
+ } else if ('elements' in singleMaidr) {
6487
+ elements = singleMaidr.elements;
6609
6488
  }
6610
6489
 
6611
6490
  // gracefull failure: must have level + fill + data, elements optional
@@ -6613,9 +6492,10 @@ class Segmented {
6613
6492
  logError.LogAbsentElement('elements');
6614
6493
  constants.hasRect = 0;
6615
6494
  }
6616
- if (level != null && fill != null && data != null) {
6617
- this.level = level;
6618
- this.fill = fill.reverse(); // typically fill is in reverse order
6495
+ if (data) {
6496
+ if (this.fill) {
6497
+ this.fill = this.fill.reverse(); // typically fill is in reverse order
6498
+ }
6619
6499
  let dataAndELements = this.ParseData(data, elements);
6620
6500
  this.plotData = dataAndELements[0];
6621
6501
  this.elements = dataAndELements[1];
@@ -6703,7 +6583,12 @@ class Segmented {
6703
6583
  let plotData = [];
6704
6584
  let plotElements = [];
6705
6585
 
6706
- if (elements.length != data.length) {
6586
+ // override and kill elements if not same length as data
6587
+ if (elements) {
6588
+ if (elements.length != data.length) {
6589
+ plotElements = null;
6590
+ }
6591
+ } else {
6707
6592
  plotElements = null;
6708
6593
  }
6709
6594
 
@@ -6733,7 +6618,9 @@ class Segmented {
6733
6618
  // set actual values
6734
6619
  if (data[k].x == this.level[i] && data[k].fill == this.fill[j]) {
6735
6620
  plotData[i][j] = data[k].y;
6736
- plotElements[i][j] = elements[k];
6621
+ if (elements) {
6622
+ plotElements[i][j] = elements[k];
6623
+ }
6737
6624
  break;
6738
6625
  }
6739
6626
  }
@@ -6784,6 +6671,7 @@ class Segmented {
6784
6671
  if (constants.sonifMode == 'on') {
6785
6672
  // we play a run of tones
6786
6673
  position.z = 0;
6674
+ constants.KillSepPlay();
6787
6675
  constants.sepPlayId = setInterval(
6788
6676
  function () {
6789
6677
  // play this tone
@@ -7428,10 +7316,8 @@ class Control {
7428
7316
  window.position = new Position(-1, plot.plotData.length);
7429
7317
  }
7430
7318
  let rect;
7431
- constants.hasRect = false;
7432
- if ('selector' in singleMaidr) {
7319
+ if (constants.hasRect) {
7433
7320
  rect = new BoxplotRect();
7434
- constants.hasRect = true;
7435
7321
  }
7436
7322
  let lastPlayed = '';
7437
7323
 
@@ -8380,7 +8266,7 @@ class Control {
8380
8266
  return new Promise((resolve) => setTimeout(resolve, time));
8381
8267
  }
8382
8268
 
8383
- // helper functions
8269
+ // heat helper functions
8384
8270
  function lockPosition() {
8385
8271
  // lock to min / max postions
8386
8272
  let didLockHappen = false;
@@ -8783,7 +8669,7 @@ class Control {
8783
8669
  if (constants.showDisplay) {
8784
8670
  display.displayValues();
8785
8671
  }
8786
- if (constants.showRect) {
8672
+ if (layer0Point.hasRect) {
8787
8673
  layer0Point.UpdatePointDisplay();
8788
8674
  }
8789
8675
  if (constants.sonifMode != 'off') {
@@ -8796,9 +8682,9 @@ class Control {
8796
8682
  display.displayValues();
8797
8683
  }
8798
8684
  if (constants.showRect) {
8799
- if (constants.chartType == 'point') {
8685
+ if (constants.chartType == 'point' && layer0Point.hasRect) {
8800
8686
  layer0Point.UpdatePointDisplay();
8801
- } else {
8687
+ } else if (constants.chartType == 'smooth' && layer1Point.hasRect) {
8802
8688
  layer1Point.UpdatePointDisplay();
8803
8689
  }
8804
8690
  }
@@ -8813,7 +8699,7 @@ class Control {
8813
8699
  if (constants.showDisplayInBraille) {
8814
8700
  display.displayValues();
8815
8701
  }
8816
- if (constants.showRect) {
8702
+ if (layer1Point.hasRect) {
8817
8703
  layer1Point.UpdatePointDisplay();
8818
8704
  }
8819
8705
  if (constants.sonifMode != 'off') {