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