maidr 2.18.0 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/maidr.js +585 -599
- package/dist/maidr.min.js +1 -1
- package/package.json +1 -1
package/dist/maidr.js
CHANGED
|
@@ -12,77 +12,77 @@ class Constants {
|
|
|
12
12
|
* @memberof HtmlIds
|
|
13
13
|
* @default 'chart-container'
|
|
14
14
|
*/
|
|
15
|
-
chart_container_id =
|
|
15
|
+
chart_container_id = 'chart-container';
|
|
16
16
|
/**
|
|
17
17
|
* HTML id of the main container div.
|
|
18
18
|
* @type {string}
|
|
19
19
|
* @memberof HtmlIds
|
|
20
20
|
* @default 'maidr-container'
|
|
21
21
|
*/
|
|
22
|
-
main_container_id =
|
|
22
|
+
main_container_id = 'maidr-container';
|
|
23
23
|
/**
|
|
24
24
|
* HTML id of the div containing the braille display input
|
|
25
25
|
* @type {string}
|
|
26
26
|
* @memberof HtmlIds
|
|
27
27
|
* @default 'braille-div'
|
|
28
28
|
*/
|
|
29
|
-
braille_container_id =
|
|
29
|
+
braille_container_id = 'braille-div';
|
|
30
30
|
/**
|
|
31
31
|
* HTML id of the actual braille input element.
|
|
32
32
|
* @type {string}
|
|
33
33
|
* @memberof HtmlIds
|
|
34
34
|
* @default 'braille-input'
|
|
35
35
|
*/
|
|
36
|
-
braille_input_id =
|
|
36
|
+
braille_input_id = 'braille-input';
|
|
37
37
|
/**
|
|
38
38
|
* HTML id of the div containing the info box.
|
|
39
39
|
* @type {string}
|
|
40
40
|
* @memberof HtmlIds
|
|
41
41
|
* @default 'info'
|
|
42
42
|
*/
|
|
43
|
-
info_id =
|
|
43
|
+
info_id = 'info';
|
|
44
44
|
/**
|
|
45
45
|
* HTML id of the div containing announcements that hook directly into the screen reader via aria-live.
|
|
46
46
|
* @type {string}
|
|
47
47
|
* @memberof HtmlIds
|
|
48
48
|
* @default 'announcements'
|
|
49
49
|
*/
|
|
50
|
-
announcement_container_id =
|
|
50
|
+
announcement_container_id = 'announcements';
|
|
51
51
|
/**
|
|
52
52
|
* HTML id of the div containing the end chime. To be implemented in the future.
|
|
53
53
|
* @type {string}
|
|
54
54
|
* @memberof HtmlIds
|
|
55
55
|
* @default 'end_chime'
|
|
56
56
|
*/
|
|
57
|
-
end_chime_id =
|
|
57
|
+
end_chime_id = 'end_chime';
|
|
58
58
|
/**
|
|
59
59
|
* HTML id of the main container div.
|
|
60
60
|
* @type {string}
|
|
61
61
|
* @memberof HtmlIds
|
|
62
62
|
* @default 'container'
|
|
63
63
|
*/
|
|
64
|
-
container_id =
|
|
64
|
+
container_id = 'container';
|
|
65
65
|
/**
|
|
66
66
|
* The main project id, used throughout the application.
|
|
67
67
|
* @type {string}
|
|
68
68
|
* @memberof HtmlIds
|
|
69
69
|
* @default 'maidr'
|
|
70
70
|
*/
|
|
71
|
-
project_id =
|
|
71
|
+
project_id = 'maidr';
|
|
72
72
|
/**
|
|
73
73
|
* HTML id of the div containing the review text.
|
|
74
74
|
* @type {string}
|
|
75
75
|
* @memberof HtmlIds
|
|
76
76
|
* @default 'review_container'
|
|
77
77
|
*/
|
|
78
|
-
review_id_container =
|
|
78
|
+
review_id_container = 'review_container';
|
|
79
79
|
/**
|
|
80
80
|
* HTML id of the review input element.
|
|
81
81
|
* @type {string}
|
|
82
82
|
* @memberof HtmlIds
|
|
83
83
|
* @default 'review'
|
|
84
84
|
*/
|
|
85
|
-
review_id =
|
|
85
|
+
review_id = 'review';
|
|
86
86
|
/**
|
|
87
87
|
* Storage element, used to store the last focused element before moving to the review input so we can switch back to it easily.
|
|
88
88
|
* @type {HTMLElement}
|
|
@@ -99,7 +99,7 @@ class Constants {
|
|
|
99
99
|
* @type {string}
|
|
100
100
|
* @memberof HtmlIds
|
|
101
101
|
*/
|
|
102
|
-
chartId =
|
|
102
|
+
chartId = '';
|
|
103
103
|
/**
|
|
104
104
|
* @typedef {Object} EventListenerSetupObject
|
|
105
105
|
* @property {HTMLElement} element - The element to attach the event listener to.
|
|
@@ -128,14 +128,15 @@ class Constants {
|
|
|
128
128
|
* @memberof BTSModes
|
|
129
129
|
* @default 'verbose'
|
|
130
130
|
*/
|
|
131
|
-
textMode =
|
|
131
|
+
textMode = 'verbose';
|
|
132
|
+
|
|
132
133
|
/**
|
|
133
134
|
* The current braille mode. Can be 'off' or 'on'.
|
|
134
135
|
* @type {("off"|"on")}
|
|
135
136
|
* @memberof BTSModes
|
|
136
137
|
* @default 'off'
|
|
137
138
|
*/
|
|
138
|
-
brailleMode =
|
|
139
|
+
brailleMode = 'off';
|
|
139
140
|
|
|
140
141
|
/**
|
|
141
142
|
* We lock the selection so we don't pick up programatic selection changes
|
|
@@ -150,14 +151,14 @@ class Constants {
|
|
|
150
151
|
* @memberof BTSModes
|
|
151
152
|
* @default 'on'
|
|
152
153
|
*/
|
|
153
|
-
sonifMode =
|
|
154
|
+
sonifMode = 'on';
|
|
154
155
|
/**
|
|
155
156
|
* The current review mode. Can be 'on' or 'off'.
|
|
156
157
|
* @type {("on"|"off")}
|
|
157
158
|
* @memberof BTSModes
|
|
158
159
|
* @default 'off'
|
|
159
160
|
*/
|
|
160
|
-
reviewMode =
|
|
161
|
+
reviewMode = 'off';
|
|
161
162
|
|
|
162
163
|
// basic chart properties
|
|
163
164
|
/**
|
|
@@ -198,14 +199,14 @@ class Constants {
|
|
|
198
199
|
* @memberof HtmlIds
|
|
199
200
|
* @default ''
|
|
200
201
|
*/
|
|
201
|
-
plotId =
|
|
202
|
+
plotId = ''; // update with id in chart specific js
|
|
202
203
|
/**
|
|
203
204
|
* The chart type, sort of a short name of the chart such as 'box', 'bar', 'line', etc.
|
|
204
205
|
* @type {string}
|
|
205
206
|
* @default ''
|
|
206
207
|
* @memberof BasicChartProperties
|
|
207
208
|
*/
|
|
208
|
-
chartType =
|
|
209
|
+
chartType = '';
|
|
209
210
|
/**
|
|
210
211
|
* The navigation orientation of the chart. 0 = row navigation (up/down), 1 = col navigation (left/right).
|
|
211
212
|
* @type {number}
|
|
@@ -219,7 +220,7 @@ class Constants {
|
|
|
219
220
|
* @default 'horz'
|
|
220
221
|
* @memberof BasicChartProperties
|
|
221
222
|
*/
|
|
222
|
-
plotOrientation =
|
|
223
|
+
plotOrientation = 'horz';
|
|
223
224
|
|
|
224
225
|
/**
|
|
225
226
|
* @namespace AudioProperties
|
|
@@ -296,7 +297,7 @@ class Constants {
|
|
|
296
297
|
* @default 5000
|
|
297
298
|
* @memberof AudioProperties
|
|
298
299
|
*/
|
|
299
|
-
AUTOPLAY_DURATION =
|
|
300
|
+
AUTOPLAY_DURATION = 2000; // 5s
|
|
300
301
|
|
|
301
302
|
// user settings
|
|
302
303
|
/**
|
|
@@ -329,7 +330,7 @@ class Constants {
|
|
|
329
330
|
* @default '#03C809' (green)
|
|
330
331
|
* @memberof UserSettings
|
|
331
332
|
*/
|
|
332
|
-
colorSelected =
|
|
333
|
+
colorSelected = '#03C809';
|
|
333
334
|
/**
|
|
334
335
|
* The length of the braille display in characters. Braille displays have a variety of sizes; 40 is pretty common, 32 is quite reliable. Set this to your actual display length so that the system can scale and display braille properly for you (where possible).
|
|
335
336
|
* @type {number}
|
|
@@ -397,7 +398,7 @@ class Constants {
|
|
|
397
398
|
* @default 50
|
|
398
399
|
* @memberof AdvancedUserSettings
|
|
399
400
|
*/
|
|
400
|
-
colorUnselected =
|
|
401
|
+
colorUnselected = '#595959'; // deprecated, todo: find all instances replace with storing old color method
|
|
401
402
|
/**
|
|
402
403
|
* Whether or not we're logging user data. This is off by default, but is used for research purposes.
|
|
403
404
|
* @type {boolean}
|
|
@@ -425,30 +426,30 @@ class Constants {
|
|
|
425
426
|
* @default 'assertive'
|
|
426
427
|
* @memberof AdvancedUserSettings
|
|
427
428
|
*/
|
|
428
|
-
ariaMode =
|
|
429
|
+
ariaMode = 'assertive';
|
|
429
430
|
|
|
430
431
|
/**
|
|
431
432
|
* Full list of user settings, used internally to save and load settings.
|
|
432
433
|
* @type {string[]}
|
|
433
434
|
*/
|
|
434
435
|
userSettingsKeys = [
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
436
|
+
'vol',
|
|
437
|
+
'autoPlayRate',
|
|
438
|
+
'brailleDisplayLength',
|
|
439
|
+
'colorSelected',
|
|
440
|
+
'MIN_FREQUENCY',
|
|
441
|
+
'MAX_FREQUENCY',
|
|
442
|
+
'AUTOPLAY_DURATION',
|
|
443
|
+
'ariaMode',
|
|
444
|
+
'openAIAuthKey',
|
|
445
|
+
'geminiAuthKey',
|
|
446
|
+
'skillLevel',
|
|
447
|
+
'skillLevelOther',
|
|
448
|
+
'LLMModel',
|
|
449
|
+
'LLMPreferences',
|
|
450
|
+
'LLMOpenAiMulti',
|
|
451
|
+
'LLMGeminiMulti',
|
|
452
|
+
'autoInitLLM',
|
|
452
453
|
];
|
|
453
454
|
|
|
454
455
|
// LLM settings
|
|
@@ -489,14 +490,14 @@ class Constants {
|
|
|
489
490
|
* @default 'high'
|
|
490
491
|
* @memberof LLMSettings
|
|
491
492
|
*/
|
|
492
|
-
LLMDetail =
|
|
493
|
+
LLMDetail = 'high'; // low (default for testing, like 100 tokens) / high (default for real, like 1000 tokens)
|
|
493
494
|
/**
|
|
494
495
|
* Current LLM model in use. Can be 'openai' (default) or 'gemini' or 'multi'. More to be added.
|
|
495
496
|
* @type {("openai"|"gemini"|"multi")}
|
|
496
497
|
* @default 'openai'
|
|
497
498
|
* @memberof LLMSettings
|
|
498
499
|
*/
|
|
499
|
-
LLMModel =
|
|
500
|
+
LLMModel = 'openai';
|
|
500
501
|
/**
|
|
501
502
|
* The default system message for the LLM. Helps the LLM understand the context of the chart and its role.
|
|
502
503
|
* @type {string}
|
|
@@ -504,21 +505,21 @@ class Constants {
|
|
|
504
505
|
* @memberof LLMSettings
|
|
505
506
|
*/
|
|
506
507
|
LLMSystemMessage =
|
|
507
|
-
|
|
508
|
+
'You are a helpful assistant describing the chart to a blind person. ';
|
|
508
509
|
/**
|
|
509
510
|
* The level of skill the user has with statistical charts. Can be 'basic', 'intermediate', 'expert', or 'other'. This is passed to the LLM on the initial message to help it speak correctly to the user. If 'other' is selected, the user can provide a custom skill level.
|
|
510
511
|
* @type {("basic"|"intermediate"|"expert"|"other")}
|
|
511
512
|
* @default 'basic'
|
|
512
513
|
* @memberof LLMSettings
|
|
513
514
|
*/
|
|
514
|
-
skillLevel =
|
|
515
|
+
skillLevel = 'basic'; // basic / intermediate / expert
|
|
515
516
|
/**
|
|
516
517
|
* Custom skill level, used if the user selects 'other' as their skill level.
|
|
517
518
|
* @type {string}
|
|
518
519
|
* @default ''
|
|
519
520
|
* @memberof LLMSettings
|
|
520
521
|
*/
|
|
521
|
-
skillLevelOther =
|
|
522
|
+
skillLevelOther = ''; // custom skill level
|
|
522
523
|
/**
|
|
523
524
|
* The LLM can send the first default message containing the chart image on initialization, so when the user opens the chat window the LLM already has an initial response and is ready for a conversation.
|
|
524
525
|
* @type {boolean}
|
|
@@ -532,7 +533,7 @@ class Constants {
|
|
|
532
533
|
* @default ''
|
|
533
534
|
* @memberof LLMSettings
|
|
534
535
|
*/
|
|
535
|
-
verboseText =
|
|
536
|
+
verboseText = '';
|
|
536
537
|
/**
|
|
537
538
|
* An internal variable used to turn the waiting beep on and off.
|
|
538
539
|
* @type {number}
|
|
@@ -580,35 +581,38 @@ class Constants {
|
|
|
580
581
|
* @type {boolean}
|
|
581
582
|
* @memberof PlatformControls
|
|
582
583
|
*/
|
|
583
|
-
isMac = navigator.userAgent.toLowerCase().includes(
|
|
584
|
+
isMac = navigator.userAgent.toLowerCase().includes('mac'); // true if macOS
|
|
584
585
|
/**
|
|
585
586
|
* The control key for the user's platform. Can be 'Cmd' or 'Ctrl'. Used in keyboard shortcut display in help.
|
|
586
587
|
* @type {"Cmd"|"Ctrl"}
|
|
587
588
|
* @memberof PlatformControls
|
|
588
589
|
*/
|
|
589
|
-
control = this.isMac ?
|
|
590
|
+
control = this.isMac ? 'Cmd' : 'Ctrl';
|
|
590
591
|
/**
|
|
591
592
|
* The alt key for the user's platform. Can be 'option' or 'Alt'. Used in keyboard shortcut display in help.
|
|
592
593
|
* @type {"option"|"Alt"}
|
|
593
594
|
* @memberof PlatformControls
|
|
594
595
|
*/
|
|
595
|
-
alt = this.isMac ?
|
|
596
|
+
alt = this.isMac ? 'option' : 'Alt';
|
|
596
597
|
/**
|
|
597
598
|
* The home key for the user's platform. Can be 'fn + Left arrow' or 'Home'. Used in keyboard shortcut display in help.
|
|
598
599
|
* @type {"fn + Left arrow"|"Home"}
|
|
599
600
|
* @memberof PlatformControls
|
|
600
601
|
*/
|
|
601
|
-
home = this.isMac ?
|
|
602
|
+
home = this.isMac ? 'fn + Left arrow' : 'Home';
|
|
602
603
|
/**
|
|
603
604
|
* The end key for the user's platform. Can be 'fn + Right arrow' or 'End'. Used in keyboard shortcut display in help.
|
|
604
605
|
* @type {"fn + Right arrow"|"End"}
|
|
605
606
|
* @memberof PlatformControls
|
|
606
607
|
*/
|
|
607
|
-
end = this.isMac ?
|
|
608
|
+
end = this.isMac ? 'fn + Right arrow' : 'End';
|
|
609
|
+
/**
|
|
610
|
+
* The interval we wait for an L + X prefix event
|
|
611
|
+
*/
|
|
612
|
+
keypressInterval = 2000; // ms or 2s
|
|
608
613
|
|
|
609
614
|
// internal controls
|
|
610
615
|
// todo: are these even used? Sean doesn't think so (May 2024)
|
|
611
|
-
keypressInterval = 2000; // ms or 2s
|
|
612
616
|
tabMovement = null;
|
|
613
617
|
|
|
614
618
|
// debug stuff
|
|
@@ -697,13 +701,13 @@ class Constants {
|
|
|
697
701
|
*/
|
|
698
702
|
ConvertHexToRGBString(hexColorString) {
|
|
699
703
|
return (
|
|
700
|
-
|
|
704
|
+
'rgb(' +
|
|
701
705
|
parseInt(hexColorString.slice(1, 3), 16) +
|
|
702
|
-
|
|
706
|
+
',' +
|
|
703
707
|
parseInt(hexColorString.slice(3, 5), 16) +
|
|
704
|
-
|
|
708
|
+
',' +
|
|
705
709
|
parseInt(hexColorString.slice(5, 7), 16) +
|
|
706
|
-
|
|
710
|
+
')'
|
|
707
711
|
);
|
|
708
712
|
}
|
|
709
713
|
|
|
@@ -713,12 +717,12 @@ class Constants {
|
|
|
713
717
|
* @returns {string} - hexadecimal color (e.g., "#595959").
|
|
714
718
|
*/
|
|
715
719
|
ConvertRGBStringToHex(rgbColorString) {
|
|
716
|
-
let rgb = rgbColorString.replace(/[^\d,]/g,
|
|
720
|
+
let rgb = rgbColorString.replace(/[^\d,]/g, '').split(',');
|
|
717
721
|
return (
|
|
718
|
-
|
|
719
|
-
rgb[0].toString(16).padStart(2,
|
|
720
|
-
rgb[1].toString(16).padStart(2,
|
|
721
|
-
rgb[2].toString(16).padStart(2,
|
|
722
|
+
'#' +
|
|
723
|
+
rgb[0].toString(16).padStart(2, '0') +
|
|
724
|
+
rgb[1].toString(16).padStart(2, '0') +
|
|
725
|
+
rgb[2].toString(16).padStart(2, '0')
|
|
722
726
|
);
|
|
723
727
|
}
|
|
724
728
|
|
|
@@ -730,11 +734,11 @@ class Constants {
|
|
|
730
734
|
*/
|
|
731
735
|
ColorInvert(color) {
|
|
732
736
|
// invert an rgb color
|
|
733
|
-
let rgb = color.replace(/[^\d,]/g,
|
|
737
|
+
let rgb = color.replace(/[^\d,]/g, '').split(',');
|
|
734
738
|
let r = 255 - rgb[0];
|
|
735
739
|
let g = 255 - rgb[1];
|
|
736
740
|
let b = 255 - rgb[2];
|
|
737
|
-
return
|
|
741
|
+
return 'rgb(' + r + ',' + g + ',' + b + ')';
|
|
738
742
|
}
|
|
739
743
|
|
|
740
744
|
/**
|
|
@@ -743,11 +747,11 @@ class Constants {
|
|
|
743
747
|
* @returns {string} The better color
|
|
744
748
|
*/
|
|
745
749
|
GetBetterColor(oldColor) {
|
|
746
|
-
if (oldColor.indexOf(
|
|
750
|
+
if (oldColor.indexOf('#') !== -1) {
|
|
747
751
|
oldColor = this.ConvertHexToRGBString(oldColor);
|
|
748
752
|
}
|
|
749
753
|
let newColor = this.ColorInvert(oldColor);
|
|
750
|
-
let rgb = newColor.replace(/[^\d,]/g,
|
|
754
|
+
let rgb = newColor.replace(/[^\d,]/g, '').split(',');
|
|
751
755
|
if (
|
|
752
756
|
rgb[1] < rgb[0] + 10 &&
|
|
753
757
|
rgb[1] > rgb[0] - 10 &&
|
|
@@ -769,7 +773,7 @@ class Constants {
|
|
|
769
773
|
*/
|
|
770
774
|
GetStyleArrayFromString(styleString) {
|
|
771
775
|
// Get an array of CSS style attributes and values from a style string
|
|
772
|
-
return styleString.replaceAll(
|
|
776
|
+
return styleString.replaceAll(' ', '').split(/[:;]/);
|
|
773
777
|
}
|
|
774
778
|
|
|
775
779
|
/**
|
|
@@ -779,16 +783,16 @@ class Constants {
|
|
|
779
783
|
*/
|
|
780
784
|
GetStyleStringFromArray(styleArray) {
|
|
781
785
|
// Get CSS style string from an array of style attributes and values
|
|
782
|
-
let styleString =
|
|
786
|
+
let styleString = '';
|
|
783
787
|
for (let i = 0; i < styleArray.length; i++) {
|
|
784
788
|
if (i % 2 === 0) {
|
|
785
789
|
if (i !== styleArray.length - 1) {
|
|
786
|
-
styleString += styleArray[i] +
|
|
790
|
+
styleString += styleArray[i] + ': ';
|
|
787
791
|
} else {
|
|
788
792
|
styleString += styleArray[i];
|
|
789
793
|
}
|
|
790
794
|
} else {
|
|
791
|
-
styleString += styleArray[i] +
|
|
795
|
+
styleString += styleArray[i] + '; ';
|
|
792
796
|
}
|
|
793
797
|
}
|
|
794
798
|
return styleString;
|
|
@@ -801,35 +805,35 @@ class Constants {
|
|
|
801
805
|
class Resources {
|
|
802
806
|
constructor() {}
|
|
803
807
|
|
|
804
|
-
language =
|
|
805
|
-
knowledgeLevel =
|
|
808
|
+
language = 'en'; // Current language, 2 char lang code
|
|
809
|
+
knowledgeLevel = 'basic'; // basic, intermediate, expert
|
|
806
810
|
|
|
807
811
|
// language strings, per 2 char language code
|
|
808
812
|
strings = {
|
|
809
813
|
en: {
|
|
810
814
|
basic: {
|
|
811
|
-
upper_outlier:
|
|
812
|
-
lower_outlier:
|
|
813
|
-
min:
|
|
814
|
-
max:
|
|
815
|
-
25:
|
|
816
|
-
50:
|
|
817
|
-
75:
|
|
818
|
-
q1:
|
|
819
|
-
q2:
|
|
820
|
-
q3:
|
|
821
|
-
son_on:
|
|
822
|
-
son_off:
|
|
823
|
-
son_des:
|
|
824
|
-
son_comp:
|
|
825
|
-
son_ch:
|
|
826
|
-
son_sep:
|
|
827
|
-
son_same:
|
|
828
|
-
empty:
|
|
829
|
-
openai:
|
|
830
|
-
gemini:
|
|
831
|
-
multi:
|
|
832
|
-
processing:
|
|
815
|
+
upper_outlier: 'Upper Outlier',
|
|
816
|
+
lower_outlier: 'Lower Outlier',
|
|
817
|
+
min: 'Minimum',
|
|
818
|
+
max: 'Maximum',
|
|
819
|
+
25: '25%',
|
|
820
|
+
50: '50%',
|
|
821
|
+
75: '75%',
|
|
822
|
+
q1: '25%',
|
|
823
|
+
q2: '50%',
|
|
824
|
+
q3: '75%',
|
|
825
|
+
son_on: 'Sonification on',
|
|
826
|
+
son_off: 'Sonification off',
|
|
827
|
+
son_des: 'Sonification descrete',
|
|
828
|
+
son_comp: 'Sonification compare',
|
|
829
|
+
son_ch: 'Sonification chord',
|
|
830
|
+
son_sep: 'Sonification separate',
|
|
831
|
+
son_same: 'Sonification combined',
|
|
832
|
+
empty: 'Empty',
|
|
833
|
+
openai: 'OpenAI Vision',
|
|
834
|
+
gemini: 'Gemini Pro Vision',
|
|
835
|
+
multi: 'Multiple AI',
|
|
836
|
+
processing: 'Processing Chart...',
|
|
833
837
|
},
|
|
834
838
|
},
|
|
835
839
|
};
|
|
@@ -991,25 +995,20 @@ class Menu {
|
|
|
991
995
|
<h5 class="modal-title">Settings</h5>
|
|
992
996
|
<p><input type="range" id="vol" name="vol" min="0" max="1" step=".05"><label for="vol">Volume</label></p>
|
|
993
997
|
<!-- <p><input type="checkbox" id="show_rect" name="show_rect"><label for="show_rect">Show Outline</label></p> //-->
|
|
994
|
-
<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>
|
|
995
|
-
<p><input type="number" min="${
|
|
996
|
-
constants.MIN_SPEED
|
|
997
|
-
}" max="500" step="${
|
|
998
|
-
constants.INTERVAL
|
|
999
|
-
}" id="autoplay_rate" name="autoplay_rate"><label for="autoplay_rate">Autoplay Rate</label></p>
|
|
1000
998
|
<p><input type="color" id="color_selected" name="color_selected"><label for="color_selected">Outline Color</label></p>
|
|
999
|
+
<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>
|
|
1001
1000
|
<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>
|
|
1002
1001
|
<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>
|
|
1003
|
-
<p><input type="number" min="500" max="
|
|
1002
|
+
<p> <p><input type="number" min="500" max="500000" step="500" id="AUTOPLAY_DURATION">Autoplay Duration (ms)</label></p>
|
|
1004
1003
|
<div><fieldset>
|
|
1005
1004
|
<legend>Aria Mode</legend>
|
|
1006
1005
|
<p><input type="radio" id="aria_mode_assertive" name="aria_mode" value="assertive" ${
|
|
1007
|
-
constants.ariaMode ==
|
|
1008
|
-
?
|
|
1009
|
-
:
|
|
1006
|
+
constants.ariaMode == 'assertive'
|
|
1007
|
+
? 'checked'
|
|
1008
|
+
: ''
|
|
1010
1009
|
}><label for="aria_mode_assertive">Assertive</label></p>
|
|
1011
1010
|
<p><input type="radio" id="aria_mode_polite" name="aria_mode" value="polite" ${
|
|
1012
|
-
constants.ariaMode ==
|
|
1011
|
+
constants.ariaMode == 'polite' ? 'checked' : ''
|
|
1013
1012
|
}><label for="aria_mode_polite">Polite</label></p>
|
|
1014
1013
|
</fieldset></div>
|
|
1015
1014
|
<h5 class="modal-title">LLM Settings</h5>
|
|
@@ -1030,7 +1029,7 @@ class Menu {
|
|
|
1030
1029
|
<input type="password" size="50" id="gemini_auth_key"><button aria-label="Delete Gemini key" title="Delete Gemini key" id="delete_gemini_key" class="invis_button">×</button><label for="gemini_auth_key">Gemini Authentication Key</label>
|
|
1031
1030
|
</p>
|
|
1032
1031
|
<p><input type="checkbox" ${
|
|
1033
|
-
constants.autoInitLLM ?
|
|
1032
|
+
constants.autoInitLLM ? 'checked' : ''
|
|
1034
1033
|
} id="init_llm_on_load" name="init_llm_on_load"><label for="init_llm_on_load">Start first LLM chat chart load</label></p>
|
|
1035
1034
|
<p>
|
|
1036
1035
|
<select id="skill_level">
|
|
@@ -1067,33 +1066,33 @@ class Menu {
|
|
|
1067
1066
|
CreateMenu() {
|
|
1068
1067
|
// menu element creation
|
|
1069
1068
|
document
|
|
1070
|
-
.querySelector(
|
|
1071
|
-
.insertAdjacentHTML(
|
|
1069
|
+
.querySelector('body')
|
|
1070
|
+
.insertAdjacentHTML('beforeend', this.menuHtml);
|
|
1072
1071
|
|
|
1073
1072
|
// menu close events
|
|
1074
|
-
let allClose = document.querySelectorAll(
|
|
1073
|
+
let allClose = document.querySelectorAll('#close_menu, #menu .close');
|
|
1075
1074
|
for (let i = 0; i < allClose.length; i++) {
|
|
1076
1075
|
constants.events.push([
|
|
1077
1076
|
allClose[i],
|
|
1078
|
-
|
|
1077
|
+
'click',
|
|
1079
1078
|
function (e) {
|
|
1080
1079
|
menu.Toggle(false);
|
|
1081
1080
|
},
|
|
1082
1081
|
]);
|
|
1083
1082
|
}
|
|
1084
1083
|
constants.events.push([
|
|
1085
|
-
document.getElementById(
|
|
1086
|
-
|
|
1084
|
+
document.getElementById('save_and_close_menu'),
|
|
1085
|
+
'click',
|
|
1087
1086
|
function (e) {
|
|
1088
1087
|
menu.SaveData();
|
|
1089
1088
|
menu.Toggle(false);
|
|
1090
1089
|
},
|
|
1091
1090
|
]);
|
|
1092
1091
|
constants.events.push([
|
|
1093
|
-
document.getElementById(
|
|
1094
|
-
|
|
1092
|
+
document.getElementById('menu'),
|
|
1093
|
+
'keyup',
|
|
1095
1094
|
function (e) {
|
|
1096
|
-
if (e.key ==
|
|
1095
|
+
if (e.key == 'Esc') {
|
|
1097
1096
|
// esc
|
|
1098
1097
|
menu.Toggle(false);
|
|
1099
1098
|
}
|
|
@@ -1103,15 +1102,15 @@ class Menu {
|
|
|
1103
1102
|
// Menu open events
|
|
1104
1103
|
constants.events.push([
|
|
1105
1104
|
document,
|
|
1106
|
-
|
|
1105
|
+
'keyup',
|
|
1107
1106
|
function (e) {
|
|
1108
1107
|
// don't fire on input elements
|
|
1109
1108
|
if (
|
|
1110
|
-
e.target.tagName.toLowerCase() ==
|
|
1111
|
-
e.target.tagName.toLowerCase() ==
|
|
1109
|
+
e.target.tagName.toLowerCase() == 'input' ||
|
|
1110
|
+
e.target.tagName.toLowerCase() == 'textarea'
|
|
1112
1111
|
) {
|
|
1113
1112
|
return;
|
|
1114
|
-
} else if (e.key ==
|
|
1113
|
+
} else if (e.key == 'h') {
|
|
1115
1114
|
menu.Toggle(true);
|
|
1116
1115
|
}
|
|
1117
1116
|
},
|
|
@@ -1119,71 +1118,71 @@ class Menu {
|
|
|
1119
1118
|
|
|
1120
1119
|
// toggle auth key fields
|
|
1121
1120
|
constants.events.push([
|
|
1122
|
-
document.getElementById(
|
|
1123
|
-
|
|
1121
|
+
document.getElementById('LLM_model'),
|
|
1122
|
+
'change',
|
|
1124
1123
|
function (e) {
|
|
1125
|
-
if (e.target.value ==
|
|
1124
|
+
if (e.target.value == 'openai') {
|
|
1126
1125
|
document
|
|
1127
|
-
.getElementById(
|
|
1128
|
-
.classList.remove(
|
|
1126
|
+
.getElementById('openai_auth_key_container')
|
|
1127
|
+
.classList.remove('hidden');
|
|
1129
1128
|
document
|
|
1130
|
-
.getElementById(
|
|
1131
|
-
.classList.add(
|
|
1129
|
+
.getElementById('gemini_auth_key_container')
|
|
1130
|
+
.classList.add('hidden');
|
|
1132
1131
|
document
|
|
1133
|
-
.getElementById(
|
|
1134
|
-
.classList.add(
|
|
1132
|
+
.getElementById('openai_multi_container')
|
|
1133
|
+
.classList.add('hidden');
|
|
1135
1134
|
document
|
|
1136
|
-
.getElementById(
|
|
1137
|
-
.classList.add(
|
|
1138
|
-
document.getElementById(
|
|
1139
|
-
document.getElementById(
|
|
1140
|
-
} else if (e.target.value ==
|
|
1135
|
+
.getElementById('gemini_multi_container')
|
|
1136
|
+
.classList.add('hidden');
|
|
1137
|
+
document.getElementById('openai_multi').checked = true;
|
|
1138
|
+
document.getElementById('gemini_multi').checked = false;
|
|
1139
|
+
} else if (e.target.value == 'gemini') {
|
|
1141
1140
|
document
|
|
1142
|
-
.getElementById(
|
|
1143
|
-
.classList.add(
|
|
1141
|
+
.getElementById('openai_auth_key_container')
|
|
1142
|
+
.classList.add('hidden');
|
|
1144
1143
|
document
|
|
1145
|
-
.getElementById(
|
|
1146
|
-
.classList.remove(
|
|
1144
|
+
.getElementById('gemini_auth_key_container')
|
|
1145
|
+
.classList.remove('hidden');
|
|
1147
1146
|
document
|
|
1148
|
-
.getElementById(
|
|
1149
|
-
.classList.add(
|
|
1147
|
+
.getElementById('openai_multi_container')
|
|
1148
|
+
.classList.add('hidden');
|
|
1150
1149
|
document
|
|
1151
|
-
.getElementById(
|
|
1152
|
-
.classList.add(
|
|
1153
|
-
document.getElementById(
|
|
1154
|
-
document.getElementById(
|
|
1155
|
-
} else if (e.target.value ==
|
|
1150
|
+
.getElementById('gemini_multi_container')
|
|
1151
|
+
.classList.add('hidden');
|
|
1152
|
+
document.getElementById('openai_multi').checked = false;
|
|
1153
|
+
document.getElementById('gemini_multi').checked = true;
|
|
1154
|
+
} else if (e.target.value == 'multi') {
|
|
1156
1155
|
document
|
|
1157
|
-
.getElementById(
|
|
1158
|
-
.classList.remove(
|
|
1156
|
+
.getElementById('openai_auth_key_container')
|
|
1157
|
+
.classList.remove('hidden');
|
|
1159
1158
|
document
|
|
1160
|
-
.getElementById(
|
|
1161
|
-
.classList.remove(
|
|
1159
|
+
.getElementById('gemini_auth_key_container')
|
|
1160
|
+
.classList.remove('hidden');
|
|
1162
1161
|
document
|
|
1163
|
-
.getElementById(
|
|
1164
|
-
.classList.remove(
|
|
1162
|
+
.getElementById('openai_multi_container')
|
|
1163
|
+
.classList.remove('hidden');
|
|
1165
1164
|
document
|
|
1166
|
-
.getElementById(
|
|
1167
|
-
.classList.remove(
|
|
1168
|
-
document.getElementById(
|
|
1169
|
-
document.getElementById(
|
|
1165
|
+
.getElementById('gemini_multi_container')
|
|
1166
|
+
.classList.remove('hidden');
|
|
1167
|
+
document.getElementById('openai_multi').checked = true;
|
|
1168
|
+
document.getElementById('gemini_multi').checked = true;
|
|
1170
1169
|
}
|
|
1171
1170
|
},
|
|
1172
1171
|
]);
|
|
1173
1172
|
|
|
1174
1173
|
// Skill level other events
|
|
1175
1174
|
constants.events.push([
|
|
1176
|
-
document.getElementById(
|
|
1177
|
-
|
|
1175
|
+
document.getElementById('skill_level'),
|
|
1176
|
+
'change',
|
|
1178
1177
|
function (e) {
|
|
1179
|
-
if (e.target.value ==
|
|
1178
|
+
if (e.target.value == 'other') {
|
|
1180
1179
|
document
|
|
1181
|
-
.getElementById(
|
|
1182
|
-
.classList.remove(
|
|
1180
|
+
.getElementById('skill_level_other_container')
|
|
1181
|
+
.classList.remove('hidden');
|
|
1183
1182
|
} else {
|
|
1184
1183
|
document
|
|
1185
|
-
.getElementById(
|
|
1186
|
-
.classList.add(
|
|
1184
|
+
.getElementById('skill_level_other_container')
|
|
1185
|
+
.classList.add('hidden');
|
|
1187
1186
|
}
|
|
1188
1187
|
},
|
|
1189
1188
|
]);
|
|
@@ -1191,16 +1190,16 @@ class Menu {
|
|
|
1191
1190
|
// trigger notification that LLM will be reset
|
|
1192
1191
|
// this is done on change of LLM model, multi settings, or skill level
|
|
1193
1192
|
let LLMResetIds = [
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1193
|
+
'LLM_model',
|
|
1194
|
+
'openai_multi',
|
|
1195
|
+
'gemini_multi',
|
|
1196
|
+
'skill_level',
|
|
1197
|
+
'LLM_preferences',
|
|
1199
1198
|
];
|
|
1200
1199
|
for (let i = 0; i < LLMResetIds.length; i++) {
|
|
1201
1200
|
constants.events.push([
|
|
1202
1201
|
document.getElementById(LLMResetIds[i]),
|
|
1203
|
-
|
|
1202
|
+
'change',
|
|
1204
1203
|
function (e) {
|
|
1205
1204
|
menu.NotifyOfLLMReset();
|
|
1206
1205
|
},
|
|
@@ -1214,11 +1213,11 @@ class Menu {
|
|
|
1214
1213
|
*/
|
|
1215
1214
|
Destroy() {
|
|
1216
1215
|
// menu element destruction
|
|
1217
|
-
let menu = document.getElementById(
|
|
1216
|
+
let menu = document.getElementById('menu');
|
|
1218
1217
|
if (menu) {
|
|
1219
1218
|
menu.remove();
|
|
1220
1219
|
}
|
|
1221
|
-
let backdrop = document.getElementById(
|
|
1220
|
+
let backdrop = document.getElementById('menu_modal_backdrop');
|
|
1222
1221
|
if (backdrop) {
|
|
1223
1222
|
backdrop.remove();
|
|
1224
1223
|
}
|
|
@@ -1230,16 +1229,16 @@ class Menu {
|
|
|
1230
1229
|
* @return {void}
|
|
1231
1230
|
*/
|
|
1232
1231
|
Toggle(onoff = false) {
|
|
1233
|
-
if (typeof onoff ==
|
|
1234
|
-
if (document.getElementById(
|
|
1232
|
+
if (typeof onoff == 'undefined') {
|
|
1233
|
+
if (document.getElementById('menu').classList.contains('hidden')) {
|
|
1235
1234
|
onoff = true;
|
|
1236
1235
|
} else {
|
|
1237
1236
|
onoff = false;
|
|
1238
1237
|
}
|
|
1239
1238
|
}
|
|
1240
1239
|
// don't open if we have another modal open already
|
|
1241
|
-
if (onoff && document.getElementById(
|
|
1242
|
-
if (!document.getElementById(
|
|
1240
|
+
if (onoff && document.getElementById('chatLLM')) {
|
|
1241
|
+
if (!document.getElementById('chatLLM').classList.contains('hidden')) {
|
|
1243
1242
|
return;
|
|
1244
1243
|
}
|
|
1245
1244
|
}
|
|
@@ -1248,13 +1247,13 @@ class Menu {
|
|
|
1248
1247
|
this.whereWasMyFocus = document.activeElement;
|
|
1249
1248
|
this.PopulateData();
|
|
1250
1249
|
constants.tabMovement = 0;
|
|
1251
|
-
document.getElementById(
|
|
1252
|
-
document.getElementById(
|
|
1253
|
-
document.querySelector(
|
|
1250
|
+
document.getElementById('menu').classList.remove('hidden');
|
|
1251
|
+
document.getElementById('menu_modal_backdrop').classList.remove('hidden');
|
|
1252
|
+
document.querySelector('#menu .close').focus();
|
|
1254
1253
|
} else {
|
|
1255
1254
|
// close
|
|
1256
|
-
document.getElementById(
|
|
1257
|
-
document.getElementById(
|
|
1255
|
+
document.getElementById('menu').classList.add('hidden');
|
|
1256
|
+
document.getElementById('menu_modal_backdrop').classList.add('hidden');
|
|
1258
1257
|
this.whereWasMyFocus.focus();
|
|
1259
1258
|
this.whereWasMyFocus = null;
|
|
1260
1259
|
}
|
|
@@ -1265,89 +1264,88 @@ class Menu {
|
|
|
1265
1264
|
* @return {void}
|
|
1266
1265
|
*/
|
|
1267
1266
|
PopulateData() {
|
|
1268
|
-
document.getElementById(
|
|
1269
|
-
document.getElementById(
|
|
1270
|
-
document.getElementById("braille_display_length").value =
|
|
1267
|
+
document.getElementById('vol').value = constants.vol;
|
|
1268
|
+
document.getElementById('braille_display_length').value =
|
|
1271
1269
|
constants.brailleDisplayLength;
|
|
1272
|
-
document.getElementById(
|
|
1273
|
-
document.getElementById(
|
|
1274
|
-
document.getElementById(
|
|
1275
|
-
document.getElementById(
|
|
1276
|
-
constants.
|
|
1277
|
-
if (typeof constants.openAIAuthKey ==
|
|
1278
|
-
document.getElementById(
|
|
1270
|
+
document.getElementById('color_selected').value = constants.colorSelected;
|
|
1271
|
+
document.getElementById('min_freq').value = constants.MIN_FREQUENCY;
|
|
1272
|
+
document.getElementById('max_freq').value = constants.MAX_FREQUENCY;
|
|
1273
|
+
document.getElementById('AUTOPLAY_DURATION').value =
|
|
1274
|
+
constants.AUTOPLAY_DURATION;
|
|
1275
|
+
if (typeof constants.openAIAuthKey == 'string') {
|
|
1276
|
+
document.getElementById('openai_auth_key').value =
|
|
1279
1277
|
constants.openAIAuthKey;
|
|
1280
1278
|
}
|
|
1281
|
-
if (typeof constants.geminiAuthKey ==
|
|
1282
|
-
document.getElementById(
|
|
1279
|
+
if (typeof constants.geminiAuthKey == 'string') {
|
|
1280
|
+
document.getElementById('gemini_auth_key').value =
|
|
1283
1281
|
constants.geminiAuthKey;
|
|
1284
1282
|
}
|
|
1285
|
-
document.getElementById(
|
|
1283
|
+
document.getElementById('skill_level').value = constants.skillLevel;
|
|
1286
1284
|
if (constants.skillLevelOther) {
|
|
1287
|
-
document.getElementById(
|
|
1285
|
+
document.getElementById('skill_level_other').value =
|
|
1288
1286
|
constants.skillLevelOther;
|
|
1289
1287
|
}
|
|
1290
|
-
document.getElementById(
|
|
1288
|
+
document.getElementById('LLM_model').value = constants.LLMModel;
|
|
1291
1289
|
|
|
1292
1290
|
// aria mode
|
|
1293
|
-
if (constants.ariaMode ==
|
|
1294
|
-
document.getElementById(
|
|
1295
|
-
document.getElementById(
|
|
1291
|
+
if (constants.ariaMode == 'assertive') {
|
|
1292
|
+
document.getElementById('aria_mode_assertive').checked = true;
|
|
1293
|
+
document.getElementById('aria_mode_polite').checked = false;
|
|
1296
1294
|
} else {
|
|
1297
|
-
document.getElementById(
|
|
1298
|
-
document.getElementById(
|
|
1295
|
+
document.getElementById('aria_mode_polite').checked = true;
|
|
1296
|
+
document.getElementById('aria_mode_assertive').checked = false;
|
|
1299
1297
|
}
|
|
1300
1298
|
// hide either openai or gemini auth key field
|
|
1301
|
-
if (constants.LLMModel ==
|
|
1299
|
+
if (constants.LLMModel == 'openai') {
|
|
1302
1300
|
document
|
|
1303
|
-
.getElementById(
|
|
1304
|
-
.classList.remove(
|
|
1301
|
+
.getElementById('openai_auth_key_container')
|
|
1302
|
+
.classList.remove('hidden');
|
|
1305
1303
|
document
|
|
1306
|
-
.getElementById(
|
|
1307
|
-
.classList.add(
|
|
1308
|
-
} else if (constants.LLMModel ==
|
|
1304
|
+
.getElementById('gemini_auth_key_container')
|
|
1305
|
+
.classList.add('hidden');
|
|
1306
|
+
} else if (constants.LLMModel == 'gemini') {
|
|
1309
1307
|
document
|
|
1310
|
-
.getElementById(
|
|
1311
|
-
.classList.add(
|
|
1308
|
+
.getElementById('openai_auth_key_container')
|
|
1309
|
+
.classList.add('hidden');
|
|
1312
1310
|
document
|
|
1313
|
-
.getElementById(
|
|
1314
|
-
.classList.remove(
|
|
1315
|
-
} else if (constants.LLMModel ==
|
|
1311
|
+
.getElementById('gemini_auth_key_container')
|
|
1312
|
+
.classList.remove('hidden');
|
|
1313
|
+
} else if (constants.LLMModel == 'multi') {
|
|
1316
1314
|
// multi LLM mode
|
|
1317
1315
|
document
|
|
1318
|
-
.getElementById(
|
|
1319
|
-
.classList.remove(
|
|
1316
|
+
.getElementById('openai_auth_key_container')
|
|
1317
|
+
.classList.remove('hidden');
|
|
1320
1318
|
document
|
|
1321
|
-
.getElementById(
|
|
1322
|
-
.classList.remove(
|
|
1319
|
+
.getElementById('gemini_auth_key_container')
|
|
1320
|
+
.classList.remove('hidden');
|
|
1323
1321
|
document
|
|
1324
|
-
.getElementById(
|
|
1325
|
-
.classList.remove(
|
|
1322
|
+
.getElementById('openai_multi_container')
|
|
1323
|
+
.classList.remove('hidden');
|
|
1326
1324
|
document
|
|
1327
|
-
.getElementById(
|
|
1328
|
-
.classList.remove(
|
|
1329
|
-
document.getElementById(
|
|
1325
|
+
.getElementById('gemini_multi_container')
|
|
1326
|
+
.classList.remove('hidden');
|
|
1327
|
+
document.getElementById('openai_multi').checked = false;
|
|
1330
1328
|
if (constants.LLMOpenAiMulti) {
|
|
1331
|
-
document.getElementById(
|
|
1329
|
+
document.getElementById('openai_multi').checked = true;
|
|
1332
1330
|
}
|
|
1333
|
-
document.getElementById(
|
|
1331
|
+
document.getElementById('gemini_multi').checked = false;
|
|
1334
1332
|
if (constants.LLMGeminiMulti) {
|
|
1335
|
-
document.getElementById(
|
|
1333
|
+
document.getElementById('gemini_multi').checked = true;
|
|
1336
1334
|
}
|
|
1337
1335
|
}
|
|
1338
1336
|
// skill level other
|
|
1339
|
-
if (constants.skillLevel ==
|
|
1337
|
+
if (constants.skillLevel == 'other') {
|
|
1340
1338
|
document
|
|
1341
|
-
.getElementById(
|
|
1342
|
-
.classList.remove(
|
|
1339
|
+
.getElementById('skill_level_other_container')
|
|
1340
|
+
.classList.remove('hidden');
|
|
1343
1341
|
}
|
|
1344
1342
|
// LLM preferences
|
|
1345
1343
|
if (constants.LLMPreferences) {
|
|
1346
|
-
document.getElementById(
|
|
1344
|
+
document.getElementById('LLM_preferences').value =
|
|
1347
1345
|
constants.LLMPreferences;
|
|
1348
1346
|
}
|
|
1349
|
-
if (document.getElementById(
|
|
1350
|
-
document.getElementById(
|
|
1347
|
+
if (document.getElementById('LLM_reset_notification')) {
|
|
1348
|
+
document.getElementById('LLM_reset_notification').remove();
|
|
1351
1349
|
}
|
|
1352
1350
|
}
|
|
1353
1351
|
|
|
@@ -1358,33 +1356,32 @@ class Menu {
|
|
|
1358
1356
|
SaveData() {
|
|
1359
1357
|
let shouldReset = this.ShouldLLMReset();
|
|
1360
1358
|
|
|
1361
|
-
constants.vol = document.getElementById(
|
|
1362
|
-
constants.autoPlayRate = document.getElementById("autoplay_rate").value;
|
|
1359
|
+
constants.vol = document.getElementById('vol').value;
|
|
1363
1360
|
constants.brailleDisplayLength = document.getElementById(
|
|
1364
|
-
|
|
1361
|
+
'braille_display_length'
|
|
1365
1362
|
).value;
|
|
1366
|
-
constants.colorSelected = document.getElementById(
|
|
1367
|
-
constants.MIN_FREQUENCY = document.getElementById(
|
|
1368
|
-
constants.MAX_FREQUENCY = document.getElementById(
|
|
1369
|
-
constants.
|
|
1370
|
-
document.getElementById(
|
|
1371
|
-
|
|
1372
|
-
constants.openAIAuthKey = document.getElementById(
|
|
1373
|
-
constants.geminiAuthKey = document.getElementById(
|
|
1374
|
-
constants.skillLevel = document.getElementById(
|
|
1363
|
+
constants.colorSelected = document.getElementById('color_selected').value;
|
|
1364
|
+
constants.MIN_FREQUENCY = document.getElementById('min_freq').value;
|
|
1365
|
+
constants.MAX_FREQUENCY = document.getElementById('max_freq').value;
|
|
1366
|
+
constants.AUTOPLAY_DURATION =
|
|
1367
|
+
document.getElementById('AUTOPLAY_DURATION').value;
|
|
1368
|
+
|
|
1369
|
+
constants.openAIAuthKey = document.getElementById('openai_auth_key').value;
|
|
1370
|
+
constants.geminiAuthKey = document.getElementById('gemini_auth_key').value;
|
|
1371
|
+
constants.skillLevel = document.getElementById('skill_level').value;
|
|
1375
1372
|
constants.skillLevelOther =
|
|
1376
|
-
document.getElementById(
|
|
1377
|
-
constants.LLMModel = document.getElementById(
|
|
1378
|
-
constants.LLMPreferences = document.getElementById(
|
|
1379
|
-
constants.LLMOpenAiMulti = document.getElementById(
|
|
1380
|
-
constants.LLMGeminiMulti = document.getElementById(
|
|
1381
|
-
constants.autoInitLLM = document.getElementById(
|
|
1373
|
+
document.getElementById('skill_level_other').value;
|
|
1374
|
+
constants.LLMModel = document.getElementById('LLM_model').value;
|
|
1375
|
+
constants.LLMPreferences = document.getElementById('LLM_preferences').value;
|
|
1376
|
+
constants.LLMOpenAiMulti = document.getElementById('openai_multi').checked;
|
|
1377
|
+
constants.LLMGeminiMulti = document.getElementById('gemini_multi').checked;
|
|
1378
|
+
constants.autoInitLLM = document.getElementById('init_llm_on_load').checked;
|
|
1382
1379
|
|
|
1383
1380
|
// aria
|
|
1384
|
-
if (document.getElementById(
|
|
1385
|
-
constants.ariaMode =
|
|
1386
|
-
} else if (document.getElementById(
|
|
1387
|
-
constants.ariaMode =
|
|
1381
|
+
if (document.getElementById('aria_mode_assertive').checked) {
|
|
1382
|
+
constants.ariaMode = 'assertive';
|
|
1383
|
+
} else if (document.getElementById('aria_mode_polite').checked) {
|
|
1384
|
+
constants.ariaMode = 'polite';
|
|
1388
1385
|
}
|
|
1389
1386
|
|
|
1390
1387
|
this.SaveDataToLocalStorage();
|
|
@@ -1403,29 +1400,29 @@ class Menu {
|
|
|
1403
1400
|
*/
|
|
1404
1401
|
UpdateHtml() {
|
|
1405
1402
|
// set aria attributes
|
|
1406
|
-
constants.infoDiv.setAttribute(
|
|
1403
|
+
constants.infoDiv.setAttribute('aria-live', constants.ariaMode);
|
|
1407
1404
|
document
|
|
1408
1405
|
.getElementById(constants.announcement_container_id)
|
|
1409
|
-
.setAttribute(
|
|
1406
|
+
.setAttribute('aria-live', constants.ariaMode);
|
|
1410
1407
|
|
|
1411
|
-
document.getElementById(
|
|
1412
|
-
const scatter = document.getElementsByClassName(
|
|
1413
|
-
const heatmap = document.getElementById(
|
|
1414
|
-
const line = document.getElementById(
|
|
1408
|
+
document.getElementById('init_llm_on_load').checked = constants.autoInitLLM;
|
|
1409
|
+
const scatter = document.getElementsByClassName('highlight_point');
|
|
1410
|
+
const heatmap = document.getElementById('highlight_rect');
|
|
1411
|
+
const line = document.getElementById('highlight_point');
|
|
1415
1412
|
|
|
1416
1413
|
if (scatter !== null && scatter.length > 0) {
|
|
1417
1414
|
for (let i = 0; i < scatter.length; i++) {
|
|
1418
|
-
scatter[i].setAttribute(
|
|
1419
|
-
scatter[i].setAttribute(
|
|
1415
|
+
scatter[i].setAttribute('stroke', constants.colorSelected);
|
|
1416
|
+
scatter[i].setAttribute('fill', constants.colorSelected);
|
|
1420
1417
|
}
|
|
1421
1418
|
}
|
|
1422
1419
|
|
|
1423
1420
|
if (heatmap !== null) {
|
|
1424
|
-
heatmap.setAttribute(
|
|
1421
|
+
heatmap.setAttribute('stroke', constants.colorSelected);
|
|
1425
1422
|
}
|
|
1426
1423
|
|
|
1427
1424
|
if (line !== null) {
|
|
1428
|
-
line.setAttribute(
|
|
1425
|
+
line.setAttribute('stroke', constants.colorSelected);
|
|
1429
1426
|
}
|
|
1430
1427
|
}
|
|
1431
1428
|
|
|
@@ -1437,19 +1434,19 @@ class Menu {
|
|
|
1437
1434
|
let html =
|
|
1438
1435
|
'<p id="LLM_reset_notification">Note: Changes in LLM settings will reset any existing conversation.</p>';
|
|
1439
1436
|
|
|
1440
|
-
if (document.getElementById(
|
|
1441
|
-
document.getElementById(
|
|
1437
|
+
if (document.getElementById('LLM_reset_notification')) {
|
|
1438
|
+
document.getElementById('LLM_reset_notification').remove();
|
|
1442
1439
|
}
|
|
1443
1440
|
document
|
|
1444
|
-
.getElementById(
|
|
1445
|
-
.parentElement.insertAdjacentHTML(
|
|
1441
|
+
.getElementById('save_and_close_menu')
|
|
1442
|
+
.parentElement.insertAdjacentHTML('afterend', html);
|
|
1446
1443
|
|
|
1447
1444
|
// add to aria button text
|
|
1448
1445
|
document
|
|
1449
|
-
.getElementById(
|
|
1446
|
+
.getElementById('save_and_close_menu')
|
|
1450
1447
|
.setAttribute(
|
|
1451
|
-
|
|
1452
|
-
|
|
1448
|
+
'aria-labelledby',
|
|
1449
|
+
'save_and_close_text LLM_reset_notification'
|
|
1453
1450
|
);
|
|
1454
1451
|
}
|
|
1455
1452
|
/**
|
|
@@ -1461,29 +1458,29 @@ class Menu {
|
|
|
1461
1458
|
let shouldReset = false;
|
|
1462
1459
|
if (
|
|
1463
1460
|
!shouldReset &&
|
|
1464
|
-
constants.skillLevel != document.getElementById(
|
|
1461
|
+
constants.skillLevel != document.getElementById('skill_level').value
|
|
1465
1462
|
) {
|
|
1466
1463
|
shouldReset = true;
|
|
1467
1464
|
}
|
|
1468
1465
|
if (
|
|
1469
1466
|
!shouldReset &&
|
|
1470
1467
|
constants.LLMPreferences !=
|
|
1471
|
-
document.getElementById(
|
|
1468
|
+
document.getElementById('LLM_preferences').value
|
|
1472
1469
|
) {
|
|
1473
1470
|
shouldReset = true;
|
|
1474
1471
|
}
|
|
1475
1472
|
if (
|
|
1476
1473
|
!shouldReset &&
|
|
1477
|
-
constants.LLMModel != document.getElementById(
|
|
1474
|
+
constants.LLMModel != document.getElementById('LLM_model').value
|
|
1478
1475
|
) {
|
|
1479
1476
|
shouldReset = true;
|
|
1480
1477
|
}
|
|
1481
1478
|
if (
|
|
1482
1479
|
!shouldReset &&
|
|
1483
1480
|
(constants.LLMOpenAiMulti !=
|
|
1484
|
-
document.getElementById(
|
|
1481
|
+
document.getElementById('openai_multi').checked ||
|
|
1485
1482
|
constants.LLMGeminiMulti !=
|
|
1486
|
-
document.getElementById(
|
|
1483
|
+
document.getElementById('gemini_multi').checked)
|
|
1487
1484
|
) {
|
|
1488
1485
|
shouldReset = true;
|
|
1489
1486
|
}
|
|
@@ -1501,27 +1498,28 @@ class Menu {
|
|
|
1501
1498
|
data[constants.userSettingsKeys[i]] =
|
|
1502
1499
|
constants[constants.userSettingsKeys[i]];
|
|
1503
1500
|
}
|
|
1504
|
-
localStorage.setItem(
|
|
1501
|
+
localStorage.setItem('settings_data', JSON.stringify(data));
|
|
1505
1502
|
|
|
1506
1503
|
// also save to tracking if we're doing that
|
|
1507
1504
|
if (constants.isTracking) {
|
|
1508
1505
|
// but not auth keys
|
|
1509
|
-
data.openAIAuthKey =
|
|
1510
|
-
data.geminiAuthKey =
|
|
1506
|
+
data.openAIAuthKey = 'hidden';
|
|
1507
|
+
data.geminiAuthKey = 'hidden';
|
|
1511
1508
|
// and need a timestamp
|
|
1512
1509
|
data.timestamp = new Date().toISOString();
|
|
1513
|
-
tracker.SetData(
|
|
1510
|
+
tracker.SetData('settings', data);
|
|
1514
1511
|
}
|
|
1515
1512
|
}
|
|
1516
1513
|
/**
|
|
1517
1514
|
* Loads data from 'settings_data' localStorage, and updates contants variables
|
|
1518
1515
|
*/
|
|
1519
1516
|
LoadDataFromLocalStorage() {
|
|
1520
|
-
let data = JSON.parse(localStorage.getItem(
|
|
1517
|
+
let data = JSON.parse(localStorage.getItem('settings_data'));
|
|
1521
1518
|
if (data) {
|
|
1522
1519
|
for (let i = 0; i < constants.userSettingsKeys.length; i++) {
|
|
1523
1520
|
constants[constants.userSettingsKeys[i]] =
|
|
1524
|
-
data[constants.userSettingsKeys[i]]
|
|
1521
|
+
data[constants.userSettingsKeys[i]] ||
|
|
1522
|
+
constants[constants.userSettingsKeys[i]];
|
|
1525
1523
|
}
|
|
1526
1524
|
}
|
|
1527
1525
|
this.PopulateData();
|
|
@@ -1543,9 +1541,9 @@ class ChatLLM {
|
|
|
1543
1541
|
if (constants.autoInitLLM) {
|
|
1544
1542
|
// only run if we have API keys set
|
|
1545
1543
|
if (
|
|
1546
|
-
(constants.LLMModel ==
|
|
1547
|
-
(constants.LLMModel ==
|
|
1548
|
-
(constants.LLMModel ==
|
|
1544
|
+
(constants.LLMModel == 'openai' && constants.openAIAuthKey) ||
|
|
1545
|
+
(constants.LLMModel == 'gemini' && constants.geminiAuthKey) ||
|
|
1546
|
+
(constants.LLMModel == 'multi' &&
|
|
1549
1547
|
constants.openAIAuthKey &&
|
|
1550
1548
|
constants.geminiAuthKey)
|
|
1551
1549
|
) {
|
|
@@ -1600,7 +1598,7 @@ class ChatLLM {
|
|
|
1600
1598
|
</div>
|
|
1601
1599
|
<div id="chatLLM_modal_backdrop" class="modal-backdrop hidden"></div>
|
|
1602
1600
|
`;
|
|
1603
|
-
document.querySelector(
|
|
1601
|
+
document.querySelector('body').insertAdjacentHTML('beforeend', html);
|
|
1604
1602
|
}
|
|
1605
1603
|
|
|
1606
1604
|
/**
|
|
@@ -1609,21 +1607,21 @@ class ChatLLM {
|
|
|
1609
1607
|
*/
|
|
1610
1608
|
SetEvents() {
|
|
1611
1609
|
// chatLLM close events
|
|
1612
|
-
let allClose = document.querySelectorAll(
|
|
1610
|
+
let allClose = document.querySelectorAll('#close_chatLLM, #chatLLM .close');
|
|
1613
1611
|
for (let i = 0; i < allClose.length; i++) {
|
|
1614
1612
|
constants.events.push([
|
|
1615
1613
|
allClose[i],
|
|
1616
|
-
|
|
1614
|
+
'click',
|
|
1617
1615
|
function (e) {
|
|
1618
1616
|
chatLLM.Toggle(false);
|
|
1619
1617
|
},
|
|
1620
1618
|
]);
|
|
1621
1619
|
}
|
|
1622
1620
|
constants.events.push([
|
|
1623
|
-
document.getElementById(
|
|
1624
|
-
|
|
1621
|
+
document.getElementById('chatLLM'),
|
|
1622
|
+
'keyup',
|
|
1625
1623
|
function (e) {
|
|
1626
|
-
if (e.key ==
|
|
1624
|
+
if (e.key == 'Esc') {
|
|
1627
1625
|
// esc
|
|
1628
1626
|
chatLLM.Toggle(false);
|
|
1629
1627
|
}
|
|
@@ -1633,9 +1631,9 @@ class ChatLLM {
|
|
|
1633
1631
|
// ChatLLM open/close toggle
|
|
1634
1632
|
constants.events.push([
|
|
1635
1633
|
document,
|
|
1636
|
-
|
|
1634
|
+
'keyup',
|
|
1637
1635
|
function (e) {
|
|
1638
|
-
if ((e.key ==
|
|
1636
|
+
if ((e.key == '?' && (e.ctrlKey || e.metaKey)) || e.key == '¿') {
|
|
1639
1637
|
chatLLM.Toggle();
|
|
1640
1638
|
}
|
|
1641
1639
|
},
|
|
@@ -1643,21 +1641,21 @@ class ChatLLM {
|
|
|
1643
1641
|
|
|
1644
1642
|
// ChatLLM request events
|
|
1645
1643
|
constants.events.push([
|
|
1646
|
-
document.getElementById(
|
|
1647
|
-
|
|
1644
|
+
document.getElementById('chatLLM_submit'),
|
|
1645
|
+
'click',
|
|
1648
1646
|
function (e) {
|
|
1649
|
-
let text = document.getElementById(
|
|
1650
|
-
chatLLM.DisplayChatMessage(
|
|
1647
|
+
let text = document.getElementById('chatLLM_input').value;
|
|
1648
|
+
chatLLM.DisplayChatMessage('User', text);
|
|
1651
1649
|
chatLLM.Submit(text);
|
|
1652
1650
|
},
|
|
1653
1651
|
]);
|
|
1654
1652
|
constants.events.push([
|
|
1655
|
-
document.getElementById(
|
|
1656
|
-
|
|
1653
|
+
document.getElementById('chatLLM_input'),
|
|
1654
|
+
'keyup',
|
|
1657
1655
|
function (e) {
|
|
1658
|
-
if (e.key ==
|
|
1659
|
-
let text = document.getElementById(
|
|
1660
|
-
chatLLM.DisplayChatMessage(
|
|
1656
|
+
if (e.key == 'Enter' && !e.shiftKey) {
|
|
1657
|
+
let text = document.getElementById('chatLLM_input').value;
|
|
1658
|
+
chatLLM.DisplayChatMessage('User', text);
|
|
1661
1659
|
chatLLM.Submit(text);
|
|
1662
1660
|
}
|
|
1663
1661
|
},
|
|
@@ -1666,15 +1664,15 @@ class ChatLLM {
|
|
|
1666
1664
|
// ChatLLM suggestion events
|
|
1667
1665
|
// actual suggestions:
|
|
1668
1666
|
let suggestions = document.querySelectorAll(
|
|
1669
|
-
|
|
1667
|
+
'#chatLLM .LLM_suggestions button:not(#more_suggestions)'
|
|
1670
1668
|
);
|
|
1671
1669
|
for (let i = 0; i < suggestions.length; i++) {
|
|
1672
1670
|
constants.events.push([
|
|
1673
1671
|
suggestions[i],
|
|
1674
|
-
|
|
1672
|
+
'click',
|
|
1675
1673
|
function (e) {
|
|
1676
1674
|
let text = e.target.innerHTML;
|
|
1677
|
-
chatLLM.DisplayChatMessage(
|
|
1675
|
+
chatLLM.DisplayChatMessage('User', text);
|
|
1678
1676
|
chatLLM.Submit(text);
|
|
1679
1677
|
},
|
|
1680
1678
|
]);
|
|
@@ -1682,24 +1680,24 @@ class ChatLLM {
|
|
|
1682
1680
|
|
|
1683
1681
|
// Delete OpenAI and Gemini keys
|
|
1684
1682
|
constants.events.push([
|
|
1685
|
-
document.getElementById(
|
|
1686
|
-
|
|
1683
|
+
document.getElementById('delete_openai_key'),
|
|
1684
|
+
'click',
|
|
1687
1685
|
function (e) {
|
|
1688
|
-
document.getElementById(
|
|
1686
|
+
document.getElementById('openai_auth_key').value = '';
|
|
1689
1687
|
},
|
|
1690
1688
|
]);
|
|
1691
1689
|
constants.events.push([
|
|
1692
|
-
document.getElementById(
|
|
1693
|
-
|
|
1690
|
+
document.getElementById('delete_gemini_key'),
|
|
1691
|
+
'click',
|
|
1694
1692
|
function (e) {
|
|
1695
|
-
document.getElementById(
|
|
1693
|
+
document.getElementById('gemini_auth_key').value = '';
|
|
1696
1694
|
},
|
|
1697
1695
|
]);
|
|
1698
1696
|
|
|
1699
1697
|
// Reset chatLLM
|
|
1700
1698
|
constants.events.push([
|
|
1701
|
-
document.getElementById(
|
|
1702
|
-
|
|
1699
|
+
document.getElementById('reset_chatLLM'),
|
|
1700
|
+
'click',
|
|
1703
1701
|
function (e) {
|
|
1704
1702
|
chatLLM.ResetLLM();
|
|
1705
1703
|
},
|
|
@@ -1707,15 +1705,15 @@ class ChatLLM {
|
|
|
1707
1705
|
|
|
1708
1706
|
// copy to clipboard
|
|
1709
1707
|
constants.events.push([
|
|
1710
|
-
document.getElementById(
|
|
1711
|
-
|
|
1708
|
+
document.getElementById('chatLLM'),
|
|
1709
|
+
'click',
|
|
1712
1710
|
function (e) {
|
|
1713
1711
|
chatLLM.CopyChatHistory(e);
|
|
1714
1712
|
},
|
|
1715
1713
|
]);
|
|
1716
1714
|
constants.events.push([
|
|
1717
|
-
document.getElementById(
|
|
1718
|
-
|
|
1715
|
+
document.getElementById('chatLLM'),
|
|
1716
|
+
'keyup',
|
|
1719
1717
|
function (e) {
|
|
1720
1718
|
chatLLM.CopyChatHistory(e);
|
|
1721
1719
|
},
|
|
@@ -1733,96 +1731,96 @@ class ChatLLM {
|
|
|
1733
1731
|
* @param {Event|undefined} e - The event that triggered the copy action. If undefined, the entire chat history is copied.
|
|
1734
1732
|
*/
|
|
1735
1733
|
CopyChatHistory(e) {
|
|
1736
|
-
let text =
|
|
1737
|
-
if (typeof e ==
|
|
1734
|
+
let text = '';
|
|
1735
|
+
if (typeof e == 'undefined') {
|
|
1738
1736
|
// check for passthrough
|
|
1739
1737
|
// get html of the full chat history
|
|
1740
|
-
text = document.getElementById(
|
|
1741
|
-
} else if (e.type ==
|
|
1738
|
+
text = document.getElementById('chatLLM_chat_history').innerHTML;
|
|
1739
|
+
} else if (e.type == 'click') {
|
|
1742
1740
|
// check for buttons
|
|
1743
|
-
if (e.target.id ==
|
|
1741
|
+
if (e.target.id == 'chatLLM_copy_all') {
|
|
1744
1742
|
// get html of the full chat history
|
|
1745
|
-
text = document.getElementById(
|
|
1746
|
-
} else if (e.target.classList.contains(
|
|
1743
|
+
text = document.getElementById('chatLLM_chat_history').innerHTML;
|
|
1744
|
+
} else if (e.target.classList.contains('chatLLM_message_copy_button')) {
|
|
1747
1745
|
// get the text of the element before the button
|
|
1748
|
-
text = e.target.closest(
|
|
1746
|
+
text = e.target.closest('p').previousElementSibling.innerHTML;
|
|
1749
1747
|
}
|
|
1750
|
-
} else if (e.type ==
|
|
1748
|
+
} else if (e.type == 'keyup') {
|
|
1751
1749
|
// check for alt shift c or ctrl shift c
|
|
1752
|
-
if (e.key ==
|
|
1750
|
+
if (e.key == 'C' && (e.ctrlKey || e.metaKey || e.altKey) && e.shiftKey) {
|
|
1753
1751
|
e.preventDefault();
|
|
1754
1752
|
// get the last message
|
|
1755
1753
|
let elem = document.querySelector(
|
|
1756
|
-
|
|
1754
|
+
'#chatLLM_chat_history > .chatLLM_message_other:last-of-type'
|
|
1757
1755
|
);
|
|
1758
1756
|
if (elem) {
|
|
1759
1757
|
text = elem.innerHTML;
|
|
1760
1758
|
}
|
|
1761
1759
|
} else if (
|
|
1762
|
-
e.key ==
|
|
1760
|
+
e.key == 'A' &&
|
|
1763
1761
|
(e.ctrlKey || e.metaKey || e.altKey) &&
|
|
1764
1762
|
e.shiftKey
|
|
1765
1763
|
) {
|
|
1766
1764
|
e.preventDefault();
|
|
1767
1765
|
// get html of the full chat history
|
|
1768
|
-
text = document.getElementById(
|
|
1766
|
+
text = document.getElementById('chatLLM_chat_history').innerHTML;
|
|
1769
1767
|
}
|
|
1770
1768
|
}
|
|
1771
1769
|
|
|
1772
|
-
if (text ==
|
|
1770
|
+
if (text == '') {
|
|
1773
1771
|
return;
|
|
1774
1772
|
} else {
|
|
1775
1773
|
// clear the html, removing buttons etc
|
|
1776
|
-
let cleanElems = document.createElement(
|
|
1774
|
+
let cleanElems = document.createElement('div');
|
|
1777
1775
|
cleanElems.innerHTML = text;
|
|
1778
|
-
let removeThese = cleanElems.querySelectorAll(
|
|
1776
|
+
let removeThese = cleanElems.querySelectorAll('.chatLLM_message_copy');
|
|
1779
1777
|
removeThese.forEach((elem) => elem.remove());
|
|
1780
1778
|
|
|
1781
1779
|
// convert from html to markdown
|
|
1782
1780
|
let markdown = this.htmlToMarkdown(cleanElems);
|
|
1783
1781
|
// this messes up a bit with spacing, so kill more than 2 newlines in a row
|
|
1784
|
-
markdown = markdown.replace(/\n{3,}/g,
|
|
1782
|
+
markdown = markdown.replace(/\n{3,}/g, '\n\n');
|
|
1785
1783
|
|
|
1786
1784
|
try {
|
|
1787
1785
|
navigator.clipboard.writeText(markdown); // note: this fails if you're on the inspector. That's fine as it'll never happen to real users
|
|
1788
1786
|
} catch (err) {
|
|
1789
|
-
console.error(
|
|
1787
|
+
console.error('Failed to copy: ', err);
|
|
1790
1788
|
}
|
|
1791
1789
|
return markdown;
|
|
1792
1790
|
}
|
|
1793
1791
|
}
|
|
1794
1792
|
|
|
1795
1793
|
htmlToMarkdown(element) {
|
|
1796
|
-
let markdown =
|
|
1794
|
+
let markdown = '';
|
|
1797
1795
|
|
|
1798
1796
|
const convertElementToMarkdown = (element) => {
|
|
1799
1797
|
switch (element.tagName) {
|
|
1800
|
-
case
|
|
1798
|
+
case 'H1':
|
|
1801
1799
|
return `# ${element.textContent}`;
|
|
1802
|
-
case
|
|
1800
|
+
case 'H2':
|
|
1803
1801
|
return `## ${element.textContent}`;
|
|
1804
|
-
case
|
|
1802
|
+
case 'H3':
|
|
1805
1803
|
return `### ${element.textContent}`;
|
|
1806
|
-
case
|
|
1804
|
+
case 'H4':
|
|
1807
1805
|
return `#### ${element.textContent}`;
|
|
1808
|
-
case
|
|
1806
|
+
case 'H5':
|
|
1809
1807
|
return `##### ${element.textContent}`;
|
|
1810
|
-
case
|
|
1808
|
+
case 'H6':
|
|
1811
1809
|
return `###### ${element.textContent}`;
|
|
1812
|
-
case
|
|
1810
|
+
case 'P':
|
|
1813
1811
|
return element.textContent;
|
|
1814
|
-
case
|
|
1812
|
+
case 'DIV':
|
|
1815
1813
|
// For divs, process each child and add newlines as needed
|
|
1816
1814
|
return (
|
|
1817
1815
|
Array.from(element.childNodes)
|
|
1818
1816
|
.map((child) => convertElementToMarkdown(child))
|
|
1819
|
-
.join(
|
|
1817
|
+
.join('\n') + '\n\n'
|
|
1820
1818
|
);
|
|
1821
1819
|
default:
|
|
1822
1820
|
// For any other element, process its children recursively
|
|
1823
1821
|
return Array.from(element.childNodes)
|
|
1824
1822
|
.map((child) => convertElementToMarkdown(child))
|
|
1825
|
-
.join(
|
|
1823
|
+
.join('');
|
|
1826
1824
|
}
|
|
1827
1825
|
};
|
|
1828
1826
|
|
|
@@ -1830,7 +1828,7 @@ class ChatLLM {
|
|
|
1830
1828
|
markdown += convertElementToMarkdown(element);
|
|
1831
1829
|
} else if (
|
|
1832
1830
|
element.nodeType === Node.TEXT_NODE &&
|
|
1833
|
-
element.textContent.trim() !==
|
|
1831
|
+
element.textContent.trim() !== ''
|
|
1834
1832
|
) {
|
|
1835
1833
|
markdown += element.textContent.trim();
|
|
1836
1834
|
}
|
|
@@ -1855,14 +1853,14 @@ class ChatLLM {
|
|
|
1855
1853
|
|
|
1856
1854
|
// if this is the user's first message (or we're gemini, in which case we need to send every time), prepend prompt with user position
|
|
1857
1855
|
if (
|
|
1858
|
-
(this.firstOpen || constants.LLMModel ==
|
|
1856
|
+
(this.firstOpen || constants.LLMModel == 'gemini') &&
|
|
1859
1857
|
!firsttime &&
|
|
1860
1858
|
constants.verboseText.length > 0
|
|
1861
1859
|
) {
|
|
1862
1860
|
text =
|
|
1863
1861
|
"Here is the current position in the chart; no response necessarily needed, use this info only if it's relevant to future questions: " +
|
|
1864
1862
|
constants.verboseText +
|
|
1865
|
-
|
|
1863
|
+
'. My question is: ' +
|
|
1866
1864
|
text;
|
|
1867
1865
|
|
|
1868
1866
|
this.firstOpen = false;
|
|
@@ -1873,15 +1871,15 @@ class ChatLLM {
|
|
|
1873
1871
|
this.WaitingSound(true);
|
|
1874
1872
|
}
|
|
1875
1873
|
|
|
1876
|
-
if (constants.LLMOpenAiMulti || constants.LLMModel ==
|
|
1874
|
+
if (constants.LLMOpenAiMulti || constants.LLMModel == 'openai') {
|
|
1877
1875
|
if (firsttime) {
|
|
1878
|
-
img = await this.ConvertSVGtoJPG(singleMaidr.id,
|
|
1876
|
+
img = await this.ConvertSVGtoJPG(singleMaidr.id, 'openai');
|
|
1879
1877
|
}
|
|
1880
1878
|
chatLLM.OpenAIPrompt(text, img);
|
|
1881
1879
|
}
|
|
1882
|
-
if (constants.LLMGeminiMulti || constants.LLMModel ==
|
|
1880
|
+
if (constants.LLMGeminiMulti || constants.LLMModel == 'gemini') {
|
|
1883
1881
|
if (firsttime) {
|
|
1884
|
-
img = await this.ConvertSVGtoJPG(singleMaidr.id,
|
|
1882
|
+
img = await this.ConvertSVGtoJPG(singleMaidr.id, 'gemini');
|
|
1885
1883
|
}
|
|
1886
1884
|
chatLLM.GeminiPrompt(text, img);
|
|
1887
1885
|
}
|
|
@@ -1942,7 +1940,7 @@ class ChatLLM {
|
|
|
1942
1940
|
}, 30000);
|
|
1943
1941
|
|
|
1944
1942
|
// set queue for multi
|
|
1945
|
-
if (constants.LLMModel !=
|
|
1943
|
+
if (constants.LLMModel != 'multi') {
|
|
1946
1944
|
constants.waitingQueue = 1;
|
|
1947
1945
|
} else {
|
|
1948
1946
|
constants.waitingQueue = 0;
|
|
@@ -1974,7 +1972,7 @@ class ChatLLM {
|
|
|
1974
1972
|
// get name from resource
|
|
1975
1973
|
let LLMName = resources.GetString(constants.LLMModel);
|
|
1976
1974
|
this.firstTime = false;
|
|
1977
|
-
this.DisplayChatMessage(LLMName, resources.GetString(
|
|
1975
|
+
this.DisplayChatMessage(LLMName, resources.GetString('processing'), true);
|
|
1978
1976
|
let defaultPrompt = this.GetDefaultPrompt();
|
|
1979
1977
|
this.Submit(defaultPrompt, true);
|
|
1980
1978
|
}
|
|
@@ -1986,35 +1984,35 @@ class ChatLLM {
|
|
|
1986
1984
|
*/
|
|
1987
1985
|
ProcessLLMResponse(data, model) {
|
|
1988
1986
|
chatLLM.WaitingSound(false);
|
|
1989
|
-
console.log(
|
|
1990
|
-
let text =
|
|
1987
|
+
console.log('LLM response: ', data);
|
|
1988
|
+
let text = '';
|
|
1991
1989
|
let LLMName = resources.GetString(model);
|
|
1992
1990
|
|
|
1993
|
-
if (model ==
|
|
1991
|
+
if (model == 'openai') {
|
|
1994
1992
|
text = data.choices[0].message.content;
|
|
1995
1993
|
let i = this.requestJson.messages.length;
|
|
1996
1994
|
this.requestJson.messages[i] = {};
|
|
1997
|
-
this.requestJson.messages[i].role =
|
|
1995
|
+
this.requestJson.messages[i].role = 'assistant';
|
|
1998
1996
|
this.requestJson.messages[i].content = text;
|
|
1999
1997
|
|
|
2000
1998
|
if (data.error) {
|
|
2001
|
-
chatLLM.DisplayChatMessage(LLMName,
|
|
1999
|
+
chatLLM.DisplayChatMessage(LLMName, 'Error processing request.', true);
|
|
2002
2000
|
chatLLM.WaitingSound(false);
|
|
2003
2001
|
} else {
|
|
2004
2002
|
chatLLM.DisplayChatMessage(LLMName, text);
|
|
2005
2003
|
}
|
|
2006
|
-
} else if (model ==
|
|
2004
|
+
} else if (model == 'gemini') {
|
|
2007
2005
|
if (data.text()) {
|
|
2008
2006
|
text = data.text();
|
|
2009
2007
|
chatLLM.DisplayChatMessage(LLMName, text);
|
|
2010
2008
|
} else {
|
|
2011
2009
|
if (!data.error) {
|
|
2012
|
-
data.error =
|
|
2010
|
+
data.error = 'Error processing request.';
|
|
2013
2011
|
chatLLM.WaitingSound(false);
|
|
2014
2012
|
}
|
|
2015
2013
|
}
|
|
2016
2014
|
if (data.error) {
|
|
2017
|
-
chatLLM.DisplayChatMessage(LLMName,
|
|
2015
|
+
chatLLM.DisplayChatMessage(LLMName, 'Error processing request.', true);
|
|
2018
2016
|
chatLLM.WaitingSound(false);
|
|
2019
2017
|
} else {
|
|
2020
2018
|
// todo: display actual response
|
|
@@ -2024,7 +2022,7 @@ class ChatLLM {
|
|
|
2024
2022
|
// if we're tracking, log the data
|
|
2025
2023
|
if (constants.isTracking) {
|
|
2026
2024
|
let chatHist = chatLLM.CopyChatHistory();
|
|
2027
|
-
tracker.SetData(
|
|
2025
|
+
tracker.SetData('ChatHistory', chatHist);
|
|
2028
2026
|
}
|
|
2029
2027
|
}
|
|
2030
2028
|
|
|
@@ -2038,11 +2036,11 @@ class ChatLLM {
|
|
|
2038
2036
|
if (this.requestJson.messages.length > 2) {
|
|
2039
2037
|
// subsequent responses
|
|
2040
2038
|
responseText = {
|
|
2041
|
-
id:
|
|
2042
|
-
object:
|
|
2039
|
+
id: 'chatcmpl-8Y44iRCRrohYbAqm8rfBbJqTUADC7',
|
|
2040
|
+
object: 'chat.completion',
|
|
2043
2041
|
created: 1703129508,
|
|
2044
2042
|
//model: 'gpt-4-1106-vision-preview',
|
|
2045
|
-
model:
|
|
2043
|
+
model: 'gpt4-o',
|
|
2046
2044
|
usage: {
|
|
2047
2045
|
prompt_tokens: 451,
|
|
2048
2046
|
completion_tokens: 16,
|
|
@@ -2051,10 +2049,10 @@ class ChatLLM {
|
|
|
2051
2049
|
choices: [
|
|
2052
2050
|
{
|
|
2053
2051
|
message: {
|
|
2054
|
-
role:
|
|
2055
|
-
content:
|
|
2052
|
+
role: 'assistant',
|
|
2053
|
+
content: 'A fake response from the LLM. Nice.',
|
|
2056
2054
|
},
|
|
2057
|
-
finish_reason:
|
|
2055
|
+
finish_reason: 'length',
|
|
2058
2056
|
index: 0,
|
|
2059
2057
|
},
|
|
2060
2058
|
],
|
|
@@ -2062,10 +2060,10 @@ class ChatLLM {
|
|
|
2062
2060
|
} else {
|
|
2063
2061
|
// first response
|
|
2064
2062
|
responseText = {
|
|
2065
|
-
id:
|
|
2066
|
-
object:
|
|
2063
|
+
id: 'chatcmpl-8Y44iRCRrohYbAqm8rfBbJqTUADC7',
|
|
2064
|
+
object: 'chat.completion',
|
|
2067
2065
|
created: 1703129508,
|
|
2068
|
-
model:
|
|
2066
|
+
model: 'gpt-4-1106-vision-preview',
|
|
2069
2067
|
usage: {
|
|
2070
2068
|
prompt_tokens: 451,
|
|
2071
2069
|
completion_tokens: 16,
|
|
@@ -2074,11 +2072,11 @@ class ChatLLM {
|
|
|
2074
2072
|
choices: [
|
|
2075
2073
|
{
|
|
2076
2074
|
message: {
|
|
2077
|
-
role:
|
|
2075
|
+
role: 'assistant',
|
|
2078
2076
|
content:
|
|
2079
|
-
|
|
2077
|
+
'The chart you\'re referring to is a bar graph titled "The Number of Diamonds',
|
|
2080
2078
|
},
|
|
2081
|
-
finish_reason:
|
|
2079
|
+
finish_reason: 'length',
|
|
2082
2080
|
index: 0,
|
|
2083
2081
|
},
|
|
2084
2082
|
],
|
|
@@ -2097,49 +2095,49 @@ class ChatLLM {
|
|
|
2097
2095
|
*/
|
|
2098
2096
|
OpenAIPrompt(text, img = null) {
|
|
2099
2097
|
// request init
|
|
2100
|
-
let url =
|
|
2098
|
+
let url = 'https://api.openai.com/v1/chat/completions';
|
|
2101
2099
|
let auth = constants.openAIAuthKey;
|
|
2102
2100
|
let requestJson = chatLLM.OpenAIJson(text, img);
|
|
2103
|
-
console.log(
|
|
2101
|
+
console.log('LLM request: ', requestJson);
|
|
2104
2102
|
|
|
2105
2103
|
fetch(url, {
|
|
2106
|
-
method:
|
|
2104
|
+
method: 'POST',
|
|
2107
2105
|
headers: {
|
|
2108
|
-
|
|
2109
|
-
Authorization:
|
|
2106
|
+
'Content-Type': 'application/json',
|
|
2107
|
+
Authorization: 'Bearer ' + auth,
|
|
2110
2108
|
},
|
|
2111
2109
|
body: JSON.stringify(requestJson),
|
|
2112
2110
|
})
|
|
2113
2111
|
.then((response) => response.json())
|
|
2114
2112
|
.then((data) => {
|
|
2115
|
-
chatLLM.ProcessLLMResponse(data,
|
|
2113
|
+
chatLLM.ProcessLLMResponse(data, 'openai');
|
|
2116
2114
|
})
|
|
2117
2115
|
.catch((error) => {
|
|
2118
2116
|
chatLLM.WaitingSound(false);
|
|
2119
|
-
console.error(
|
|
2120
|
-
chatLLM.DisplayChatMessage(
|
|
2117
|
+
console.error('Error:', error);
|
|
2118
|
+
chatLLM.DisplayChatMessage('OpenAI', 'Error processing request.', true);
|
|
2121
2119
|
// also todo: handle errors somehow
|
|
2122
2120
|
});
|
|
2123
2121
|
}
|
|
2124
2122
|
OpenAIJson(text, img = null) {
|
|
2125
2123
|
let sysMessage = constants.LLMSystemMessage;
|
|
2126
2124
|
let backupMessage =
|
|
2127
|
-
|
|
2125
|
+
'Describe ' + singleMaidr.type + ' charts to a blind person';
|
|
2128
2126
|
// headers and sys message
|
|
2129
2127
|
if (!this.requestJson) {
|
|
2130
2128
|
this.requestJson = {};
|
|
2131
2129
|
//this.requestJson.model = 'gpt-4-vision-preview';
|
|
2132
|
-
this.requestJson.model =
|
|
2130
|
+
this.requestJson.model = 'gpt-4o-2024-08-06';
|
|
2133
2131
|
this.requestJson.max_tokens = constants.LLMmaxResponseTokens; // note: if this is too short (tested with less than 200), the response gets cut off
|
|
2134
2132
|
|
|
2135
2133
|
// sys message
|
|
2136
2134
|
this.requestJson.messages = [];
|
|
2137
2135
|
this.requestJson.messages[0] = {};
|
|
2138
|
-
this.requestJson.messages[0].role =
|
|
2136
|
+
this.requestJson.messages[0].role = 'system';
|
|
2139
2137
|
this.requestJson.messages[0].content = sysMessage;
|
|
2140
2138
|
if (constants.LLMPreferences) {
|
|
2141
2139
|
this.requestJson.messages[1] = {};
|
|
2142
|
-
this.requestJson.messages[1].role =
|
|
2140
|
+
this.requestJson.messages[1].role = 'system';
|
|
2143
2141
|
this.requestJson.messages[1].content = constants.LLMPreferences;
|
|
2144
2142
|
}
|
|
2145
2143
|
}
|
|
@@ -2148,16 +2146,16 @@ class ChatLLM {
|
|
|
2148
2146
|
// if we have an image (first time only), send the image and the text, otherwise just the text
|
|
2149
2147
|
let i = this.requestJson.messages.length;
|
|
2150
2148
|
this.requestJson.messages[i] = {};
|
|
2151
|
-
this.requestJson.messages[i].role =
|
|
2149
|
+
this.requestJson.messages[i].role = 'user';
|
|
2152
2150
|
if (img) {
|
|
2153
2151
|
// first message, include the img
|
|
2154
2152
|
this.requestJson.messages[i].content = [
|
|
2155
2153
|
{
|
|
2156
|
-
type:
|
|
2154
|
+
type: 'text',
|
|
2157
2155
|
text: text,
|
|
2158
2156
|
},
|
|
2159
2157
|
{
|
|
2160
|
-
type:
|
|
2158
|
+
type: 'image_url',
|
|
2161
2159
|
image_url: { url: img },
|
|
2162
2160
|
},
|
|
2163
2161
|
];
|
|
@@ -2182,12 +2180,12 @@ class ChatLLM {
|
|
|
2182
2180
|
|
|
2183
2181
|
// Import the module
|
|
2184
2182
|
const { GoogleGenerativeAI } = await import(
|
|
2185
|
-
|
|
2183
|
+
'https://esm.run/@google/generative-ai'
|
|
2186
2184
|
);
|
|
2187
2185
|
const API_KEY = constants.geminiAuthKey;
|
|
2188
2186
|
const genAI = new GoogleGenerativeAI(API_KEY);
|
|
2189
2187
|
const model = genAI.getGenerativeModel({
|
|
2190
|
-
model:
|
|
2188
|
+
model: 'gemini-1.5-pro-latest',
|
|
2191
2189
|
}); // old model was 'gemini-pro-vision'
|
|
2192
2190
|
|
|
2193
2191
|
// Create the prompt
|
|
@@ -2195,25 +2193,25 @@ class ChatLLM {
|
|
|
2195
2193
|
if (constants.LLMPreferences) {
|
|
2196
2194
|
prompt += constants.LLMPreferences;
|
|
2197
2195
|
}
|
|
2198
|
-
prompt +=
|
|
2196
|
+
prompt += '\n\n' + text; // Use the text parameter as the prompt
|
|
2199
2197
|
const image = {
|
|
2200
2198
|
inlineData: {
|
|
2201
2199
|
data: imgBase64, // Use the base64 image string
|
|
2202
|
-
mimeType:
|
|
2200
|
+
mimeType: 'image/png', // Or the appropriate mime type of your image
|
|
2203
2201
|
},
|
|
2204
2202
|
};
|
|
2205
2203
|
|
|
2206
2204
|
// Generate the content
|
|
2207
|
-
console.log(
|
|
2205
|
+
console.log('LLM request: ', prompt, image);
|
|
2208
2206
|
const result = await model.generateContent([prompt, image]);
|
|
2209
2207
|
console.log(result.response.text());
|
|
2210
2208
|
|
|
2211
2209
|
// Process the response
|
|
2212
|
-
chatLLM.ProcessLLMResponse(result.response,
|
|
2210
|
+
chatLLM.ProcessLLMResponse(result.response, 'gemini');
|
|
2213
2211
|
} catch (error) {
|
|
2214
2212
|
chatLLM.WaitingSound(false);
|
|
2215
|
-
chatLLM.DisplayChatMessage(
|
|
2216
|
-
console.error(
|
|
2213
|
+
chatLLM.DisplayChatMessage('Gemini', 'Error processing request.', true);
|
|
2214
|
+
console.error('Error in GeminiPrompt:', error);
|
|
2217
2215
|
throw error; // Rethrow the error for further handling if necessary
|
|
2218
2216
|
}
|
|
2219
2217
|
}
|
|
@@ -2225,11 +2223,11 @@ class ChatLLM {
|
|
|
2225
2223
|
* @memberof module:constants
|
|
2226
2224
|
* @returns {void}
|
|
2227
2225
|
*/
|
|
2228
|
-
DisplayChatMessage(user =
|
|
2229
|
-
let hLevel =
|
|
2230
|
-
if (!isSystem && constants.LLMModel ==
|
|
2226
|
+
DisplayChatMessage(user = 'User', text = '', isSystem = false) {
|
|
2227
|
+
let hLevel = 'h3';
|
|
2228
|
+
if (!isSystem && constants.LLMModel == 'multi' && user != 'User') {
|
|
2231
2229
|
if (this.firstMulti) {
|
|
2232
|
-
let multiAIName = resources.GetString(
|
|
2230
|
+
let multiAIName = resources.GetString('multi');
|
|
2233
2231
|
let titleHtml = `
|
|
2234
2232
|
<div class="chatLLM_message chatLLM_message_other">
|
|
2235
2233
|
<h3 class="chatLLM_message_user">${multiAIName} Responses</h3>
|
|
@@ -2238,20 +2236,20 @@ class ChatLLM {
|
|
|
2238
2236
|
this.RenderChatMessage(titleHtml);
|
|
2239
2237
|
this.firstMulti = false;
|
|
2240
2238
|
}
|
|
2241
|
-
hLevel =
|
|
2239
|
+
hLevel = 'h4';
|
|
2242
2240
|
}
|
|
2243
2241
|
let html = `
|
|
2244
2242
|
<div class="chatLLM_message ${
|
|
2245
|
-
user ==
|
|
2243
|
+
user == 'User' ? 'chatLLM_message_self' : 'chatLLM_message_other'
|
|
2246
2244
|
}">`;
|
|
2247
|
-
if (text != resources.GetString(
|
|
2245
|
+
if (text != resources.GetString('processing')) {
|
|
2248
2246
|
html += `<${hLevel} class="chatLLM_message_user">${user}</${hLevel}>`;
|
|
2249
2247
|
}
|
|
2250
2248
|
html += `<p class="chatLLM_message_text">${text}</p>
|
|
2251
2249
|
</div>
|
|
2252
2250
|
`;
|
|
2253
2251
|
// add a copy button to actual messages
|
|
2254
|
-
if (user !=
|
|
2252
|
+
if (user != 'User' && text != resources.GetString('processing')) {
|
|
2255
2253
|
html += `
|
|
2256
2254
|
<p class="chatLLM_message_copy"><button class="chatLLM_message_copy_button">Copy</button></p>
|
|
2257
2255
|
`;
|
|
@@ -2261,13 +2259,13 @@ class ChatLLM {
|
|
|
2261
2259
|
}
|
|
2262
2260
|
RenderChatMessage(html) {
|
|
2263
2261
|
document
|
|
2264
|
-
.getElementById(
|
|
2265
|
-
.insertAdjacentHTML(
|
|
2266
|
-
document.getElementById(
|
|
2262
|
+
.getElementById('chatLLM_chat_history')
|
|
2263
|
+
.insertAdjacentHTML('beforeend', html);
|
|
2264
|
+
document.getElementById('chatLLM_input').value = '';
|
|
2267
2265
|
|
|
2268
2266
|
// scroll to bottom
|
|
2269
|
-
document.getElementById(
|
|
2270
|
-
document.getElementById(
|
|
2267
|
+
document.getElementById('chatLLM_chat_history').scrollTop =
|
|
2268
|
+
document.getElementById('chatLLM_chat_history').scrollHeight;
|
|
2271
2269
|
}
|
|
2272
2270
|
|
|
2273
2271
|
/**
|
|
@@ -2275,7 +2273,7 @@ class ChatLLM {
|
|
|
2275
2273
|
*/
|
|
2276
2274
|
ResetLLM() {
|
|
2277
2275
|
// clear the main chat history
|
|
2278
|
-
document.getElementById(
|
|
2276
|
+
document.getElementById('chatLLM_chat_history').innerHTML = '';
|
|
2279
2277
|
|
|
2280
2278
|
// reset the data
|
|
2281
2279
|
this.requestJson = null;
|
|
@@ -2296,11 +2294,11 @@ class ChatLLM {
|
|
|
2296
2294
|
*/
|
|
2297
2295
|
Destroy() {
|
|
2298
2296
|
// chatLLM element destruction
|
|
2299
|
-
let chatLLM = document.getElementById(
|
|
2297
|
+
let chatLLM = document.getElementById('chatLLM');
|
|
2300
2298
|
if (chatLLM) {
|
|
2301
2299
|
chatLLM.remove();
|
|
2302
2300
|
}
|
|
2303
|
-
let backdrop = document.getElementById(
|
|
2301
|
+
let backdrop = document.getElementById('chatLLM_modal_backdrop');
|
|
2304
2302
|
if (backdrop) {
|
|
2305
2303
|
backdrop.remove();
|
|
2306
2304
|
}
|
|
@@ -2311,8 +2309,8 @@ class ChatLLM {
|
|
|
2311
2309
|
* @param {boolean} [onoff=false] - Whether to turn the chatLLM on or off. Defaults to false (close).
|
|
2312
2310
|
*/
|
|
2313
2311
|
Toggle(onoff) {
|
|
2314
|
-
if (typeof onoff ==
|
|
2315
|
-
if (document.getElementById(
|
|
2312
|
+
if (typeof onoff == 'undefined') {
|
|
2313
|
+
if (document.getElementById('chatLLM').classList.contains('hidden')) {
|
|
2316
2314
|
onoff = true;
|
|
2317
2315
|
} else {
|
|
2318
2316
|
onoff = false;
|
|
@@ -2323,19 +2321,19 @@ class ChatLLM {
|
|
|
2323
2321
|
// open
|
|
2324
2322
|
this.whereWasMyFocus = document.activeElement;
|
|
2325
2323
|
constants.tabMovement = 0;
|
|
2326
|
-
document.getElementById(
|
|
2324
|
+
document.getElementById('chatLLM').classList.remove('hidden');
|
|
2327
2325
|
document
|
|
2328
|
-
.getElementById(
|
|
2329
|
-
.classList.remove(
|
|
2330
|
-
document.querySelector(
|
|
2326
|
+
.getElementById('chatLLM_modal_backdrop')
|
|
2327
|
+
.classList.remove('hidden');
|
|
2328
|
+
document.querySelector('#chatLLM .close').focus();
|
|
2331
2329
|
|
|
2332
2330
|
if (this.firstTime) {
|
|
2333
2331
|
this.InitChatMessage();
|
|
2334
2332
|
}
|
|
2335
2333
|
} else {
|
|
2336
2334
|
// close
|
|
2337
|
-
document.getElementById(
|
|
2338
|
-
document.getElementById(
|
|
2335
|
+
document.getElementById('chatLLM').classList.add('hidden');
|
|
2336
|
+
document.getElementById('chatLLM_modal_backdrop').classList.add('hidden');
|
|
2339
2337
|
this.whereWasMyFocus.focus();
|
|
2340
2338
|
this.whereWasMyFocus = null;
|
|
2341
2339
|
this.firstOpen = true;
|
|
@@ -2349,11 +2347,11 @@ class ChatLLM {
|
|
|
2349
2347
|
async ConvertSVGtoJPG(id, model) {
|
|
2350
2348
|
let svgElement = document.getElementById(id);
|
|
2351
2349
|
return new Promise((resolve, reject) => {
|
|
2352
|
-
var canvas = document.createElement(
|
|
2353
|
-
var ctx = canvas.getContext(
|
|
2350
|
+
var canvas = document.createElement('canvas');
|
|
2351
|
+
var ctx = canvas.getContext('2d');
|
|
2354
2352
|
|
|
2355
2353
|
var svgData = new XMLSerializer().serializeToString(svgElement);
|
|
2356
|
-
if (!svgData.startsWith(
|
|
2354
|
+
if (!svgData.startsWith('<svg xmlns')) {
|
|
2357
2355
|
svgData = `<svg xmlns="http://www.w3.org/2000/svg" ${svgData.slice(4)}`;
|
|
2358
2356
|
}
|
|
2359
2357
|
|
|
@@ -2365,11 +2363,11 @@ class ChatLLM {
|
|
|
2365
2363
|
var img = new Image();
|
|
2366
2364
|
img.onload = function () {
|
|
2367
2365
|
ctx.drawImage(img, 0, 0, svgSize.width, svgSize.height);
|
|
2368
|
-
var jpegData = canvas.toDataURL(
|
|
2369
|
-
if (model ==
|
|
2366
|
+
var jpegData = canvas.toDataURL('image/jpeg', 0.9); // 0.9 is the quality parameter
|
|
2367
|
+
if (model == 'openai') {
|
|
2370
2368
|
resolve(jpegData);
|
|
2371
|
-
} else if (model ==
|
|
2372
|
-
let base64Data = jpegData.split(
|
|
2369
|
+
} else if (model == 'gemini') {
|
|
2370
|
+
let base64Data = jpegData.split(',')[1];
|
|
2373
2371
|
resolve(base64Data);
|
|
2374
2372
|
//resolve(jpegData);
|
|
2375
2373
|
}
|
|
@@ -2377,11 +2375,11 @@ class ChatLLM {
|
|
|
2377
2375
|
};
|
|
2378
2376
|
|
|
2379
2377
|
img.onerror = function () {
|
|
2380
|
-
reject(new Error(
|
|
2378
|
+
reject(new Error('Error loading SVG'));
|
|
2381
2379
|
};
|
|
2382
2380
|
|
|
2383
2381
|
var svgBlob = new Blob([svgData], {
|
|
2384
|
-
type:
|
|
2382
|
+
type: 'image/svg+xml;charset=utf-8',
|
|
2385
2383
|
});
|
|
2386
2384
|
var url = URL.createObjectURL(svgBlob);
|
|
2387
2385
|
img.src = url;
|
|
@@ -2394,25 +2392,25 @@ class ChatLLM {
|
|
|
2394
2392
|
* The prompt includes information about the blind person's skill level and the chart's image and raw data, if available.
|
|
2395
2393
|
*/
|
|
2396
2394
|
GetDefaultPrompt() {
|
|
2397
|
-
let text =
|
|
2395
|
+
let text = 'Describe this chart to a blind person';
|
|
2398
2396
|
if (constants.skillLevel) {
|
|
2399
|
-
if (constants.skillLevel ==
|
|
2397
|
+
if (constants.skillLevel == 'other' && constants.skillLevelOther) {
|
|
2400
2398
|
text +=
|
|
2401
|
-
|
|
2399
|
+
' who has a ' +
|
|
2402
2400
|
constants.skillLevelOther +
|
|
2403
|
-
|
|
2401
|
+
' understanding of statistical charts. ';
|
|
2404
2402
|
} else {
|
|
2405
2403
|
text +=
|
|
2406
|
-
|
|
2404
|
+
' who has a ' +
|
|
2407
2405
|
constants.skillLevel +
|
|
2408
|
-
|
|
2406
|
+
' understanding of statistical charts. ';
|
|
2409
2407
|
}
|
|
2410
2408
|
} else {
|
|
2411
|
-
text +=
|
|
2409
|
+
text += ' who has a basic understanding of statistical charts. ';
|
|
2412
2410
|
}
|
|
2413
|
-
text +=
|
|
2411
|
+
text += 'Here is a chart in image format';
|
|
2414
2412
|
if (singleMaidr) {
|
|
2415
|
-
text +=
|
|
2413
|
+
text += ' and raw data in json format: \n';
|
|
2416
2414
|
text += JSON.stringify(singleMaidr);
|
|
2417
2415
|
}
|
|
2418
2416
|
|
|
@@ -2467,26 +2465,26 @@ class Description {
|
|
|
2467
2465
|
|
|
2468
2466
|
`;
|
|
2469
2467
|
|
|
2470
|
-
document.querySelector(
|
|
2468
|
+
document.querySelector('body').insertAdjacentHTML('beforeend', html);
|
|
2471
2469
|
|
|
2472
2470
|
// close events
|
|
2473
2471
|
let allClose = document.querySelectorAll(
|
|
2474
|
-
|
|
2472
|
+
'#close_desc, #description .close'
|
|
2475
2473
|
);
|
|
2476
2474
|
for (let i = 0; i < allClose.length; i++) {
|
|
2477
2475
|
constants.events.push([
|
|
2478
2476
|
allClose[i],
|
|
2479
|
-
|
|
2477
|
+
'click',
|
|
2480
2478
|
function (e) {
|
|
2481
2479
|
description.Toggle(false);
|
|
2482
2480
|
},
|
|
2483
2481
|
]);
|
|
2484
2482
|
}
|
|
2485
2483
|
constants.events.push([
|
|
2486
|
-
document.getElementById(
|
|
2487
|
-
|
|
2484
|
+
document.getElementById('description'),
|
|
2485
|
+
'keyup',
|
|
2488
2486
|
function (e) {
|
|
2489
|
-
if (e.key ==
|
|
2487
|
+
if (e.key == 'Esc') {
|
|
2490
2488
|
// esc
|
|
2491
2489
|
description.Toggle(false);
|
|
2492
2490
|
}
|
|
@@ -2496,9 +2494,9 @@ class Description {
|
|
|
2496
2494
|
// open events
|
|
2497
2495
|
constants.events.push([
|
|
2498
2496
|
document,
|
|
2499
|
-
|
|
2497
|
+
'keyup',
|
|
2500
2498
|
function (e) {
|
|
2501
|
-
if (e.key ==
|
|
2499
|
+
if (e.key == 'd') {
|
|
2502
2500
|
description.Toggle(true);
|
|
2503
2501
|
}
|
|
2504
2502
|
},
|
|
@@ -2510,11 +2508,11 @@ class Description {
|
|
|
2510
2508
|
*/
|
|
2511
2509
|
Destroy() {
|
|
2512
2510
|
// description element destruction
|
|
2513
|
-
let description = document.getElementById(
|
|
2511
|
+
let description = document.getElementById('menu');
|
|
2514
2512
|
if (description) {
|
|
2515
2513
|
description.remove();
|
|
2516
2514
|
}
|
|
2517
|
-
let backdrop = document.getElementById(
|
|
2515
|
+
let backdrop = document.getElementById('desc_modal_backdrop');
|
|
2518
2516
|
if (backdrop) {
|
|
2519
2517
|
backdrop.remove();
|
|
2520
2518
|
}
|
|
@@ -2525,8 +2523,8 @@ class Description {
|
|
|
2525
2523
|
* @param {boolean} [onoff=false] - Whether to turn the description element on or off.
|
|
2526
2524
|
*/
|
|
2527
2525
|
Toggle(onoff = false) {
|
|
2528
|
-
if (typeof onoff ==
|
|
2529
|
-
if (document.getElementById(
|
|
2526
|
+
if (typeof onoff == 'undefined') {
|
|
2527
|
+
if (document.getElementById('description').classList.contains('hidden')) {
|
|
2530
2528
|
onoff = true;
|
|
2531
2529
|
} else {
|
|
2532
2530
|
onoff = false;
|
|
@@ -2537,13 +2535,13 @@ class Description {
|
|
|
2537
2535
|
this.whereWasMyFocus = document.activeElement;
|
|
2538
2536
|
constants.tabMovement = 0;
|
|
2539
2537
|
this.PopulateData();
|
|
2540
|
-
document.getElementById(
|
|
2541
|
-
document.getElementById(
|
|
2542
|
-
document.querySelector(
|
|
2538
|
+
document.getElementById('description').classList.remove('hidden');
|
|
2539
|
+
document.getElementById('desc_modal_backdrop').classList.remove('hidden');
|
|
2540
|
+
document.querySelector('#description .close').focus();
|
|
2543
2541
|
} else {
|
|
2544
2542
|
// close
|
|
2545
|
-
document.getElementById(
|
|
2546
|
-
document.getElementById(
|
|
2543
|
+
document.getElementById('description').classList.add('hidden');
|
|
2544
|
+
document.getElementById('desc_modal_backdrop').classList.add('hidden');
|
|
2547
2545
|
this.whereWasMyFocus.focus();
|
|
2548
2546
|
this.whereWasMyFocus = null;
|
|
2549
2547
|
}
|
|
@@ -2553,22 +2551,22 @@ class Description {
|
|
|
2553
2551
|
* Populates the data for the chart and table based on the chart type and plot data.
|
|
2554
2552
|
*/
|
|
2555
2553
|
PopulateData() {
|
|
2556
|
-
let descHtml =
|
|
2554
|
+
let descHtml = '';
|
|
2557
2555
|
|
|
2558
2556
|
// chart labels and descriptions
|
|
2559
|
-
let descType =
|
|
2560
|
-
if (constants.chartType ==
|
|
2561
|
-
descType =
|
|
2562
|
-
} else if (constants.chartType ==
|
|
2563
|
-
descType =
|
|
2564
|
-
} else if (constants.chartType ==
|
|
2565
|
-
descType =
|
|
2566
|
-
} else if (constants.chartType ==
|
|
2567
|
-
descType =
|
|
2568
|
-
} else if (constants.chartType ==
|
|
2569
|
-
descType =
|
|
2570
|
-
} else if (constants.chartType ==
|
|
2571
|
-
descType =
|
|
2557
|
+
let descType = '';
|
|
2558
|
+
if (constants.chartType == 'bar') {
|
|
2559
|
+
descType = 'Bar chart';
|
|
2560
|
+
} else if (constants.chartType == 'heat') {
|
|
2561
|
+
descType = 'Heatmap';
|
|
2562
|
+
} else if (constants.chartType == 'box') {
|
|
2563
|
+
descType = 'Box plot';
|
|
2564
|
+
} else if (constants.chartType == 'scatter') {
|
|
2565
|
+
descType = 'Scatter plot';
|
|
2566
|
+
} else if (constants.chartType == 'line') {
|
|
2567
|
+
descType = 'Line chart';
|
|
2568
|
+
} else if (constants.chartType == 'hist') {
|
|
2569
|
+
descType = 'Histogram';
|
|
2572
2570
|
}
|
|
2573
2571
|
|
|
2574
2572
|
if (descType) {
|
|
@@ -2585,7 +2583,7 @@ class Description {
|
|
|
2585
2583
|
}
|
|
2586
2584
|
|
|
2587
2585
|
// table of data, prep
|
|
2588
|
-
let descTableHtml =
|
|
2586
|
+
let descTableHtml = '';
|
|
2589
2587
|
let descLabelX = null;
|
|
2590
2588
|
let descLabelY = null;
|
|
2591
2589
|
let descTickX = null;
|
|
@@ -2595,7 +2593,7 @@ class Description {
|
|
|
2595
2593
|
let descNumColsWithLabels = 0;
|
|
2596
2594
|
let descNumRows = 0;
|
|
2597
2595
|
let descNumRowsWithLabels = 0;
|
|
2598
|
-
if (constants.chartType ==
|
|
2596
|
+
if (constants.chartType == 'bar') {
|
|
2599
2597
|
if (plot.plotLegend.x != null) {
|
|
2600
2598
|
descLabelX = plot.plotLegend.x;
|
|
2601
2599
|
descNumColsWithLabels += 1;
|
|
@@ -2620,43 +2618,43 @@ class Description {
|
|
|
2620
2618
|
|
|
2621
2619
|
// table of data, create
|
|
2622
2620
|
if (descData != null) {
|
|
2623
|
-
descTableHtml +=
|
|
2621
|
+
descTableHtml += '<table>';
|
|
2624
2622
|
|
|
2625
2623
|
// header rows
|
|
2626
2624
|
if (descLabelX != null || descTickX != null) {
|
|
2627
|
-
descTableHtml +=
|
|
2625
|
+
descTableHtml += '<thead>';
|
|
2628
2626
|
if (descLabelX != null) {
|
|
2629
|
-
descTableHtml +=
|
|
2627
|
+
descTableHtml += '<tr>';
|
|
2630
2628
|
if (descLabelY != null) {
|
|
2631
|
-
descTableHtml +=
|
|
2629
|
+
descTableHtml += '<td></td>';
|
|
2632
2630
|
}
|
|
2633
2631
|
if (descTickY != null) {
|
|
2634
|
-
descTableHtml +=
|
|
2632
|
+
descTableHtml += '<td></td>';
|
|
2635
2633
|
}
|
|
2636
2634
|
descTableHtml += `<th scope="col" colspan="${descNumCols}">${descLabelX}</th>`;
|
|
2637
|
-
descTableHtml +=
|
|
2635
|
+
descTableHtml += '</tr>';
|
|
2638
2636
|
}
|
|
2639
2637
|
if (descTickX != null) {
|
|
2640
|
-
descTableHtml +=
|
|
2638
|
+
descTableHtml += '<tr>';
|
|
2641
2639
|
if (descLabelY != null) {
|
|
2642
|
-
descTableHtml +=
|
|
2640
|
+
descTableHtml += '<td></td>';
|
|
2643
2641
|
}
|
|
2644
2642
|
if (descTickY != null) {
|
|
2645
|
-
descTableHtml +=
|
|
2643
|
+
descTableHtml += '<td></td>';
|
|
2646
2644
|
}
|
|
2647
2645
|
for (let i = 0; i < descNumCols; i++) {
|
|
2648
2646
|
descTableHtml += `<th scope="col">${descTickX[i]}</th>`;
|
|
2649
2647
|
}
|
|
2650
|
-
descTableHtml +=
|
|
2648
|
+
descTableHtml += '</tr>';
|
|
2651
2649
|
}
|
|
2652
|
-
descTableHtml +=
|
|
2650
|
+
descTableHtml += '</thead>';
|
|
2653
2651
|
}
|
|
2654
2652
|
|
|
2655
2653
|
// body rows
|
|
2656
2654
|
if (descNumRows > 0) {
|
|
2657
|
-
descTableHtml +=
|
|
2655
|
+
descTableHtml += '<tbody>';
|
|
2658
2656
|
for (let i = 0; i < descNumRows; i++) {
|
|
2659
|
-
descTableHtml +=
|
|
2657
|
+
descTableHtml += '<tr>';
|
|
2660
2658
|
if (descLabelY != null && i == 0) {
|
|
2661
2659
|
descTableHtml += `<th scope="row" rowspan="${descNumRows}">${descLabelY}</th>`;
|
|
2662
2660
|
}
|
|
@@ -2666,19 +2664,19 @@ class Description {
|
|
|
2666
2664
|
for (let j = 0; j < descNumCols; j++) {
|
|
2667
2665
|
descTableHtml += `<td>${descData[i][j]}</td>`;
|
|
2668
2666
|
}
|
|
2669
|
-
descTableHtml +=
|
|
2667
|
+
descTableHtml += '</tr>';
|
|
2670
2668
|
}
|
|
2671
|
-
descTableHtml +=
|
|
2669
|
+
descTableHtml += '</tbody>';
|
|
2672
2670
|
}
|
|
2673
2671
|
|
|
2674
|
-
descTableHtml +=
|
|
2672
|
+
descTableHtml += '</table>';
|
|
2675
2673
|
}
|
|
2676
2674
|
|
|
2677
2675
|
// bar: don't need colspan or rowspan stuff, put legendX and Y as headers
|
|
2678
2676
|
|
|
2679
|
-
document.getElementById(
|
|
2680
|
-
document.getElementById(
|
|
2681
|
-
document.getElementById(
|
|
2677
|
+
document.getElementById('desc_title').innerHTML = descType + ' description';
|
|
2678
|
+
document.getElementById('desc_content').innerHTML = descHtml;
|
|
2679
|
+
document.getElementById('desc_table').innerHTML = descTableHtml;
|
|
2682
2680
|
}
|
|
2683
2681
|
}
|
|
2684
2682
|
|
|
@@ -2748,11 +2746,11 @@ class Tracker {
|
|
|
2748
2746
|
* Downloads the tracker data as a JSON file.
|
|
2749
2747
|
*/
|
|
2750
2748
|
DownloadTrackerData() {
|
|
2751
|
-
let link = document.createElement(
|
|
2749
|
+
let link = document.createElement('a');
|
|
2752
2750
|
let data = this.GetTrackerData();
|
|
2753
|
-
let fileStr = new Blob([JSON.stringify(data)], { type:
|
|
2751
|
+
let fileStr = new Blob([JSON.stringify(data)], { type: 'text/plain' });
|
|
2754
2752
|
link.href = URL.createObjectURL(fileStr);
|
|
2755
|
-
link.download =
|
|
2753
|
+
link.download = 'tracking.json';
|
|
2756
2754
|
link.click();
|
|
2757
2755
|
}
|
|
2758
2756
|
|
|
@@ -2781,7 +2779,7 @@ class Tracker {
|
|
|
2781
2779
|
this.data = null;
|
|
2782
2780
|
|
|
2783
2781
|
if (constants.debugLevel > 0) {
|
|
2784
|
-
console.log(
|
|
2782
|
+
console.log('tracking data cleared');
|
|
2785
2783
|
}
|
|
2786
2784
|
|
|
2787
2785
|
this.DataSetup();
|
|
@@ -2789,12 +2787,12 @@ class Tracker {
|
|
|
2789
2787
|
|
|
2790
2788
|
SaveSettings() {
|
|
2791
2789
|
// fetch all settings, push to data.settings
|
|
2792
|
-
let settings = JSON.parse(localStorage.getItem(
|
|
2790
|
+
let settings = JSON.parse(localStorage.getItem('settings_data'));
|
|
2793
2791
|
if (settings) {
|
|
2794
2792
|
// don't store their auth keys
|
|
2795
|
-
settings.openAIAuthKey =
|
|
2796
|
-
settings.geminiAuthKey =
|
|
2797
|
-
this.SetData(
|
|
2793
|
+
settings.openAIAuthKey = 'hidden';
|
|
2794
|
+
settings.geminiAuthKey = 'hidden';
|
|
2795
|
+
this.SetData('settings', settings);
|
|
2798
2796
|
}
|
|
2799
2797
|
}
|
|
2800
2798
|
|
|
@@ -2850,9 +2848,6 @@ class Tracker {
|
|
|
2850
2848
|
if (!this.isUndefinedOrNull(constants.vol)) {
|
|
2851
2849
|
eventToLog.volume = Object.assign(constants.vol);
|
|
2852
2850
|
}
|
|
2853
|
-
if (!this.isUndefinedOrNull(constants.autoPlayRate)) {
|
|
2854
|
-
eventToLog.autoplay_rate = Object.assign(constants.autoPlayRate);
|
|
2855
|
-
}
|
|
2856
2851
|
if (!this.isUndefinedOrNull(constants.colorSelected)) {
|
|
2857
2852
|
eventToLog.color = Object.assign(constants.colorSelected);
|
|
2858
2853
|
}
|
|
@@ -2864,6 +2859,9 @@ class Tracker {
|
|
|
2864
2859
|
if (!this.isUndefinedOrNull(constants.duration)) {
|
|
2865
2860
|
eventToLog.tone_duration = Object.assign(constants.duration);
|
|
2866
2861
|
}
|
|
2862
|
+
if (!this.isUndefinedOrNull(constants.AUTOPLAY_DURATION)) {
|
|
2863
|
+
eventToLog.AUTOPLAY_DURATION = Object.assign(constants.AUTOPLAY_DURATION);
|
|
2864
|
+
}
|
|
2867
2865
|
if (!this.isUndefinedOrNull(constants.autoPlayOutlierRate)) {
|
|
2868
2866
|
eventToLog.autoplay_outlier_rate = Object.assign(
|
|
2869
2867
|
constants.autoPlayOutlierRate
|
|
@@ -2888,7 +2886,7 @@ class Tracker {
|
|
|
2888
2886
|
}
|
|
2889
2887
|
if (!this.isUndefinedOrNull(constants.infoDiv.innerHTML)) {
|
|
2890
2888
|
let textDisplay = Object.assign(constants.infoDiv.innerHTML);
|
|
2891
|
-
textDisplay = textDisplay.replaceAll(/<[^>]*>?/gm,
|
|
2889
|
+
textDisplay = textDisplay.replaceAll(/<[^>]*>?/gm, '');
|
|
2892
2890
|
eventToLog.text_display = textDisplay;
|
|
2893
2891
|
}
|
|
2894
2892
|
if (!this.isUndefinedOrNull(location.href)) {
|
|
@@ -2896,13 +2894,13 @@ class Tracker {
|
|
|
2896
2894
|
}
|
|
2897
2895
|
|
|
2898
2896
|
// chart specific values
|
|
2899
|
-
let x_tickmark =
|
|
2900
|
-
let y_tickmark =
|
|
2901
|
-
let x_label =
|
|
2902
|
-
let y_label =
|
|
2903
|
-
let value =
|
|
2904
|
-
let fill_value =
|
|
2905
|
-
if (constants.chartType ==
|
|
2897
|
+
let x_tickmark = '';
|
|
2898
|
+
let y_tickmark = '';
|
|
2899
|
+
let x_label = '';
|
|
2900
|
+
let y_label = '';
|
|
2901
|
+
let value = '';
|
|
2902
|
+
let fill_value = '';
|
|
2903
|
+
if (constants.chartType == 'bar') {
|
|
2906
2904
|
if (!this.isUndefinedOrNull(plot.columnLabels[position.x])) {
|
|
2907
2905
|
x_tickmark = plot.columnLabels[position.x];
|
|
2908
2906
|
}
|
|
@@ -2915,7 +2913,7 @@ class Tracker {
|
|
|
2915
2913
|
if (!this.isUndefinedOrNull(plot.plotData[position.x])) {
|
|
2916
2914
|
value = plot.plotData[position.x];
|
|
2917
2915
|
}
|
|
2918
|
-
} else if (constants.chartType ==
|
|
2916
|
+
} else if (constants.chartType == 'heat') {
|
|
2919
2917
|
if (!this.isUndefinedOrNull(plot.x_labels[position.x])) {
|
|
2920
2918
|
x_tickmark = plot.x_labels[position.x].trim();
|
|
2921
2919
|
}
|
|
@@ -2936,11 +2934,11 @@ class Tracker {
|
|
|
2936
2934
|
if (!this.isUndefinedOrNull(plot.group_labels[2])) {
|
|
2937
2935
|
fill_value = plot.group_labels[2];
|
|
2938
2936
|
}
|
|
2939
|
-
} else if (constants.chartType ==
|
|
2937
|
+
} else if (constants.chartType == 'box') {
|
|
2940
2938
|
let plotPos =
|
|
2941
|
-
constants.plotOrientation ==
|
|
2939
|
+
constants.plotOrientation == 'vert' ? position.x : position.y;
|
|
2942
2940
|
let sectionPos =
|
|
2943
|
-
constants.plotOrientation ==
|
|
2941
|
+
constants.plotOrientation == 'vert' ? position.y : position.x;
|
|
2944
2942
|
let sectionLabel = plot.sections[sectionPos];
|
|
2945
2943
|
|
|
2946
2944
|
if (!this.isUndefinedOrNull(plot.x_group_label)) {
|
|
@@ -2949,7 +2947,7 @@ class Tracker {
|
|
|
2949
2947
|
if (!this.isUndefinedOrNull(plot.y_group_label)) {
|
|
2950
2948
|
y_label = plot.y_group_label;
|
|
2951
2949
|
}
|
|
2952
|
-
if (constants.plotOrientation ==
|
|
2950
|
+
if (constants.plotOrientation == 'vert') {
|
|
2953
2951
|
if (plotPos > -1 && sectionPos > -1) {
|
|
2954
2952
|
if (!this.isUndefinedOrNull(sectionLabel)) {
|
|
2955
2953
|
y_tickmark = sectionLabel;
|
|
@@ -2974,7 +2972,7 @@ class Tracker {
|
|
|
2974
2972
|
}
|
|
2975
2973
|
}
|
|
2976
2974
|
}
|
|
2977
|
-
} else if (constants.chartType ==
|
|
2975
|
+
} else if (constants.chartType == 'point') {
|
|
2978
2976
|
if (!this.isUndefinedOrNull(plot.x_group_label)) {
|
|
2979
2977
|
x_label = plot.x_group_label;
|
|
2980
2978
|
}
|
|
@@ -3001,13 +2999,13 @@ class Tracker {
|
|
|
3001
2999
|
|
|
3002
3000
|
//console.log("x_tickmark: '", x_tickmark, "', y_tickmark: '", y_tickmark, "', x_label: '", x_label, "', y_label: '", y_label, "', value: '", value, "', fill_value: '", fill_value);
|
|
3003
3001
|
|
|
3004
|
-
this.SetData(
|
|
3002
|
+
this.SetData('events', eventToLog);
|
|
3005
3003
|
//console.log('logged an event');
|
|
3006
3004
|
}
|
|
3007
3005
|
|
|
3008
3006
|
SetData(key, value) {
|
|
3009
3007
|
let data = this.GetTrackerData();
|
|
3010
|
-
let arrayKeys = [
|
|
3008
|
+
let arrayKeys = ['events', 'ChatHistory', 'settings'];
|
|
3011
3009
|
if (!arrayKeys.includes(key)) {
|
|
3012
3010
|
data[key] = value;
|
|
3013
3011
|
} else {
|
|
@@ -3048,20 +3046,20 @@ class Review {
|
|
|
3048
3046
|
// true means on or show
|
|
3049
3047
|
if (onoff) {
|
|
3050
3048
|
constants.reviewSaveSpot = document.activeElement;
|
|
3051
|
-
constants.review_container.classList.remove(
|
|
3049
|
+
constants.review_container.classList.remove('hidden');
|
|
3052
3050
|
constants.reviewSaveBrailleMode = constants.brailleMode;
|
|
3053
3051
|
constants.review.focus();
|
|
3054
3052
|
|
|
3055
|
-
display.announceText(
|
|
3053
|
+
display.announceText('Review on');
|
|
3056
3054
|
} else {
|
|
3057
|
-
constants.review_container.classList.add(
|
|
3058
|
-
if (constants.reviewSaveBrailleMode ==
|
|
3055
|
+
constants.review_container.classList.add('hidden');
|
|
3056
|
+
if (constants.reviewSaveBrailleMode == 'on') {
|
|
3059
3057
|
// we have to turn braille mode back on
|
|
3060
|
-
display.toggleBrailleMode(
|
|
3058
|
+
display.toggleBrailleMode('on');
|
|
3061
3059
|
} else {
|
|
3062
3060
|
constants.reviewSaveSpot.focus();
|
|
3063
3061
|
}
|
|
3064
|
-
display.announceText(
|
|
3062
|
+
display.announceText('Review off');
|
|
3065
3063
|
}
|
|
3066
3064
|
}
|
|
3067
3065
|
}
|
|
@@ -3078,7 +3076,7 @@ class LogError {
|
|
|
3078
3076
|
* @param {string} a - The absent element to log.
|
|
3079
3077
|
*/
|
|
3080
3078
|
LogAbsentElement(a) {
|
|
3081
|
-
console.log(a,
|
|
3079
|
+
console.log(a, 'not found. Visual highlighting is turned off.');
|
|
3082
3080
|
}
|
|
3083
3081
|
|
|
3084
3082
|
/**
|
|
@@ -3086,7 +3084,7 @@ class LogError {
|
|
|
3086
3084
|
* @param {string} a - The critical element to log.
|
|
3087
3085
|
*/
|
|
3088
3086
|
LogCriticalElement(a) {
|
|
3089
|
-
consolelog(a,
|
|
3087
|
+
consolelog(a, 'is critical. MAIDR unable to run');
|
|
3090
3088
|
}
|
|
3091
3089
|
|
|
3092
3090
|
/**
|
|
@@ -3097,9 +3095,9 @@ class LogError {
|
|
|
3097
3095
|
LogDifferentLengths(a, b) {
|
|
3098
3096
|
console.log(
|
|
3099
3097
|
a,
|
|
3100
|
-
|
|
3098
|
+
'and',
|
|
3101
3099
|
b,
|
|
3102
|
-
|
|
3100
|
+
'do not have the same length. Visual highlighting is turned off.'
|
|
3103
3101
|
);
|
|
3104
3102
|
}
|
|
3105
3103
|
|
|
@@ -3110,11 +3108,11 @@ class LogError {
|
|
|
3110
3108
|
*/
|
|
3111
3109
|
LogTooManyElements(a, b) {
|
|
3112
3110
|
console.log(
|
|
3113
|
-
|
|
3111
|
+
'Too many',
|
|
3114
3112
|
a,
|
|
3115
|
-
|
|
3113
|
+
'elements. Only the first',
|
|
3116
3114
|
b,
|
|
3117
|
-
|
|
3115
|
+
'will be highlighted.'
|
|
3118
3116
|
);
|
|
3119
3117
|
}
|
|
3120
3118
|
|
|
@@ -3123,7 +3121,7 @@ class LogError {
|
|
|
3123
3121
|
* @param {*} a - The parameter that is not an array.
|
|
3124
3122
|
*/
|
|
3125
3123
|
LogNotArray(a) {
|
|
3126
|
-
console.log(a,
|
|
3124
|
+
console.log(a, 'is not an array. Visual highlighting is turned off.');
|
|
3127
3125
|
}
|
|
3128
3126
|
}
|
|
3129
3127
|
|
|
@@ -3971,10 +3969,6 @@ class Display {
|
|
|
3971
3969
|
let targetLabel = this.boxplotGridPlaceholders[sectionPos];
|
|
3972
3970
|
let haveTargetLabel = false;
|
|
3973
3971
|
let adjustedPos = 0;
|
|
3974
|
-
// bookmark: shiny issue: this is being called twice??
|
|
3975
|
-
// and the issue happens on 2nd call, sometimes it skips like 75% or whatever
|
|
3976
|
-
//
|
|
3977
|
-
// on first call, we might call it multiple as we're setting up, I care but let's check that later
|
|
3978
3972
|
|
|
3979
3973
|
if (constants.brailleData) {
|
|
3980
3974
|
for (let i = 0; i < constants.brailleData.length; i++) {
|
|
@@ -4677,6 +4671,10 @@ class Display {
|
|
|
4677
4671
|
|
|
4678
4672
|
// Step 2: normalize and allocate remaining characters and add to our main braille array
|
|
4679
4673
|
let charsAvailable = constants.brailleDisplayLength - numAllocatedChars;
|
|
4674
|
+
// Bug happened here: if our numAllocatedChars is bigger than brailleDisplayLength, it cuts off chars
|
|
4675
|
+
// temp fix: just let it overflow by setting charsAvailable to positive if it's below 0. Or maybe 5 to still give us some play
|
|
4676
|
+
// todo: real solution should probably be to kill outliers that are too close together
|
|
4677
|
+
if (charsAvailable < 5) charsAvailable = 5;
|
|
4680
4678
|
let allocateCharacters = this.AllocateCharacters(lenData, charsAvailable);
|
|
4681
4679
|
// apply allocation
|
|
4682
4680
|
let brailleData = lenData;
|
|
@@ -5090,9 +5088,8 @@ class BarChart {
|
|
|
5090
5088
|
}
|
|
5091
5089
|
}
|
|
5092
5090
|
constants.maxX = this.columnLabels.length;
|
|
5093
|
-
constants.autoPlayRate = Math.
|
|
5094
|
-
|
|
5095
|
-
constants.MAX_SPEED
|
|
5091
|
+
constants.autoPlayRate = Math.ceil(
|
|
5092
|
+
constants.AUTOPLAY_DURATION / this.plotData.length
|
|
5096
5093
|
);
|
|
5097
5094
|
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
5098
5095
|
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
@@ -5423,10 +5420,7 @@ class BoxPlot {
|
|
|
5423
5420
|
constants.minY = 0;
|
|
5424
5421
|
constants.maxY = this.plotData.length - 1;
|
|
5425
5422
|
}
|
|
5426
|
-
constants.autoPlayRate = Math.
|
|
5427
|
-
Math.ceil(constants.AUTOPLAY_DURATION / this.plotData.length),
|
|
5428
|
-
constants.MAX_SPEED
|
|
5429
|
-
);
|
|
5423
|
+
constants.autoPlayRate = Math.ceil(constants.AUTOPLAY_DURATION / 7);
|
|
5430
5424
|
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
5431
5425
|
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
5432
5426
|
constants.MIN_SPEED = constants.autoPlayRate;
|
|
@@ -6127,9 +6121,8 @@ class HeatMap {
|
|
|
6127
6121
|
constants.maxX = this.data[0].length - 1;
|
|
6128
6122
|
constants.minY = Math.min(...this.data.map((row) => Math.min(...row)));
|
|
6129
6123
|
constants.maxY = Math.max(...this.data.map((row) => Math.max(...row)));
|
|
6130
|
-
constants.autoPlayRate = Math.
|
|
6131
|
-
|
|
6132
|
-
constants.MAX_SPEED
|
|
6124
|
+
constants.autoPlayRate = Math.ceil(
|
|
6125
|
+
constants.AUTOPLAY_DURATION / (constants.maxX + 1)
|
|
6133
6126
|
);
|
|
6134
6127
|
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
6135
6128
|
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
@@ -6801,9 +6794,8 @@ class ScatterPlot {
|
|
|
6801
6794
|
constants.minY = Math.min(...yValues);
|
|
6802
6795
|
constants.maxY = Math.max(...yValues);
|
|
6803
6796
|
|
|
6804
|
-
constants.autoPlayRate = Math.
|
|
6805
|
-
|
|
6806
|
-
constants.MAX_SPEED
|
|
6797
|
+
constants.autoPlayRate = Math.ceil(
|
|
6798
|
+
constants.AUTOPLAY_DURATION / (constants.maxX + 1)
|
|
6807
6799
|
);
|
|
6808
6800
|
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
6809
6801
|
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
@@ -7353,9 +7345,8 @@ class Histogram {
|
|
|
7353
7345
|
}
|
|
7354
7346
|
}
|
|
7355
7347
|
}
|
|
7356
|
-
constants.autoPlayRate = Math.
|
|
7357
|
-
|
|
7358
|
-
constants.MAX_SPEED
|
|
7348
|
+
constants.autoPlayRate = Math.ceil(
|
|
7349
|
+
constants.AUTOPLAY_DURATION / this.plotData.length
|
|
7359
7350
|
);
|
|
7360
7351
|
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
7361
7352
|
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
@@ -7376,13 +7367,13 @@ class Histogram {
|
|
|
7376
7367
|
this.activeElementColor = this.activeElement.getAttribute('fill');
|
|
7377
7368
|
// Get new color to highlight and replace fill value
|
|
7378
7369
|
this.activeElement.setAttribute(
|
|
7379
|
-
|
|
7380
|
-
|
|
7370
|
+
'fill',
|
|
7371
|
+
constants.GetBetterColor(this.activeElementColor)
|
|
7381
7372
|
);
|
|
7382
7373
|
// Case where fill is within the style attribute
|
|
7383
7374
|
} else if (
|
|
7384
|
-
|
|
7385
|
-
|
|
7375
|
+
this.activeElement.hasAttribute('style') &&
|
|
7376
|
+
this.activeElement.getAttribute('style').indexOf('fill') !== -1
|
|
7386
7377
|
) {
|
|
7387
7378
|
let styleString = this.activeElement.getAttribute('style');
|
|
7388
7379
|
// Extract all style attributes and values
|
|
@@ -7390,7 +7381,7 @@ class Histogram {
|
|
|
7390
7381
|
this.activeElementColor = styleArray[styleArray.indexOf('fill') + 1];
|
|
7391
7382
|
// Get new color to highlight and replace fill value in style array
|
|
7392
7383
|
styleArray[styleArray.indexOf('fill') + 1] = constants.GetBetterColor(
|
|
7393
|
-
|
|
7384
|
+
this.activeElementColor
|
|
7394
7385
|
);
|
|
7395
7386
|
// Recreate style string and set style attribute
|
|
7396
7387
|
styleString = constants.GetStyleStringFromArray(styleArray);
|
|
@@ -7415,8 +7406,8 @@ class Histogram {
|
|
|
7415
7406
|
this.activeElement.setAttribute('fill', this.activeElementColor);
|
|
7416
7407
|
this.activeElement = null;
|
|
7417
7408
|
} else if (
|
|
7418
|
-
|
|
7419
|
-
|
|
7409
|
+
this.activeElement.hasAttribute('style') &&
|
|
7410
|
+
this.activeElement.getAttribute('style').indexOf('fill') !== -1
|
|
7420
7411
|
) {
|
|
7421
7412
|
let styleString = this.activeElement.getAttribute('style');
|
|
7422
7413
|
let styleArray = constants.GetStyleArrayFromString(styleString);
|
|
@@ -7491,9 +7482,8 @@ class LinePlot {
|
|
|
7491
7482
|
singleMaidr.data[0].y
|
|
7492
7483
|
);
|
|
7493
7484
|
|
|
7494
|
-
constants.autoPlayRate = Math.
|
|
7495
|
-
|
|
7496
|
-
constants.MAX_SPEED
|
|
7485
|
+
constants.autoPlayRate = Math.ceil(
|
|
7486
|
+
constants.AUTOPLAY_DURATION / (constants.maxX + 1)
|
|
7497
7487
|
);
|
|
7498
7488
|
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
7499
7489
|
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
@@ -7995,9 +7985,8 @@ class Segmented {
|
|
|
7995
7985
|
}
|
|
7996
7986
|
}
|
|
7997
7987
|
constants.maxX = this.level.length;
|
|
7998
|
-
constants.autoPlayRate = Math.
|
|
7999
|
-
|
|
8000
|
-
constants.MAX_SPEED
|
|
7988
|
+
constants.autoPlayRate = Math.ceil(
|
|
7989
|
+
constants.AUTOPLAY_DURATION / this.plotData.length
|
|
8001
7990
|
);
|
|
8002
7991
|
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
8003
7992
|
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
@@ -11205,54 +11194,51 @@ function InitMaidr(thisMaidr) {
|
|
|
11205
11194
|
// actually do eventlisteners for all events
|
|
11206
11195
|
this.SetEvents();
|
|
11207
11196
|
|
|
11208
|
-
// Set img role for chart
|
|
11197
|
+
// Set img role for chart
|
|
11209
11198
|
constants.chart.setAttribute('role', 'img');
|
|
11210
|
-
|
|
11199
|
+
|
|
11211
11200
|
// once everything is set up, announce the chart name (or title as a backup) to the user
|
|
11212
11201
|
// setTimeout(function () {
|
|
11213
11202
|
if ('name' in singleMaidr) {
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
constants.chart.setAttribute('title', announceText);
|
|
11218
|
-
|
|
11219
|
-
// display.announceText(singleMaidr.name);
|
|
11220
|
-
} else if (
|
|
11221
|
-
'title' in singleMaidr ||
|
|
11222
|
-
('labels' in singleMaidr && 'title' in singleMaidr.labels)
|
|
11223
|
-
) {
|
|
11224
|
-
let title =
|
|
11225
|
-
'title' in singleMaidr ? singleMaidr.title : singleMaidr.labels.title;
|
|
11226
|
-
|
|
11227
|
-
// Determine whether type is multiple or single. If multiple, put commas and "and" in between. If single, just put the type.
|
|
11228
|
-
let plotTypeString = Array.isArray(singleMaidr.type)
|
|
11229
|
-
? singleMaidr.type.slice(0, -1).join(', ') +
|
|
11230
|
-
' and ' +
|
|
11231
|
-
singleMaidr.type.slice(-1)
|
|
11232
|
-
: singleMaidr.type;
|
|
11203
|
+
// Add the aria-label and title attributes to the chart
|
|
11204
|
+
constants.chart.setAttribute('aria-label', announceText);
|
|
11205
|
+
constants.chart.setAttribute('title', announceText);
|
|
11233
11206
|
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11207
|
+
// display.announceText(singleMaidr.name);
|
|
11208
|
+
} else if (
|
|
11209
|
+
'title' in singleMaidr ||
|
|
11210
|
+
('labels' in singleMaidr && 'title' in singleMaidr.labels)
|
|
11211
|
+
) {
|
|
11212
|
+
let title =
|
|
11213
|
+
'title' in singleMaidr ? singleMaidr.title : singleMaidr.labels.title;
|
|
11237
11214
|
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11215
|
+
// Determine whether type is multiple or single. If multiple, put commas and "and" in between. If single, just put the type.
|
|
11216
|
+
let plotTypeString = Array.isArray(singleMaidr.type)
|
|
11217
|
+
? singleMaidr.type.slice(0, -1).join(', ') +
|
|
11218
|
+
' and ' +
|
|
11219
|
+
singleMaidr.type.slice(-1)
|
|
11220
|
+
: singleMaidr.type;
|
|
11241
11221
|
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
|
|
11245
|
-
}Toggle B for Braille, T for Text, S for Sonification, and R for Review mode. Use H for Help.`;
|
|
11222
|
+
// Prepare the instruction text for multi-layered plot
|
|
11223
|
+
let multiLayerInstruction =
|
|
11224
|
+
'This is a multi-layered plot. Use PageUp and PageDown to switch between layers.';
|
|
11246
11225
|
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11226
|
+
// Check if plotTypeString has multiple types
|
|
11227
|
+
let isMultiLayered =
|
|
11228
|
+
Array.isArray(singleMaidr.type) && singleMaidr.type.length > 1;
|
|
11250
11229
|
|
|
11230
|
+
// Construct the final announceText string
|
|
11231
|
+
let announceText = `${plotTypeString} plot of ${title}: Use Arrows to navigate data points. ${
|
|
11232
|
+
isMultiLayered ? multiLayerInstruction : ' '
|
|
11233
|
+
}Toggle B for Braille, T for Text, S for Sonification, and R for Review mode. Use H for Help.`;
|
|
11251
11234
|
|
|
11235
|
+
// Add the aria-label and title attributes to the chart
|
|
11236
|
+
constants.chart.setAttribute('aria-label', announceText);
|
|
11237
|
+
constants.chart.setAttribute('title', announceText);
|
|
11252
11238
|
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11239
|
+
// Display the announcement text
|
|
11240
|
+
// display.announceText(announceText);
|
|
11241
|
+
}
|
|
11256
11242
|
// }, 100);
|
|
11257
11243
|
}
|
|
11258
11244
|
}
|
|
@@ -11532,7 +11518,7 @@ function CreateChartComponents() {
|
|
|
11532
11518
|
constants.review_id_container +
|
|
11533
11519
|
'" class="hidden sr-only sr-only-focusable"><input id="' +
|
|
11534
11520
|
constants.review_id +
|
|
11535
|
-
'" type="text" size="50" /></div>'
|
|
11521
|
+
'" type="text" size="50" autocomplete="off" /></div>'
|
|
11536
11522
|
);
|
|
11537
11523
|
|
|
11538
11524
|
// some tweaks
|