maidr 1.0.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.
Files changed (206) hide show
  1. package/.Rbuildignore +1 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.json +6 -0
  4. package/.github/workflows/build.yml +20 -0
  5. package/.prettierignore +3 -0
  6. package/.prettierrc.json +7 -0
  7. package/.vscode/extensions.json +25 -0
  8. package/.vscode/settings.json +30 -0
  9. package/.vscode/tasks.json +57 -0
  10. package/CHANGELOG.md +7 -0
  11. package/CITATION.cff +21 -0
  12. package/CONTRIBUTING.md +87 -0
  13. package/LICENSE.md +595 -0
  14. package/README.md +341 -0
  15. package/dist/maidr.js +8851 -0
  16. package/dist/maidr.min.js +1 -0
  17. package/dist/styles.css +244 -0
  18. package/dist/styles.min.css +1 -0
  19. package/docs/Audio.html +1398 -0
  20. package/docs/Constants.html +256 -0
  21. package/docs/Description.html +582 -0
  22. package/docs/Helper.html +364 -0
  23. package/docs/LogError.html +905 -0
  24. package/docs/Menu.html +665 -0
  25. package/docs/Position.html +174 -0
  26. package/docs/Resources.html +338 -0
  27. package/docs/Review.html +333 -0
  28. package/docs/Tracker.html +965 -0
  29. package/docs/audio.js.html +635 -0
  30. package/docs/constants.js.html +1242 -0
  31. package/docs/display.js.html +1184 -0
  32. package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
  33. package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
  34. package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
  35. package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  36. package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  37. package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  38. package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
  39. package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
  40. package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
  41. package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
  42. package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
  43. package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
  44. package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  45. package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  46. package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  47. package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
  48. package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
  49. package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
  50. package/docs/fonts/OpenSans-Semibold-webfont.eot +0 -0
  51. package/docs/fonts/OpenSans-Semibold-webfont.svg +1830 -0
  52. package/docs/fonts/OpenSans-Semibold-webfont.ttf +0 -0
  53. package/docs/fonts/OpenSans-Semibold-webfont.woff +0 -0
  54. package/docs/fonts/OpenSans-SemiboldItalic-webfont.eot +0 -0
  55. package/docs/fonts/OpenSans-SemiboldItalic-webfont.svg +1830 -0
  56. package/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
  57. package/docs/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
  58. package/docs/index.html +66 -0
  59. package/docs/scripts/linenumber.js +25 -0
  60. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  61. package/docs/scripts/prettify/lang-css.js +2 -0
  62. package/docs/scripts/prettify/prettify.js +28 -0
  63. package/docs/styles/jsdoc-default.css +692 -0
  64. package/docs/styles/prettify-jsdoc.css +111 -0
  65. package/docs/styles/prettify-tomorrow.css +132 -0
  66. package/examples/dev_charts/barplot.html +1056 -0
  67. package/examples/dev_charts/boxplot.html +1856 -0
  68. package/examples/dev_charts/boxplot_flipped.svg +727 -0
  69. package/examples/dev_charts/heatmap.html +1217 -0
  70. package/examples/dev_charts/scatterplot/displ.js +18 -0
  71. package/examples/dev_charts/scatterplot/histogram_for_residual.svg +595 -0
  72. package/examples/dev_charts/scatterplot/hwy.js +15 -0
  73. package/examples/dev_charts/scatterplot/layers/point_layer.json +938 -0
  74. package/examples/dev_charts/scatterplot/layers/smooth_layer.json +322 -0
  75. package/examples/dev_charts/scatterplot/point_layer.js +938 -0
  76. package/examples/dev_charts/scatterplot/prediction_array.js +31 -0
  77. package/examples/dev_charts/scatterplot/prediction_array.json +31 -0
  78. package/examples/dev_charts/scatterplot/residual_array.js +29 -0
  79. package/examples/dev_charts/scatterplot/residual_array.json +29 -0
  80. package/examples/dev_charts/scatterplot/scatterplot.svg +1428 -0
  81. package/examples/dev_charts/scatterplot/scatterplot_data.html +2838 -0
  82. package/examples/dev_charts/scatterplot/scatterplot_no_jitter_point_only.svg +1393 -0
  83. package/examples/dev_charts/scatterplot/scatterplot_no_jitter_with_bestfit.svg +1424 -0
  84. package/examples/dev_charts/scatterplot/scatterplot_no_jitter_with_loess_curve.svg +1402 -0
  85. package/examples/dev_charts/scatterplot/smooth_layer.js +322 -0
  86. package/examples/dev_charts/scatterplot.html +4560 -0
  87. package/examples/dodged_bar/dodged_bar.png +0 -0
  88. package/examples/dodged_bar/dodged_bar.svg +198 -0
  89. package/examples/dodged_bar/schema.json +41 -0
  90. package/examples/histogram/histogram_tutorial.svg +482 -0
  91. package/examples/histogram/histogram_tutorial_raw_data.json +362 -0
  92. package/examples/histogram/histogram_user_study.svg +578 -0
  93. package/examples/histogram/histogram_user_study_raw_data.json +362 -0
  94. package/examples/lineplot/lineplot_sample.svg +126 -0
  95. package/examples/lineplot/lineplot_sample_raw_data.json +1 -0
  96. package/examples/lineplot/point+lineplot_sample.svg +700 -0
  97. package/examples/other/audio_oscillator_boxplot.js +95 -0
  98. package/examples/other/barplot_labels.svg +314 -0
  99. package/examples/other/barplot_user_study.svg +313 -0
  100. package/examples/other/boxplot.html +927 -0
  101. package/examples/other/boxplot_data_frame.html +568 -0
  102. package/examples/other/boxplot_label.svg +751 -0
  103. package/examples/other/braille-display_boxplot.js +79 -0
  104. package/examples/other/control_boxplot.js +55 -0
  105. package/examples/other/draft.js +56 -0
  106. package/examples/other/getData.html +400 -0
  107. package/examples/other/getData.js +41 -0
  108. package/examples/other/ggplot_to_svg.R +371 -0
  109. package/examples/other/heatmap.svg +582 -0
  110. package/examples/other/heatmap_label.svg +608 -0
  111. package/examples/other/multiple_barplot.html +2250 -0
  112. package/examples/other/new_scatterplot_user_study_point_layer.json +122 -0
  113. package/examples/other/py_binder_output.html +1167 -0
  114. package/examples/other/scatterplot_label.svg +1429 -0
  115. package/examples/other/seaborn_plot.py +9 -0
  116. package/examples/other/svglite_bar.svg +136 -0
  117. package/examples/other/tutorial_boxplot.svg +727 -0
  118. package/examples/other/tutorial_boxplot_data.json +72 -0
  119. package/examples/other/user_study_boxplot.svg +676 -0
  120. package/examples/stacked_bar/schema.json +41 -0
  121. package/examples/stacked_bar/stack_bar.png +0 -0
  122. package/examples/stacked_bar/stacked_bar.svg +180 -0
  123. package/examples/stacked_normalized_bar/stacked_normalized_bar.png +0 -0
  124. package/examples/stacked_normalized_bar/stacked_normalized_bar.svg +189 -0
  125. package/examples/static/barplot.svg +263 -0
  126. package/examples/static/barplot_diamonds_gridSVG.svg +254 -0
  127. package/examples/static/boxplot.svg +424 -0
  128. package/examples/static/heatmap.svg +373 -0
  129. package/examples/static/heatmap_penguins_table.html +486 -0
  130. package/examples/static/scatterplot.svg +530 -0
  131. package/examples/svglite/task_heatmap.html +802 -0
  132. package/examples/svglite/task_heatmap.svg +111 -0
  133. package/examples/svglite/tutorial_bar.svg +136 -0
  134. package/examples/svglite/tutorial_bar_plot.html +504 -0
  135. package/examples/svglite/tutorial_boxplot.html +1850 -0
  136. package/examples/svglite/tutorial_boxplot.svg +727 -0
  137. package/examples/svglite/tutorial_scatterplot.html +3135 -0
  138. package/examples/svglite/tutorial_scatterplot.svg +311 -0
  139. package/gulpfile.js +49 -0
  140. package/index.html +40 -0
  141. package/jsconfig.json +10 -0
  142. package/jsdoc.json +19 -0
  143. package/package.json +47 -0
  144. package/src/css/styles.css +241 -0
  145. package/src/js/__tests__/audio.test.js +49 -0
  146. package/src/js/__tests__/constants.test.js +622 -0
  147. package/src/js/audio.js +575 -0
  148. package/src/js/barplot.js +254 -0
  149. package/src/js/boxplot.js +682 -0
  150. package/src/js/constants.js +1182 -0
  151. package/src/js/controls.js +3182 -0
  152. package/src/js/display.js +1124 -0
  153. package/src/js/heatmap.js +411 -0
  154. package/src/js/histogram.js +134 -0
  155. package/src/js/init.js +427 -0
  156. package/src/js/lineplot.js +219 -0
  157. package/src/js/scatterplot.js +619 -0
  158. package/src/js/segmented.js +268 -0
  159. package/user_study_pilot/binder_test.html +526 -0
  160. package/user_study_pilot/data/barplot_user_study.svg +492 -0
  161. package/user_study_pilot/data/barplot_user_study_raw_data.json +22 -0
  162. package/user_study_pilot/data/boxplot_tutorial.json +72 -0
  163. package/user_study_pilot/data/boxplot_tutorial_horizontal.svg +727 -0
  164. package/user_study_pilot/data/boxplot_user_study.json +52 -0
  165. package/user_study_pilot/data/boxplot_user_study_vertical.svg +675 -0
  166. package/user_study_pilot/data/boxplot_user_study_vertical_horizontal.svg +676 -0
  167. package/user_study_pilot/data/heatmap_user_study.svg +719 -0
  168. package/user_study_pilot/data/heatmap_user_study_raw_data.json +127 -0
  169. package/user_study_pilot/data/new_barplot_user_study.svg +269 -0
  170. package/user_study_pilot/data/new_heatmap_user_study.svg +367 -0
  171. package/user_study_pilot/data/new_scatterplot_user_study.svg +603 -0
  172. package/user_study_pilot/data/new_scatterplot_user_study_point_layer.json +122 -0
  173. package/user_study_pilot/data/scatterplot_user_study (1).svg +321 -0
  174. package/user_study_pilot/data/scatterplot_user_study.svg +603 -0
  175. package/user_study_pilot/data/scatterplot_user_study_point_layer.json +122 -0
  176. package/user_study_pilot/data/scatterplot_user_study_smooth_layer.json +322 -0
  177. package/user_study_pilot/intro.html +215 -0
  178. package/user_study_pilot/jaws_settings/Chrome.JDF +10 -0
  179. package/user_study_pilot/jaws_settings/Firefox.JDF +10 -0
  180. package/user_study_pilot/jaws_settings/backup_utf8/Chrome.JDF +10 -0
  181. package/user_study_pilot/jaws_settings/backup_utf8/Firefox.JDF +10 -0
  182. package/user_study_pilot/jaws_settings/backup_utf8/msedge.JDF +10 -0
  183. package/user_study_pilot/jaws_settings/msedge.JDF +10 -0
  184. package/user_study_pilot/nvda_settings/chrome.dic +10 -0
  185. package/user_study_pilot/nvda_settings/default.dic +10 -0
  186. package/user_study_pilot/nvda_settings/firefox.dic +10 -0
  187. package/user_study_pilot/nvda_settings/msedge.dic +10 -0
  188. package/user_study_pilot/scatterplot.html +4560 -0
  189. package/user_study_pilot/seaborn_test.html +1059 -0
  190. package/user_study_pilot/svglite_test.html +534 -0
  191. package/user_study_pilot/task1_bar_plot.html +1111 -0
  192. package/user_study_pilot/task2_heatmap.html +1661 -0
  193. package/user_study_pilot/task3_boxplot_horizontal.html +1690 -0
  194. package/user_study_pilot/task3_boxplot_vertical.html +1689 -0
  195. package/user_study_pilot/task4_scatterplot.html +2091 -0
  196. package/user_study_pilot/tutorial1_bar_plot.html +1159 -0
  197. package/user_study_pilot/tutorial2_heatmap.html +1276 -0
  198. package/user_study_pilot/tutorial3_boxplot_horizontal.html +1861 -0
  199. package/user_study_pilot/tutorial3_boxplot_vertical.html +1807 -0
  200. package/user_study_pilot/tutorial4_scatterplot.html +5893 -0
  201. package/user_study_pilot/tutorial5_histogram.html +1553 -0
  202. package/user_study_pilot/tutorial6_lineplot.html +1011 -0
  203. package/user_study_pilot/tutorial7_stacked.html +763 -0
  204. package/user_study_pilot/tutorial8_stacked_normalized.html +796 -0
  205. package/user_study_pilot/tutorial9_dodged_bar.html +831 -0
  206. package/user_study_pilot/voiceover_settings/user_study_VoiceOver Archive.voprefs +573 -0
@@ -0,0 +1,1242 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>constants.js - Documentation</title>
7
+
8
+ <script src="scripts/prettify/prettify.js"></script>
9
+ <script src="scripts/prettify/lang-css.js"></script>
10
+ <!--[if lt IE 9]>
11
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
12
+ <![endif]-->
13
+ <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
16
+ </head>
17
+ <body>
18
+
19
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
20
+ <label for="nav-trigger" class="navicon-button x">
21
+ <div class="navicon"></div>
22
+ </label>
23
+
24
+ <label for="nav-trigger" class="overlay"></label>
25
+
26
+ <nav>
27
+ <li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Classes</li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Audio.html">Audio</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Audio.html#KillSmooth">KillSmooth</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Audio.html#PlayNull">PlayNull</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Audio.html#SlideBetween">SlideBetween</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Audio.html#compressorSetup">compressorSetup</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Audio.html#playOscillator">playOscillator</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Audio.html#playSmooth">playSmooth</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Audio.html#playTone">playTone</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Constants.html">Constants</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Description.html">Description</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Description.html#CreateComponent">CreateComponent</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Description.html#Destroy">Destroy</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Description.html#PopulateData">PopulateData</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Description.html#Toggle">Toggle</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Helper.html">Helper</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Helper.html#.containsObject">containsObject</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="LogError.html">LogError</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="LogError.html#LogAbsentElement">LogAbsentElement</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="LogError.html#LogCriticalElement">LogCriticalElement</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="LogError.html#LogDifferentLengths">LogDifferentLengths</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="LogError.html#LogNotArray">LogNotArray</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="LogError.html#LogTooManyElements">LogTooManyElements</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Menu.html">Menu</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Menu.html#CreateMenu">CreateMenu</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Menu.html#LoadDataFromLocalStorage">LoadDataFromLocalStorage</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Menu.html#PopulateData">PopulateData</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Menu.html#SaveData">SaveData</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Menu.html#Toggle">Toggle</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Position.html">Position</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Resources.html">Resources</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Resources.html#GetString">GetString</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Review.html">Review</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Review.html#ToggleReviewMode">ToggleReviewMode</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Tracker.html">Tracker</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Tracker.html#DataSetup">DataSetup</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Tracker.html#Delete">Delete</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Tracker.html#DownloadTrackerData">DownloadTrackerData</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Tracker.html#GetTrackerData">GetTrackerData</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Tracker.html#LogEvent">LogEvent</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Tracker.html#SaveTrackerData">SaveTrackerData</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Tracker.html#isUndefinedOrNull">isUndefinedOrNull</a></span></li>
28
+ </nav>
29
+
30
+ <div id="main">
31
+
32
+ <h1 class="page-title">constants.js</h1>
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+ <section>
41
+ <article>
42
+ <pre class="prettyprint source linenums"><code>/**
43
+ * A class representing constants used throughout the application.
44
+ */
45
+ class Constants {
46
+ // element ids
47
+ /**
48
+ * The ID of the chart container element.
49
+ * @type {string}
50
+ */
51
+ chart_container_id = 'chart-container';
52
+ main_container_id = 'maidr-container';
53
+ //chart_container_class = 'chart-container'; // remove later
54
+ braille_container_id = 'braille-div';
55
+ braille_input_id = 'braille-input';
56
+ info_id = 'info';
57
+ announcement_container_id = 'announcements';
58
+ end_chime_id = 'end_chime';
59
+ container_id = 'container';
60
+ project_id = 'maidr';
61
+ review_id_container = 'review_container';
62
+ review_id = 'review';
63
+ reviewSaveSpot;
64
+ reviewSaveBrailleMode;
65
+ chartId = '';
66
+ events = [];
67
+ postLoadEvents = [];
68
+
69
+ // default constructor for all charts
70
+ /**
71
+ * Creates a new instance of the Constants class.
72
+ * @constructor
73
+ */
74
+ constructor() {}
75
+
76
+ // BTS modes initial values
77
+ textMode = 'verbose'; // off / terse / verbose
78
+ brailleMode = 'off'; // on / off
79
+ sonifMode = 'on'; // sep / same / off
80
+ reviewMode = 'off'; // on / off
81
+
82
+ // basic chart properties
83
+ minX = 0;
84
+ maxX = 0;
85
+ minY = 0;
86
+ maxY = 0;
87
+ plotId = ''; // update with id in chart specific js
88
+ chartType = ''; // set as 'box' or whatever later in chart specific js file
89
+ navigation = 1; // 0 = row navigation (up/down), 1 = col navigation (left/right)
90
+
91
+ // basic audio properties
92
+ MAX_FREQUENCY = 1000;
93
+ MIN_FREQUENCY = 200;
94
+ NULL_FREQUENCY = 100;
95
+
96
+ // autoplay speed
97
+ MAX_SPEED = 500;
98
+ MIN_SPEED = 50; // 50;
99
+ DEFAULT_SPEED = 250;
100
+ INTERVAL = 20;
101
+ AUTOPLAY_DURATION = 5000; // 5s
102
+
103
+ // user settings
104
+ vol = 0.5;
105
+ MAX_VOL = 30;
106
+ // autoPlayRate = this.DEFAULT_SPEED; // ms per tone
107
+ autoPlayRate = this.DEFAULT_SPEED; // ms per tone
108
+ colorSelected = '#03C809';
109
+ brailleDisplayLength = 32; // num characters in user's braille display. 40 is common length for desktop / mobile applications
110
+
111
+ // advanced user settings
112
+ showRect = 1; // true / false
113
+ hasRect = 1; // true / false
114
+ hasSmooth = 1; // true / false (for smooth line points)
115
+ duration = 0.3;
116
+ outlierDuration = 0.06;
117
+ autoPlayOutlierRate = 50; // ms per tone
118
+ autoPlayPointsRate = 50; // time between tones in a run
119
+ colorUnselected = '#595959'; // deprecated, todo: find all instances replace with storing old color method
120
+ isTracking = 1; // 0 / 1, is tracking on or off
121
+ 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)
122
+ globalMinMax = true;
123
+
124
+ // user controls (not exposed to menu, with shortcuts usually)
125
+ showDisplay = 1; // true / false
126
+ showDisplayInBraille = 1; // true / false
127
+ showDisplayInAutoplay = 0; // true / false
128
+ outlierInterval = null;
129
+
130
+ // platform controls
131
+ isMac = navigator.userAgent.toLowerCase().includes('mac'); // true if macOS
132
+ control = this.isMac ? 'Cmd' : 'Ctrl';
133
+ alt = this.isMac ? 'option' : 'Alt';
134
+ home = this.isMac ? 'fn + Left arrow' : 'Home';
135
+ end = this.isMac ? 'fn + Right arrow' : 'End';
136
+
137
+ // internal controls
138
+ keypressInterval = 2000; // ms or 2s
139
+ tabMovement = null;
140
+
141
+ // debug stuff
142
+ debugLevel = 3; // 0 = no console output, 1 = some console, 2 = more console, etc
143
+ canPlayEndChime = false; //
144
+ manualData = true; // pull from manual data like chart2music (true), or do the old method where we pull from the chart (false)
145
+
146
+ KillAutoplay() {
147
+ if (this.autoplayId) {
148
+ clearInterval(this.autoplayId);
149
+ this.autoplayId = null;
150
+ }
151
+ }
152
+
153
+ KillSepPlay() {
154
+ if (this.sepPlayId) {
155
+ clearInterval(this.sepPlayId);
156
+ this.sepPlayId = null;
157
+ }
158
+ }
159
+
160
+ SpeedUp() {
161
+ if (constants.autoPlayRate - this.INTERVAL > this.MIN_SPEED) {
162
+ constants.autoPlayRate -= this.INTERVAL;
163
+ }
164
+ }
165
+
166
+ SpeedDown() {
167
+ if (constants.autoPlayRate + this.INTERVAL &lt;= this.MAX_SPEED) {
168
+ constants.autoPlayRate += this.INTERVAL;
169
+ }
170
+ }
171
+
172
+ SpeedReset() {
173
+ constants.autoPlayRate = constants.DEFAULT_SPEED;
174
+ }
175
+
176
+ ColorInvert(color) {
177
+ // invert an rgb color
178
+ let rgb = color.replace(/[^\d,]/g, '').split(',');
179
+ let r = 255 - rgb[0];
180
+ let g = 255 - rgb[1];
181
+ let b = 255 - rgb[2];
182
+ return 'rgb(' + r + ',' + g + ',' + b + ')';
183
+ }
184
+ GetBetterColor(oldColor) {
185
+ // get a highly contrasting color against the current
186
+ // method: choose an inverted color, but if it's just a shade of gray, default to this.colorSelected
187
+ let newColor = this.ColorInvert(oldColor);
188
+ let rgb = newColor.replace(/[^\d,]/g, '').split(',');
189
+ if (
190
+ rgb[1] &lt; rgb[0] + 10 &amp;&amp;
191
+ rgb[1] > rgb[0] - 10 &amp;&amp;
192
+ rgb[2] &lt; rgb[0] + 10 &amp;&amp;
193
+ rgb[2] > rgb[0] - 10 &amp;&amp;
194
+ (rgb[0] > 86 || rgb[0] &lt; 169)
195
+ ) {
196
+ // too gray and too close to center gray, use default
197
+ newColor = this.colorSelected;
198
+ }
199
+
200
+ return newColor;
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Resources class contains properties and methods related to language, knowledge level, and strings.
206
+ */
207
+ class Resources {
208
+ constructor() {}
209
+
210
+ language = 'en'; // 2 char lang code
211
+ knowledgeLevel = 'basic'; // basic, intermediate, expert
212
+
213
+ // these strings run on getters, which pull in language, knowledgeLevel, chart, and actual requested string
214
+ strings = {
215
+ en: {
216
+ basic: {
217
+ upper_outlier: 'Upper Outlier',
218
+ lower_outlier: 'Lower Outlier',
219
+ min: 'Minimum',
220
+ max: 'Maximum',
221
+ 25: '25%',
222
+ 50: '50%',
223
+ 75: '75%',
224
+ q1: '25%',
225
+ q2: '50%',
226
+ q3: '75%',
227
+ son_on: 'Sonification on',
228
+ son_off: 'Sonification off',
229
+ son_des: 'Sonification descrete',
230
+ son_comp: 'Sonification compare',
231
+ son_ch: 'Sonification chord',
232
+ son_sep: 'Sonification separate',
233
+ son_same: 'Sonification combined',
234
+ empty: 'Empty',
235
+ },
236
+ },
237
+ };
238
+
239
+ /**
240
+ * Returns a string based on the provided ID, language, and knowledge level.
241
+ * @param {string} id - The ID of the string to retrieve.
242
+ * @returns {string} The string corresponding to the provided ID, language, and knowledge level.
243
+ */
244
+ GetString(id) {
245
+ return this.strings[this.language][this.knowledgeLevel][id];
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Represents a menu object with various settings and keyboard shortcuts.
251
+ */
252
+ class Menu {
253
+ whereWasMyFocus = null;
254
+
255
+ constructor() {
256
+ this.CreateMenu();
257
+ this.LoadDataFromLocalStorage();
258
+ }
259
+
260
+ menuHtml = `
261
+ &lt;div id="menu" class="modal hidden" role="dialog" tabindex="-1">
262
+ &lt;div class="modal-dialog" role="document" tabindex="0">
263
+ &lt;div class="modal-content">
264
+ &lt;div class="modal-header">
265
+ &lt;h4 class="modal-title">Menu&lt;/h4>
266
+ &lt;button type="button" class="close" data-dismiss="modal" aria-label="Close">
267
+ &lt;span aria-hidden="true">&amp;times;&lt;/span>
268
+ &lt;/button>
269
+ &lt;/div>
270
+ &lt;div class="modal-body">
271
+ &lt;div>
272
+ &lt;h5 class="modal-title">Keyboard Shortcuts&lt;/h5>
273
+ &lt;table>
274
+ &lt;caption class="sr-only">Keyboard Shortcuts&lt;/caption>
275
+ &lt;thead>
276
+ &lt;tr>
277
+ &lt;th scope="col">Function&lt;/th>
278
+ &lt;th scope="col">Key&lt;/th>
279
+ &lt;/tr>
280
+ &lt;/thead>
281
+ &lt;tbody>
282
+ &lt;tr>
283
+ &lt;td>Move around plot&lt;/td>
284
+ &lt;td>Arrow keys&lt;/td>
285
+ &lt;/tr>
286
+ &lt;tr>
287
+ &lt;td>Go to the very left right up down&lt;/td>
288
+ &lt;td>${constants.control} + Arrow key&lt;/td>
289
+ &lt;/tr>
290
+ &lt;tr>
291
+ &lt;td>Select the first element&lt;/td>
292
+ &lt;td>${constants.control} + ${constants.home}&lt;/td>
293
+ &lt;/tr>
294
+ &lt;tr>
295
+ &lt;td>Select the last element&lt;/td>
296
+ &lt;td>${constants.control} + ${constants.end}&lt;/td>
297
+ &lt;/tr>
298
+ &lt;tr>
299
+ &lt;td>Toggle Braille Mode&lt;/td>
300
+ &lt;td>b&lt;/td>
301
+ &lt;/tr>
302
+ &lt;tr>
303
+ &lt;td>Toggle Sonification Mode&lt;/td>
304
+ &lt;td>s&lt;/td>
305
+ &lt;/tr>
306
+ &lt;tr>
307
+ &lt;td>Toggle Text Mode&lt;/td>
308
+ &lt;td>t&lt;/td>
309
+ &lt;/tr>
310
+ &lt;tr>
311
+ &lt;td>Repeat current sound&lt;/td>
312
+ &lt;td>Space&lt;/td>
313
+ &lt;/tr>
314
+ &lt;tr>
315
+ &lt;td>Auto-play outward in direction of arrow&lt;/td>
316
+ &lt;td>${constants.control} + Shift + Arrow key&lt;/td>
317
+ &lt;/tr>
318
+ &lt;tr>
319
+ &lt;td>Auto-play inward in direction of arrow&lt;/td>
320
+ &lt;td>${constants.alt} + Shift + Arrow key&lt;/td>
321
+ &lt;/tr>
322
+ &lt;tr>
323
+ &lt;td>Stop Auto-play&lt;/td>
324
+ &lt;td>${constants.control}&lt;/td>
325
+ &lt;/tr>
326
+ &lt;tr>
327
+ &lt;td>Auto-play speed up&lt;/td>
328
+ &lt;td>Period&lt;/td>
329
+ &lt;/tr>
330
+ &lt;tr>
331
+ &lt;td>Auto-play speed down&lt;/td>
332
+ &lt;td>Comma&lt;/td>
333
+ &lt;/tr>
334
+ &lt;/tbody>
335
+ &lt;/table>
336
+ &lt;/div>
337
+
338
+ &lt;div>
339
+ &lt;h5 class="modal-title">Settings&lt;/h5>
340
+ &lt;p>&lt;input type="range" id="vol" name="vol" min="0" max="1" step=".05">&lt;label for="vol">Volume&lt;/label>&lt;/p>
341
+ &lt;!-- &lt;p>&lt;input type="checkbox" id="show_rect" name="show_rect">&lt;label for="show_rect">Show Outline&lt;/label>&lt;/p> //-->
342
+ &lt;p>&lt;input type="number" min="4" max="2000" step="1" id="braille_display_length" name="braille_display_length">&lt;label for="braille_display_length">Braille Display Size&lt;/label>&lt;/p>
343
+ &lt;p>&lt;input type="number" min="${constants.MIN_SPEED}" max="500" step="${constants.INTERVAL}" id="autoplay_rate" name="autoplay_rate">&lt;label for="autoplay_rate">Autoplay Rate&lt;/label>&lt;/p>
344
+ &lt;p>&lt;input type="color" id="color_selected" name="color_selected">&lt;label for="color_selected">Outline Color&lt;/label>&lt;/p>
345
+ &lt;p>&lt;input type="number" min="10" max="2000" step="10" id="min_freq" name="min_freq">&lt;label for="min_freq">Min Frequency (Hz)&lt;/label>&lt;/p>
346
+ &lt;p>&lt;input type="number" min="20" max="2010" step="10" id="max_freq" name="max_freq">&lt;label for="max_freq">Max Frequency (Hz)&lt;/label>&lt;/p>
347
+ &lt;p>&lt;input type="number" min="500" max="5000" step="500" id="keypress_interval" name="keypress_interval">&lt;label for="keypress_interval">Keypress Interval (ms)&lt;/label>&lt;/p>
348
+ &lt;/div>
349
+ &lt;/div>
350
+ &lt;div class="modal-footer">
351
+ &lt;button type="button" id="save_and_close_menu">Save and close&lt;/button>
352
+ &lt;button type="button" id="close_menu">Close&lt;/button>
353
+ &lt;/div>
354
+ &lt;/div>
355
+ &lt;/div>
356
+ &lt;/div>
357
+ &lt;div id="menu_modal_backdrop" class="modal-backdrop hidden">&lt;/div>
358
+ `;
359
+
360
+ /**
361
+ * Creates a menu element and sets up event listeners for opening and closing the menu.
362
+ */
363
+ CreateMenu() {
364
+ // menu element creation
365
+ document
366
+ .querySelector('body')
367
+ .insertAdjacentHTML('beforeend', this.menuHtml);
368
+
369
+ // menu close events
370
+ let allClose = document.querySelectorAll('#close_menu, #menu .close');
371
+ for (let i = 0; i &lt; allClose.length; i++) {
372
+ constants.events.push([
373
+ allClose[i],
374
+ 'click',
375
+ function (e) {
376
+ menu.Toggle(false);
377
+ },
378
+ ]);
379
+ }
380
+ constants.events.push([
381
+ document.getElementById('save_and_close_menu'),
382
+ 'click',
383
+ function (e) {
384
+ menu.SaveData();
385
+ menu.Toggle(false);
386
+ },
387
+ ]);
388
+ constants.events.push([
389
+ document.getElementById('menu'),
390
+ 'keydown',
391
+ function (e) {
392
+ if (e.key == 'Esc') {
393
+ // esc
394
+ menu.Toggle(false);
395
+ }
396
+ },
397
+ ]);
398
+
399
+ // open events
400
+ // note: this triggers a maidr destroy
401
+ constants.events.push([
402
+ document,
403
+ 'keyup',
404
+ function (e) {
405
+ if (e.key == 'h') {
406
+ menu.Toggle(true);
407
+ }
408
+ },
409
+ ]);
410
+ }
411
+
412
+ /**
413
+ * Destroys the menu element and its backdrop.
414
+ * @function
415
+ * @name Destroy
416
+ * @memberof module:constants
417
+ * @returns {void}
418
+ */
419
+ Destroy() {
420
+ // menu element destruction
421
+ let menu = document.getElementById('menu');
422
+ if (menu) {
423
+ menu.remove();
424
+ }
425
+ let backdrop = document.getElementById('menu_modal_backdrop');
426
+ if (backdrop) {
427
+ backdrop.remove();
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Toggles the menu on and off.
433
+ * @param {boolean} [onoff=false] - Whether to turn the menu on or off. Defaults to false.
434
+ */
435
+ Toggle(onoff = false) {
436
+ if (typeof onoff == 'undefined') {
437
+ if (document.getElementById('menu').classList.contains('hidden')) {
438
+ onoff = true;
439
+ } else {
440
+ onoff = false;
441
+ }
442
+ }
443
+ if (onoff) {
444
+ // open
445
+ this.whereWasMyFocus = document.activeElement;
446
+ this.PopulateData();
447
+ constants.tabMovement = 0;
448
+ document.getElementById('menu').classList.remove('hidden');
449
+ document.getElementById('menu_modal_backdrop').classList.remove('hidden');
450
+ document.querySelector('#menu .close').focus();
451
+ } else {
452
+ // close
453
+ document.getElementById('menu').classList.add('hidden');
454
+ document.getElementById('menu_modal_backdrop').classList.add('hidden');
455
+ this.whereWasMyFocus.focus();
456
+ this.whereWasMyFocus = null;
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Populates the data in the HTML elements with the values from the constants object.
462
+ */
463
+ PopulateData() {
464
+ document.getElementById('vol').value = constants.vol;
465
+ //document.getElementById('show_rect').checked = constants.showRect;
466
+ document.getElementById('autoplay_rate').value = constants.autoPlayRate;
467
+ document.getElementById('braille_display_length').value =
468
+ constants.brailleDisplayLength;
469
+ document.getElementById('color_selected').value = constants.colorSelected;
470
+ document.getElementById('min_freq').value = constants.MIN_FREQUENCY;
471
+ document.getElementById('max_freq').value = constants.MAX_FREQUENCY;
472
+ document.getElementById('keypress_interval').value =
473
+ constants.keypressInterval;
474
+ }
475
+
476
+ /**
477
+ * Saves the data from the HTML elements into the constants object.
478
+ */
479
+ SaveData() {
480
+ constants.vol = document.getElementById('vol').value;
481
+ //constants.showRect = document.getElementById('show_rect').checked;
482
+ constants.autoPlayRate = document.getElementById('autoplay_rate').value;
483
+ constants.brailleDisplayLength = document.getElementById(
484
+ 'braille_display_length'
485
+ ).value;
486
+ constants.colorSelected = document.getElementById('color_selected').value;
487
+ constants.MIN_FREQUENCY = document.getElementById('min_freq').value;
488
+ constants.MAX_FREQUENCY = document.getElementById('max_freq').value;
489
+ constants.keypressInterval =
490
+ document.getElementById('keypress_interval').value;
491
+ }
492
+
493
+ /**
494
+ * Saves all data in this.SaveData() to local storage.
495
+ * @function
496
+ * @memberof constants
497
+ * @returns {void}
498
+ */
499
+ SaveDataToLocalStorage() {
500
+ // save all data in this.SaveData() to local storage
501
+ let data = {};
502
+ data.vol = constants.vol;
503
+ //data.showRect = constants.showRect;
504
+ data.autoPlayRate = constants.autoPlayRate;
505
+ data.brailleDisplayLength = constants.brailleDisplayLength;
506
+ data.colorSelected = constants.colorSelected;
507
+ data.MIN_FREQUENCY = constants.MIN_FREQUENCY;
508
+ data.MAX_FREQUENCY = constants.MAX_FREQUENCY;
509
+ data.keypressInterval = constants.keypressInterval;
510
+ localStorage.setItem('settings_data', JSON.stringify(data));
511
+ }
512
+ /**
513
+ * Loads data from local storage and updates the constants object with the retrieved values.
514
+ */
515
+ LoadDataFromLocalStorage() {
516
+ let data = JSON.parse(localStorage.getItem('settings_data'));
517
+ if (data) {
518
+ constants.vol = data.vol;
519
+ //constants.showRect = data.showRect;
520
+ constants.autoPlayRate = data.autoPlayRate;
521
+ constants.brailleDisplayLength = data.brailleDisplayLength;
522
+ constants.colorSelected = data.colorSelected;
523
+ constants.MIN_FREQUENCY = data.MIN_FREQUENCY;
524
+ constants.MAX_FREQUENCY = data.MAX_FREQUENCY;
525
+ constants.keypressInterval = data.keypressInterval;
526
+ }
527
+ }
528
+ }
529
+
530
+ /**
531
+ * Creates an html modal containing summary info of the active chart.
532
+ * @class
533
+ */
534
+ class Description {
535
+ // This class creates an html modal containing summary info of the active chart
536
+ // Trigger popup with 'D' key
537
+ // Info is basically anything available, but stuff like:
538
+ // - chart type
539
+ // - chart labels, like title, subtitle, caption etc
540
+ // - chart data (an accessible html table)
541
+
542
+ constructor() {
543
+ //this.CreateComponent(); // disabled as we're in development and have switched priorities
544
+ }
545
+
546
+ /**
547
+ * Creates a modal component containing description summary stuff.
548
+ */
549
+ CreateComponent() {
550
+ // modal containing description summary stuff
551
+ let html = `
552
+ &lt;div id="description" class="modal hidden" role="dialog" tabindex="-1">
553
+ &lt;div class="modal-dialog" role="document" tabindex="0">
554
+ &lt;div class="modal-content">
555
+ &lt;div class="modal-header">
556
+ &lt;h4 id="desc_title" class="modal-title">Description&lt;/h4>
557
+ &lt;button type="button" class="close" data-dismiss="modal" aria-label="Close">
558
+ &lt;span aria-hidden="true">&amp;times;&lt;/span>
559
+ &lt;/button>
560
+ &lt;/div>
561
+ &lt;div class="modal-body">
562
+ &lt;div id="desc_content">
563
+ content here
564
+ &lt;/div>
565
+ &lt;div id="desc_table">
566
+ &lt;/div>
567
+ &lt;/div>
568
+ &lt;div class="modal-footer">
569
+ &lt;button type="button" id="close_desc">Close&lt;/button>
570
+ &lt;/div>
571
+ &lt;/div>
572
+ &lt;/div>
573
+ &lt;/div>
574
+ &lt;div id="desc_modal_backdrop" class="modal-backdrop hidden">&lt;/div>
575
+
576
+ `;
577
+
578
+ document.querySelector('body').insertAdjacentHTML('beforeend', html);
579
+
580
+ // close events
581
+ let allClose = document.querySelectorAll(
582
+ '#close_desc, #description .close'
583
+ );
584
+ for (let i = 0; i &lt; allClose.length; i++) {
585
+ constants.events.push([
586
+ allClose[i],
587
+ 'click',
588
+ function (e) {
589
+ description.Toggle(false);
590
+ },
591
+ ]);
592
+ }
593
+ constants.events.push([
594
+ document.getElementById('description'),
595
+ 'keydown',
596
+ function (e) {
597
+ if (e.key == 'Esc') {
598
+ // esc
599
+ description.Toggle(false);
600
+ }
601
+ },
602
+ ]);
603
+
604
+ // open events
605
+ constants.events.push([
606
+ document,
607
+ 'keyup',
608
+ function (e) {
609
+ if (e.key == 'd') {
610
+ description.Toggle(true);
611
+ }
612
+ },
613
+ ]);
614
+ }
615
+
616
+ /**
617
+ * Removes the description element and backdrop from the DOM.
618
+ */
619
+ Destroy() {
620
+ // description element destruction
621
+ let description = document.getElementById('menu');
622
+ if (description) {
623
+ description.remove();
624
+ }
625
+ let backdrop = document.getElementById('desc_modal_backdrop');
626
+ if (backdrop) {
627
+ backdrop.remove();
628
+ }
629
+ }
630
+
631
+ /**
632
+ * Toggles the visibility of the description element.
633
+ * @param {boolean} [onoff=false] - Whether to turn the description element on or off.
634
+ */
635
+ Toggle(onoff = false) {
636
+ if (typeof onoff == 'undefined') {
637
+ if (document.getElementById('description').classList.contains('hidden')) {
638
+ onoff = true;
639
+ } else {
640
+ onoff = false;
641
+ }
642
+ }
643
+ if (onoff) {
644
+ // open
645
+ this.whereWasMyFocus = document.activeElement;
646
+ constants.tabMovement = 0;
647
+ this.PopulateData();
648
+ document.getElementById('description').classList.remove('hidden');
649
+ document.getElementById('desc_modal_backdrop').classList.remove('hidden');
650
+ document.querySelector('#description .close').focus();
651
+ } else {
652
+ // close
653
+ document.getElementById('description').classList.add('hidden');
654
+ document.getElementById('desc_modal_backdrop').classList.add('hidden');
655
+ this.whereWasMyFocus.focus();
656
+ this.whereWasMyFocus = null;
657
+ }
658
+ }
659
+
660
+ /**
661
+ * Populates the data for the chart and table based on the chart type and plot data.
662
+ */
663
+ PopulateData() {
664
+ let descHtml = '';
665
+
666
+ // chart labels and descriptions
667
+ let descType = '';
668
+ if (constants.chartType == 'bar') {
669
+ descType = 'Bar chart';
670
+ } else if (constants.chartType == 'heat') {
671
+ descType = 'Heatmap';
672
+ } else if (constants.chartType == 'box') {
673
+ descType = 'Box plot';
674
+ } else if (constants.chartType == 'scatter') {
675
+ descType = 'Scatter plot';
676
+ } else if (constants.chartType == 'line') {
677
+ descType = 'Line chart';
678
+ } else if (constants.chartType == 'hist') {
679
+ descType = 'Histogram';
680
+ }
681
+
682
+ if (descType) {
683
+ descHtml += `&lt;p>Type: ${descType}&lt;/p>`;
684
+ }
685
+ if (plot.title != null) {
686
+ descHtml += `&lt;p>Title: ${plot.title}&lt;/p>`;
687
+ }
688
+ if (plot.subtitle != null) {
689
+ descHtml += `&lt;p>Subtitle: ${plot.subtitle}&lt;/p>`;
690
+ }
691
+ if (plot.caption != null) {
692
+ descHtml += `&lt;p>Caption: ${plot.caption}&lt;/p>`;
693
+ }
694
+
695
+ // table of data, prep
696
+ let descTableHtml = '';
697
+ let descLabelX = null;
698
+ let descLabelY = null;
699
+ let descTickX = null;
700
+ let descTickY = null;
701
+ let descData = null;
702
+ let descNumCols = 0;
703
+ let descNumColsWithLabels = 0;
704
+ let descNumRows = 0;
705
+ let descNumRowsWithLabels = 0;
706
+ if (constants.chartType == 'bar') {
707
+ if (plot.plotLegend.x != null) {
708
+ descLabelX = plot.plotLegend.x;
709
+ descNumColsWithLabels += 1;
710
+ }
711
+ if (plot.plotLegend.y != null) {
712
+ descLabelY = plot.plotLegend.y;
713
+ descNumRowsWithLabels += 1;
714
+ }
715
+ if (plot.columnLabels != null) {
716
+ descTickX = plot.columnLabels;
717
+ descNumRowsWithLabels += 1;
718
+ }
719
+ if (plot.plotData != null) {
720
+ descData = [];
721
+ descData[0] = plot.plotData;
722
+ descNumCols = plot.plotData.length;
723
+ descNumRows = 1;
724
+ descNumColsWithLabels += descNumCols;
725
+ descNumRowsWithLabels += descNumRows;
726
+ }
727
+ }
728
+
729
+ // table of data, create
730
+ if (descData != null) {
731
+ descTableHtml += '&lt;table>';
732
+
733
+ // header rows
734
+ if (descLabelX != null || descTickX != null) {
735
+ descTableHtml += '&lt;thead>';
736
+ if (descLabelX != null) {
737
+ descTableHtml += '&lt;tr>';
738
+ if (descLabelY != null) {
739
+ descTableHtml += '&lt;td>&lt;/td>';
740
+ }
741
+ if (descTickY != null) {
742
+ descTableHtml += '&lt;td>&lt;/td>';
743
+ }
744
+ descTableHtml += `&lt;th scope="col" colspan="${descNumCols}">${descLabelX}&lt;/th>`;
745
+ descTableHtml += '&lt;/tr>';
746
+ }
747
+ if (descTickX != null) {
748
+ descTableHtml += '&lt;tr>';
749
+ if (descLabelY != null) {
750
+ descTableHtml += '&lt;td>&lt;/td>';
751
+ }
752
+ if (descTickY != null) {
753
+ descTableHtml += '&lt;td>&lt;/td>';
754
+ }
755
+ for (let i = 0; i &lt; descNumCols; i++) {
756
+ descTableHtml += `&lt;th scope="col">${descTickX[i]}&lt;/th>`;
757
+ }
758
+ descTableHtml += '&lt;/tr>';
759
+ }
760
+ descTableHtml += '&lt;/thead>';
761
+ }
762
+
763
+ // body rows
764
+ if (descNumRows > 0) {
765
+ descTableHtml += '&lt;tbody>';
766
+ for (let i = 0; i &lt; descNumRows; i++) {
767
+ descTableHtml += '&lt;tr>';
768
+ if (descLabelY != null &amp;&amp; i == 0) {
769
+ descTableHtml += `&lt;th scope="row" rowspan="${descNumRows}">${descLabelY}&lt;/th>`;
770
+ }
771
+ if (descTickY != null) {
772
+ descTableHtml += `&lt;th scope="row">${descTickY[i]}&lt;/th>`;
773
+ }
774
+ for (let j = 0; j &lt; descNumCols; j++) {
775
+ descTableHtml += `&lt;td>${descData[i][j]}&lt;/td>`;
776
+ }
777
+ descTableHtml += '&lt;/tr>';
778
+ }
779
+ descTableHtml += '&lt;/tbody>';
780
+ }
781
+
782
+ descTableHtml += '&lt;/table>';
783
+ }
784
+
785
+ // bar: don't need colspan or rowspan stuff, put legendX and Y as headers
786
+
787
+ document.getElementById('desc_title').innerHTML = descType + ' description';
788
+ document.getElementById('desc_content').innerHTML = descHtml;
789
+ document.getElementById('desc_table').innerHTML = descTableHtml;
790
+ }
791
+ }
792
+
793
+ /**
794
+ * Represents a position in 3D space.
795
+ * @class
796
+ */
797
+ class Position {
798
+ constructor(x, y, z = -1) {
799
+ this.x = x;
800
+ this.y = y;
801
+ this.z = z; // rarely used
802
+ }
803
+ }
804
+
805
+ // HELPER FUNCTIONS
806
+ /**
807
+ * A helper class with static methods.
808
+ */
809
+ class Helper {
810
+ /**
811
+ * Checks if an object is present in an array.
812
+ * @param {Object} obj - The object to search for.
813
+ * @param {Array} arr - The array to search in.
814
+ * @returns {boolean} - True if the object is present in the array, false otherwise.
815
+ */
816
+ static containsObject(obj, arr) {
817
+ for (let i = 0; i &lt; arr.length; i++) {
818
+ if (arr[i] === obj) return true;
819
+ }
820
+ return false;
821
+ }
822
+ }
823
+
824
+ /**
825
+ * A class representing a Tracker.
826
+ * @class
827
+ */
828
+ class Tracker {
829
+ constructor() {
830
+ this.DataSetup();
831
+ }
832
+
833
+ /**
834
+ * Sets up the tracker data by checking if previous data exists and creating new data if it doesn't.
835
+ */
836
+ DataSetup() {
837
+ let prevData = this.GetTrackerData();
838
+ if (prevData) {
839
+ // good to go already, do nothing
840
+ } else {
841
+ let data = {};
842
+ data.userAgent = Object.assign(navigator.userAgent);
843
+ data.vendor = Object.assign(navigator.vendor);
844
+ data.language = Object.assign(navigator.language);
845
+ data.platform = Object.assign(navigator.platform);
846
+ data.events = [];
847
+
848
+ this.SaveTrackerData(data);
849
+ }
850
+ }
851
+
852
+ /**
853
+ * Downloads the tracker data as a JSON file.
854
+ */
855
+ DownloadTrackerData() {
856
+ let link = document.createElement('a');
857
+ let data = this.GetTrackerData();
858
+ let fileStr = new Blob([JSON.stringify(data)], { type: 'text/plain' });
859
+ link.href = URL.createObjectURL(fileStr);
860
+ link.download = 'tracking.json';
861
+ link.click();
862
+ }
863
+
864
+ /**
865
+ * Saves the tracker data to local storage.
866
+ * @param {Object} data - The data to be saved.
867
+ */
868
+ SaveTrackerData(data) {
869
+ localStorage.setItem(constants.project_id, JSON.stringify(data));
870
+ }
871
+
872
+ /**
873
+ * Retrieves tracker data from local storage.
874
+ * @returns {Object} The tracker data.
875
+ */
876
+ GetTrackerData() {
877
+ let data = JSON.parse(localStorage.getItem(constants.project_id));
878
+ return data;
879
+ }
880
+
881
+ /**
882
+ * Removes the project_id from localStorage, clears the tracking data, and sets up new data.
883
+ */
884
+ Delete() {
885
+ localStorage.removeItem(constants.project_id);
886
+ this.data = null;
887
+
888
+ if (constants.debugLevel > 0) {
889
+ console.log('tracking data cleared');
890
+ }
891
+
892
+ this.DataSetup();
893
+ }
894
+
895
+ /**
896
+ * Logs an event with various properties to the tracker data.
897
+ * @param {Event} e - The event to log.
898
+ */
899
+ LogEvent(e) {
900
+ let eventToLog = {};
901
+
902
+ // computer stuff
903
+ eventToLog.timestamp = Object.assign(e.timeStamp);
904
+ eventToLog.time = Date().toString();
905
+ eventToLog.key = Object.assign(e.key);
906
+ eventToLog.altKey = Object.assign(e.altKey);
907
+ eventToLog.ctrlKey = Object.assign(e.ctrlKey);
908
+ eventToLog.shiftKey = Object.assign(e.shiftKey);
909
+ if (e.path) {
910
+ eventToLog.focus = Object.assign(e.path[0].tagName);
911
+ }
912
+
913
+ // settings etc, which we have to reassign otherwise they'll all be the same val
914
+ if (!this.isUndefinedOrNull(constants.position)) {
915
+ eventToLog.position = Object.assign(constants.position);
916
+ }
917
+ if (!this.isUndefinedOrNull(constants.minX)) {
918
+ eventToLog.min_x = Object.assign(constants.minX);
919
+ }
920
+ if (!this.isUndefinedOrNull(constants.maxX)) {
921
+ eventToLog.max_x = Object.assign(constants.maxX);
922
+ }
923
+ if (!this.isUndefinedOrNull(constants.minY)) {
924
+ eventToLog.min_y = Object.assign(constants.minY);
925
+ }
926
+ if (!this.isUndefinedOrNull(constants.MAX_FREQUENCY)) {
927
+ eventToLog.max_frequency = Object.assign(constants.MAX_FREQUENCY);
928
+ }
929
+ if (!this.isUndefinedOrNull(constants.MIN_FREQUENCY)) {
930
+ eventToLog.min_frequency = Object.assign(constants.MIN_FREQUENCY);
931
+ }
932
+ if (!this.isUndefinedOrNull(constants.NULL_FREQUENCY)) {
933
+ eventToLog.null_frequency = Object.assign(constants.NULL_FREQUENCY);
934
+ }
935
+ if (!this.isUndefinedOrNull(constants.MAX_SPEED)) {
936
+ eventToLog.max_speed = Object.assign(constants.MAX_SPEED);
937
+ }
938
+ if (!this.isUndefinedOrNull(constants.MIN_SPEED)) {
939
+ eventToLog.min_speed = Object.assign(constants.MIN_SPEED);
940
+ }
941
+ if (!this.isUndefinedOrNull(constants.INTERVAL)) {
942
+ eventToLog.interval = Object.assign(constants.INTERVAL);
943
+ }
944
+ if (!this.isUndefinedOrNull(constants.vol)) {
945
+ eventToLog.volume = Object.assign(constants.vol);
946
+ }
947
+ if (!this.isUndefinedOrNull(constants.autoPlayRate)) {
948
+ eventToLog.autoplay_rate = Object.assign(constants.autoPlayRate);
949
+ }
950
+ if (!this.isUndefinedOrNull(constants.colorSelected)) {
951
+ eventToLog.color = Object.assign(constants.colorSelected);
952
+ }
953
+ if (!this.isUndefinedOrNull(constants.brailleDisplayLength)) {
954
+ eventToLog.braille_display_length = Object.assign(
955
+ constants.brailleDisplayLength
956
+ );
957
+ }
958
+ if (!this.isUndefinedOrNull(constants.duration)) {
959
+ eventToLog.tone_duration = Object.assign(constants.duration);
960
+ }
961
+ if (!this.isUndefinedOrNull(constants.autoPlayOutlierRate)) {
962
+ eventToLog.autoplay_outlier_rate = Object.assign(
963
+ constants.autoPlayOutlierRate
964
+ );
965
+ }
966
+ if (!this.isUndefinedOrNull(constants.autoPlayPointsRate)) {
967
+ eventToLog.autoplay_points_rate = Object.assign(
968
+ constants.autoPlayPointsRate
969
+ );
970
+ }
971
+ if (!this.isUndefinedOrNull(constants.textMode)) {
972
+ eventToLog.text_mode = Object.assign(constants.textMode);
973
+ }
974
+ if (!this.isUndefinedOrNull(constants.sonifMode)) {
975
+ eventToLog.sonification_mode = Object.assign(constants.sonifMode);
976
+ }
977
+ if (!this.isUndefinedOrNull(constants.brailleMode)) {
978
+ eventToLog.braille_mode = Object.assign(constants.brailleMode);
979
+ }
980
+ if (!this.isUndefinedOrNull(constants.chartType)) {
981
+ eventToLog.chart_type = Object.assign(constants.chartType);
982
+ }
983
+ if (!this.isUndefinedOrNull(constants.infoDiv.innerHTML)) {
984
+ let textDisplay = Object.assign(constants.infoDiv.innerHTML);
985
+ textDisplay = textDisplay.replaceAll(/&lt;[^>]*>?/gm, '');
986
+ eventToLog.text_display = textDisplay;
987
+ }
988
+ if (!this.isUndefinedOrNull(location.href)) {
989
+ eventToLog.location = Object.assign(location.href);
990
+ }
991
+
992
+ // chart specific values
993
+ let x_tickmark = '';
994
+ let y_tickmark = '';
995
+ let x_label = '';
996
+ let y_label = '';
997
+ let value = '';
998
+ let fill_value = '';
999
+ if (constants.chartType == 'bar') {
1000
+ if (!this.isUndefinedOrNull(plot.columnLabels[position.x])) {
1001
+ x_tickmark = plot.columnLabels[position.x];
1002
+ }
1003
+ if (!this.isUndefinedOrNull(plot.plotLegend.x)) {
1004
+ x_label = plot.plotLegend.x;
1005
+ }
1006
+ if (!this.isUndefinedOrNull(plot.plotLegend.y)) {
1007
+ y_label = plot.plotLegend.y;
1008
+ }
1009
+ if (!this.isUndefinedOrNull(plot.plotData[position.x])) {
1010
+ value = plot.plotData[position.x];
1011
+ }
1012
+ } else if (constants.chartType == 'heat') {
1013
+ if (!this.isUndefinedOrNull(plot.x_labels[position.x])) {
1014
+ x_tickmark = plot.x_labels[position.x].trim();
1015
+ }
1016
+ if (!this.isUndefinedOrNull(plot.y_labels[position.y])) {
1017
+ y_tickmark = plot.y_labels[position.y].trim();
1018
+ }
1019
+ if (!this.isUndefinedOrNull(plot.x_group_label)) {
1020
+ x_label = plot.x_group_label;
1021
+ }
1022
+ if (!this.isUndefinedOrNull(plot.y_group_label)) {
1023
+ y_label = plot.y_group_label;
1024
+ }
1025
+ if (!this.isUndefinedOrNull(plot.values)) {
1026
+ if (!this.isUndefinedOrNull(plot.values[position.x][position.y])) {
1027
+ value = plot.values[position.x][position.y];
1028
+ }
1029
+ }
1030
+ if (!this.isUndefinedOrNull(plot.group_labels[2])) {
1031
+ fill_value = plot.group_labels[2];
1032
+ }
1033
+ } else if (constants.chartType == 'box') {
1034
+ let plotPos =
1035
+ constants.plotOrientation == 'vert' ? position.x : position.y;
1036
+ let sectionPos =
1037
+ constants.plotOrientation == 'vert' ? position.y : position.x;
1038
+
1039
+ if (!this.isUndefinedOrNull(plot.x_group_label)) {
1040
+ x_label = plot.x_group_label;
1041
+ }
1042
+ if (!this.isUndefinedOrNull(plot.y_group_label)) {
1043
+ y_label = plot.y_group_label;
1044
+ }
1045
+ if (constants.plotOrientation == 'vert') {
1046
+ if (plotPos > -1 &amp;&amp; sectionPos > -1) {
1047
+ if (
1048
+ !this.isUndefinedOrNull(plot.plotData[plotPos][sectionPos].label)
1049
+ ) {
1050
+ y_tickmark = plot.plotData[plotPos][sectionPos].label;
1051
+ }
1052
+ if (!this.isUndefinedOrNull(plot.x_labels[position.x])) {
1053
+ x_tickmark = plot.x_labels[position.x];
1054
+ }
1055
+ if (
1056
+ !this.isUndefinedOrNull(plot.plotData[plotPos][sectionPos].values)
1057
+ ) {
1058
+ value = plot.plotData[plotPos][sectionPos].values;
1059
+ } else if (
1060
+ !this.isUndefinedOrNull(plot.plotData[plotPos][sectionPos].y)
1061
+ ) {
1062
+ value = plot.plotData[plotPos][sectionPos].y;
1063
+ }
1064
+ }
1065
+ } else {
1066
+ if (plotPos > -1 &amp;&amp; sectionPos > -1) {
1067
+ if (
1068
+ !this.isUndefinedOrNull(plot.plotData[plotPos][sectionPos].label)
1069
+ ) {
1070
+ x_tickmark = plot.plotData[plotPos][sectionPos].label;
1071
+ }
1072
+ if (!this.isUndefinedOrNull(plot.y_labels[position.y])) {
1073
+ y_tickmark = plot.y_labels[position.y];
1074
+ }
1075
+ if (
1076
+ !this.isUndefinedOrNull(plot.plotData[plotPos][sectionPos].values)
1077
+ ) {
1078
+ value = plot.plotData[plotPos][sectionPos].values;
1079
+ } else if (
1080
+ !this.isUndefinedOrNull(plot.plotData[plotPos][sectionPos].x)
1081
+ ) {
1082
+ value = plot.plotData[plotPos][sectionPos].x;
1083
+ }
1084
+ }
1085
+ }
1086
+ } else if (constants.chartType == 'point') {
1087
+ if (!this.isUndefinedOrNull(plot.x_group_label)) {
1088
+ x_label = plot.x_group_label;
1089
+ }
1090
+ if (!this.isUndefinedOrNull(plot.y_group_label)) {
1091
+ y_label = plot.y_group_label;
1092
+ }
1093
+
1094
+ if (!this.isUndefinedOrNull(plot.x[position.x])) {
1095
+ x_tickmark = plot.x[position.x];
1096
+ }
1097
+ if (!this.isUndefinedOrNull(plot.y[position.x])) {
1098
+ y_tickmark = plot.y[position.x];
1099
+ }
1100
+
1101
+ value = [x_tickmark, y_tickmark];
1102
+ }
1103
+
1104
+ eventToLog.x_tickmark = Object.assign(x_tickmark);
1105
+ eventToLog.y_tickmark = Object.assign(y_tickmark);
1106
+ eventToLog.x_label = Object.assign(x_label);
1107
+ eventToLog.y_label = Object.assign(y_label);
1108
+ eventToLog.value = Object.assign(value);
1109
+ eventToLog.fill_value = Object.assign(fill_value);
1110
+
1111
+ //console.log("x_tickmark: '", x_tickmark, "', y_tickmark: '", y_tickmark, "', x_label: '", x_label, "', y_label: '", y_label, "', value: '", value, "', fill_value: '", fill_value);
1112
+
1113
+ let data = this.GetTrackerData();
1114
+ data.events.push(eventToLog);
1115
+ this.SaveTrackerData(data);
1116
+ }
1117
+
1118
+ /**
1119
+ * Checks if the given item is undefined or null.
1120
+ * @param {*} item - The item to check.
1121
+ * @returns {boolean} - Returns true if the item is undefined or null, else false.
1122
+ */
1123
+ isUndefinedOrNull(item) {
1124
+ try {
1125
+ return item === undefined || item === null;
1126
+ } catch {
1127
+ return true;
1128
+ }
1129
+ }
1130
+ }
1131
+
1132
+ /**
1133
+ * Represents a Review object.
1134
+ * @class
1135
+ */
1136
+ class Review {
1137
+ constructor() {}
1138
+
1139
+ /**
1140
+ * Toggles the review mode on or off.
1141
+ * @param {boolean} [onoff=true] - Whether to turn review mode on or off. Default is true.
1142
+ */
1143
+ ToggleReviewMode(onoff = true) {
1144
+ // true means on or show
1145
+ if (onoff) {
1146
+ constants.reviewSaveSpot = document.activeElement;
1147
+ constants.review_container.classList.remove('hidden');
1148
+ constants.reviewSaveBrailleMode = constants.brailleMode;
1149
+ constants.review.focus();
1150
+
1151
+ display.announceText('Review on');
1152
+ } else {
1153
+ constants.review_container.classList.add('hidden');
1154
+ if (constants.reviewSaveBrailleMode == 'on') {
1155
+ // we have to turn braille mode back on
1156
+ display.toggleBrailleMode('on');
1157
+ } else {
1158
+ constants.reviewSaveSpot.focus();
1159
+ }
1160
+ display.announceText('Review off');
1161
+ }
1162
+ }
1163
+ }
1164
+
1165
+ /**
1166
+ * Represents a class for logging errors.
1167
+ */
1168
+ class LogError {
1169
+ constructor() {}
1170
+
1171
+ /**
1172
+ * Logs the absent element and turns off visual highlighting.
1173
+ * @param {string} a - The absent element to log.
1174
+ */
1175
+ LogAbsentElement(a) {
1176
+ console.log(a, 'not found. Visual highlighting is turned off.');
1177
+ }
1178
+
1179
+ /**
1180
+ * Logs a critical element and indicates that MAIDR is unable to run.
1181
+ * @param {string} a - The critical element to log.
1182
+ */
1183
+ LogCriticalElement(a) {
1184
+ consolelog(a, 'is critical. MAIDR unable to run');
1185
+ }
1186
+
1187
+ /**
1188
+ * Logs a message indicating that two values do not have the same length.
1189
+ * @param {*} a - The first value to compare.
1190
+ * @param {*} b - The second value to compare.
1191
+ */
1192
+ LogDifferentLengths(a, b) {
1193
+ console.log(
1194
+ a,
1195
+ 'and',
1196
+ b,
1197
+ 'do not have the same length. Visual highlighting is turned off.'
1198
+ );
1199
+ }
1200
+
1201
+ /**
1202
+ * Logs a message indicating that too many elements were found and only the first n elements will be highlighted.
1203
+ * @param {string} a - The type of element being highlighted.
1204
+ * @param {number} b - The maximum number of elements to highlight.
1205
+ */
1206
+ LogTooManyElements(a, b) {
1207
+ console.log(
1208
+ 'Too many',
1209
+ a,
1210
+ 'elements. Only the first',
1211
+ b,
1212
+ 'will be highlighted.'
1213
+ );
1214
+ }
1215
+
1216
+ /**
1217
+ * Logs a message indicating that the provided parameter is not an array.
1218
+ * @param {*} a - The parameter that is not an array.
1219
+ */
1220
+ LogNotArray(a) {
1221
+ console.log(a, 'is not an array. Visual highlighting is turned off.');
1222
+ }
1223
+ }
1224
+ </code></pre>
1225
+ </article>
1226
+ </section>
1227
+
1228
+
1229
+
1230
+
1231
+ </div>
1232
+
1233
+ <br class="clear">
1234
+
1235
+ <footer>
1236
+ Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on Sun Nov 05 2023 14:27:04 GMT-0600 (Central Standard Time) using the Minami theme.
1237
+ </footer>
1238
+
1239
+ <script>prettyPrint();</script>
1240
+ <script src="scripts/linenumber.js"></script>
1241
+ </body>
1242
+ </html>