maidr 2.17.2 → 2.18.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/README.md +1 -1
- package/dist/maidr.js +623 -539
- 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,28 +128,36 @@ class Constants {
|
|
|
128
128
|
* @memberof BTSModes
|
|
129
129
|
* @default 'verbose'
|
|
130
130
|
*/
|
|
131
|
-
textMode =
|
|
131
|
+
textMode = "verbose";
|
|
132
132
|
/**
|
|
133
133
|
* The current braille mode. Can be 'off' or 'on'.
|
|
134
134
|
* @type {("off"|"on")}
|
|
135
135
|
* @memberof BTSModes
|
|
136
136
|
* @default 'off'
|
|
137
137
|
*/
|
|
138
|
-
brailleMode =
|
|
138
|
+
brailleMode = "off";
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* We lock the selection so we don't pick up programatic selection changes
|
|
142
|
+
* @type {boolean}
|
|
143
|
+
* @default false
|
|
144
|
+
*/
|
|
145
|
+
lockSelection = false;
|
|
146
|
+
|
|
139
147
|
/**
|
|
140
148
|
* The current sonification mode. Can be 'on', 'off', 'sep' (seperated), or 'same' (all played at once).
|
|
141
149
|
* @type {("on"|"off"|"sep"|"same")}
|
|
142
150
|
* @memberof BTSModes
|
|
143
151
|
* @default 'on'
|
|
144
152
|
*/
|
|
145
|
-
sonifMode =
|
|
153
|
+
sonifMode = "on";
|
|
146
154
|
/**
|
|
147
155
|
* The current review mode. Can be 'on' or 'off'.
|
|
148
156
|
* @type {("on"|"off")}
|
|
149
157
|
* @memberof BTSModes
|
|
150
158
|
* @default 'off'
|
|
151
159
|
*/
|
|
152
|
-
reviewMode =
|
|
160
|
+
reviewMode = "off";
|
|
153
161
|
|
|
154
162
|
// basic chart properties
|
|
155
163
|
/**
|
|
@@ -190,14 +198,14 @@ class Constants {
|
|
|
190
198
|
* @memberof HtmlIds
|
|
191
199
|
* @default ''
|
|
192
200
|
*/
|
|
193
|
-
plotId =
|
|
201
|
+
plotId = ""; // update with id in chart specific js
|
|
194
202
|
/**
|
|
195
203
|
* The chart type, sort of a short name of the chart such as 'box', 'bar', 'line', etc.
|
|
196
204
|
* @type {string}
|
|
197
205
|
* @default ''
|
|
198
206
|
* @memberof BasicChartProperties
|
|
199
207
|
*/
|
|
200
|
-
chartType =
|
|
208
|
+
chartType = "";
|
|
201
209
|
/**
|
|
202
210
|
* The navigation orientation of the chart. 0 = row navigation (up/down), 1 = col navigation (left/right).
|
|
203
211
|
* @type {number}
|
|
@@ -211,7 +219,7 @@ class Constants {
|
|
|
211
219
|
* @default 'horz'
|
|
212
220
|
* @memberof BasicChartProperties
|
|
213
221
|
*/
|
|
214
|
-
plotOrientation =
|
|
222
|
+
plotOrientation = "horz";
|
|
215
223
|
|
|
216
224
|
/**
|
|
217
225
|
* @namespace AudioProperties
|
|
@@ -321,7 +329,7 @@ class Constants {
|
|
|
321
329
|
* @default '#03C809' (green)
|
|
322
330
|
* @memberof UserSettings
|
|
323
331
|
*/
|
|
324
|
-
colorSelected =
|
|
332
|
+
colorSelected = "#03C809";
|
|
325
333
|
/**
|
|
326
334
|
* 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).
|
|
327
335
|
* @type {number}
|
|
@@ -389,7 +397,7 @@ class Constants {
|
|
|
389
397
|
* @default 50
|
|
390
398
|
* @memberof AdvancedUserSettings
|
|
391
399
|
*/
|
|
392
|
-
colorUnselected =
|
|
400
|
+
colorUnselected = "#595959"; // deprecated, todo: find all instances replace with storing old color method
|
|
393
401
|
/**
|
|
394
402
|
* Whether or not we're logging user data. This is off by default, but is used for research purposes.
|
|
395
403
|
* @type {boolean}
|
|
@@ -417,30 +425,30 @@ class Constants {
|
|
|
417
425
|
* @default 'assertive'
|
|
418
426
|
* @memberof AdvancedUserSettings
|
|
419
427
|
*/
|
|
420
|
-
ariaMode =
|
|
428
|
+
ariaMode = "assertive";
|
|
421
429
|
|
|
422
430
|
/**
|
|
423
431
|
* Full list of user settings, used internally to save and load settings.
|
|
424
432
|
* @type {string[]}
|
|
425
433
|
*/
|
|
426
434
|
userSettingsKeys = [
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
435
|
+
"vol",
|
|
436
|
+
"autoPlayRate",
|
|
437
|
+
"brailleDisplayLength",
|
|
438
|
+
"colorSelected",
|
|
439
|
+
"MIN_FREQUENCY",
|
|
440
|
+
"MAX_FREQUENCY",
|
|
441
|
+
"keypressInterval",
|
|
442
|
+
"ariaMode",
|
|
443
|
+
"openAIAuthKey",
|
|
444
|
+
"geminiAuthKey",
|
|
445
|
+
"skillLevel",
|
|
446
|
+
"skillLevelOther",
|
|
447
|
+
"LLMModel",
|
|
448
|
+
"LLMPreferences",
|
|
449
|
+
"LLMOpenAiMulti",
|
|
450
|
+
"LLMGeminiMulti",
|
|
451
|
+
"autoInitLLM",
|
|
444
452
|
];
|
|
445
453
|
|
|
446
454
|
// LLM settings
|
|
@@ -481,14 +489,14 @@ class Constants {
|
|
|
481
489
|
* @default 'high'
|
|
482
490
|
* @memberof LLMSettings
|
|
483
491
|
*/
|
|
484
|
-
LLMDetail =
|
|
492
|
+
LLMDetail = "high"; // low (default for testing, like 100 tokens) / high (default for real, like 1000 tokens)
|
|
485
493
|
/**
|
|
486
494
|
* Current LLM model in use. Can be 'openai' (default) or 'gemini' or 'multi'. More to be added.
|
|
487
495
|
* @type {("openai"|"gemini"|"multi")}
|
|
488
496
|
* @default 'openai'
|
|
489
497
|
* @memberof LLMSettings
|
|
490
498
|
*/
|
|
491
|
-
LLMModel =
|
|
499
|
+
LLMModel = "openai";
|
|
492
500
|
/**
|
|
493
501
|
* The default system message for the LLM. Helps the LLM understand the context of the chart and its role.
|
|
494
502
|
* @type {string}
|
|
@@ -496,21 +504,21 @@ class Constants {
|
|
|
496
504
|
* @memberof LLMSettings
|
|
497
505
|
*/
|
|
498
506
|
LLMSystemMessage =
|
|
499
|
-
|
|
507
|
+
"You are a helpful assistant describing the chart to a blind person. ";
|
|
500
508
|
/**
|
|
501
509
|
* 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.
|
|
502
510
|
* @type {("basic"|"intermediate"|"expert"|"other")}
|
|
503
511
|
* @default 'basic'
|
|
504
512
|
* @memberof LLMSettings
|
|
505
513
|
*/
|
|
506
|
-
skillLevel =
|
|
514
|
+
skillLevel = "basic"; // basic / intermediate / expert
|
|
507
515
|
/**
|
|
508
516
|
* Custom skill level, used if the user selects 'other' as their skill level.
|
|
509
517
|
* @type {string}
|
|
510
518
|
* @default ''
|
|
511
519
|
* @memberof LLMSettings
|
|
512
520
|
*/
|
|
513
|
-
skillLevelOther =
|
|
521
|
+
skillLevelOther = ""; // custom skill level
|
|
514
522
|
/**
|
|
515
523
|
* 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.
|
|
516
524
|
* @type {boolean}
|
|
@@ -524,7 +532,7 @@ class Constants {
|
|
|
524
532
|
* @default ''
|
|
525
533
|
* @memberof LLMSettings
|
|
526
534
|
*/
|
|
527
|
-
verboseText =
|
|
535
|
+
verboseText = "";
|
|
528
536
|
/**
|
|
529
537
|
* An internal variable used to turn the waiting beep on and off.
|
|
530
538
|
* @type {number}
|
|
@@ -572,31 +580,31 @@ class Constants {
|
|
|
572
580
|
* @type {boolean}
|
|
573
581
|
* @memberof PlatformControls
|
|
574
582
|
*/
|
|
575
|
-
isMac = navigator.userAgent.toLowerCase().includes(
|
|
583
|
+
isMac = navigator.userAgent.toLowerCase().includes("mac"); // true if macOS
|
|
576
584
|
/**
|
|
577
585
|
* The control key for the user's platform. Can be 'Cmd' or 'Ctrl'. Used in keyboard shortcut display in help.
|
|
578
586
|
* @type {"Cmd"|"Ctrl"}
|
|
579
587
|
* @memberof PlatformControls
|
|
580
588
|
*/
|
|
581
|
-
control = this.isMac ?
|
|
589
|
+
control = this.isMac ? "Cmd" : "Ctrl";
|
|
582
590
|
/**
|
|
583
591
|
* The alt key for the user's platform. Can be 'option' or 'Alt'. Used in keyboard shortcut display in help.
|
|
584
592
|
* @type {"option"|"Alt"}
|
|
585
593
|
* @memberof PlatformControls
|
|
586
594
|
*/
|
|
587
|
-
alt = this.isMac ?
|
|
595
|
+
alt = this.isMac ? "option" : "Alt";
|
|
588
596
|
/**
|
|
589
597
|
* The home key for the user's platform. Can be 'fn + Left arrow' or 'Home'. Used in keyboard shortcut display in help.
|
|
590
598
|
* @type {"fn + Left arrow"|"Home"}
|
|
591
599
|
* @memberof PlatformControls
|
|
592
600
|
*/
|
|
593
|
-
home = this.isMac ?
|
|
601
|
+
home = this.isMac ? "fn + Left arrow" : "Home";
|
|
594
602
|
/**
|
|
595
603
|
* The end key for the user's platform. Can be 'fn + Right arrow' or 'End'. Used in keyboard shortcut display in help.
|
|
596
604
|
* @type {"fn + Right arrow"|"End"}
|
|
597
605
|
* @memberof PlatformControls
|
|
598
606
|
*/
|
|
599
|
-
end = this.isMac ?
|
|
607
|
+
end = this.isMac ? "fn + Right arrow" : "End";
|
|
600
608
|
|
|
601
609
|
// internal controls
|
|
602
610
|
// todo: are these even used? Sean doesn't think so (May 2024)
|
|
@@ -689,13 +697,13 @@ class Constants {
|
|
|
689
697
|
*/
|
|
690
698
|
ConvertHexToRGBString(hexColorString) {
|
|
691
699
|
return (
|
|
692
|
-
|
|
700
|
+
"rgb(" +
|
|
693
701
|
parseInt(hexColorString.slice(1, 3), 16) +
|
|
694
|
-
|
|
702
|
+
"," +
|
|
695
703
|
parseInt(hexColorString.slice(3, 5), 16) +
|
|
696
|
-
|
|
704
|
+
"," +
|
|
697
705
|
parseInt(hexColorString.slice(5, 7), 16) +
|
|
698
|
-
|
|
706
|
+
")"
|
|
699
707
|
);
|
|
700
708
|
}
|
|
701
709
|
|
|
@@ -705,12 +713,12 @@ class Constants {
|
|
|
705
713
|
* @returns {string} - hexadecimal color (e.g., "#595959").
|
|
706
714
|
*/
|
|
707
715
|
ConvertRGBStringToHex(rgbColorString) {
|
|
708
|
-
let rgb = rgbColorString.replace(/[^\d,]/g,
|
|
716
|
+
let rgb = rgbColorString.replace(/[^\d,]/g, "").split(",");
|
|
709
717
|
return (
|
|
710
|
-
|
|
711
|
-
rgb[0].toString(16).padStart(2,
|
|
712
|
-
rgb[1].toString(16).padStart(2,
|
|
713
|
-
rgb[2].toString(16).padStart(2,
|
|
718
|
+
"#" +
|
|
719
|
+
rgb[0].toString(16).padStart(2, "0") +
|
|
720
|
+
rgb[1].toString(16).padStart(2, "0") +
|
|
721
|
+
rgb[2].toString(16).padStart(2, "0")
|
|
714
722
|
);
|
|
715
723
|
}
|
|
716
724
|
|
|
@@ -722,11 +730,11 @@ class Constants {
|
|
|
722
730
|
*/
|
|
723
731
|
ColorInvert(color) {
|
|
724
732
|
// invert an rgb color
|
|
725
|
-
let rgb = color.replace(/[^\d,]/g,
|
|
733
|
+
let rgb = color.replace(/[^\d,]/g, "").split(",");
|
|
726
734
|
let r = 255 - rgb[0];
|
|
727
735
|
let g = 255 - rgb[1];
|
|
728
736
|
let b = 255 - rgb[2];
|
|
729
|
-
return
|
|
737
|
+
return "rgb(" + r + "," + g + "," + b + ")";
|
|
730
738
|
}
|
|
731
739
|
|
|
732
740
|
/**
|
|
@@ -735,11 +743,11 @@ class Constants {
|
|
|
735
743
|
* @returns {string} The better color
|
|
736
744
|
*/
|
|
737
745
|
GetBetterColor(oldColor) {
|
|
738
|
-
if (oldColor.indexOf(
|
|
746
|
+
if (oldColor.indexOf("#") !== -1) {
|
|
739
747
|
oldColor = this.ConvertHexToRGBString(oldColor);
|
|
740
748
|
}
|
|
741
749
|
let newColor = this.ColorInvert(oldColor);
|
|
742
|
-
let rgb = newColor.replace(/[^\d,]/g,
|
|
750
|
+
let rgb = newColor.replace(/[^\d,]/g, "").split(",");
|
|
743
751
|
if (
|
|
744
752
|
rgb[1] < rgb[0] + 10 &&
|
|
745
753
|
rgb[1] > rgb[0] - 10 &&
|
|
@@ -761,7 +769,7 @@ class Constants {
|
|
|
761
769
|
*/
|
|
762
770
|
GetStyleArrayFromString(styleString) {
|
|
763
771
|
// Get an array of CSS style attributes and values from a style string
|
|
764
|
-
return styleString.replaceAll(
|
|
772
|
+
return styleString.replaceAll(" ", "").split(/[:;]/);
|
|
765
773
|
}
|
|
766
774
|
|
|
767
775
|
/**
|
|
@@ -771,16 +779,16 @@ class Constants {
|
|
|
771
779
|
*/
|
|
772
780
|
GetStyleStringFromArray(styleArray) {
|
|
773
781
|
// Get CSS style string from an array of style attributes and values
|
|
774
|
-
let styleString =
|
|
782
|
+
let styleString = "";
|
|
775
783
|
for (let i = 0; i < styleArray.length; i++) {
|
|
776
784
|
if (i % 2 === 0) {
|
|
777
785
|
if (i !== styleArray.length - 1) {
|
|
778
|
-
styleString += styleArray[i] +
|
|
786
|
+
styleString += styleArray[i] + ": ";
|
|
779
787
|
} else {
|
|
780
788
|
styleString += styleArray[i];
|
|
781
789
|
}
|
|
782
790
|
} else {
|
|
783
|
-
styleString += styleArray[i] +
|
|
791
|
+
styleString += styleArray[i] + "; ";
|
|
784
792
|
}
|
|
785
793
|
}
|
|
786
794
|
return styleString;
|
|
@@ -793,35 +801,35 @@ class Constants {
|
|
|
793
801
|
class Resources {
|
|
794
802
|
constructor() {}
|
|
795
803
|
|
|
796
|
-
language =
|
|
797
|
-
knowledgeLevel =
|
|
804
|
+
language = "en"; // Current language, 2 char lang code
|
|
805
|
+
knowledgeLevel = "basic"; // basic, intermediate, expert
|
|
798
806
|
|
|
799
807
|
// language strings, per 2 char language code
|
|
800
808
|
strings = {
|
|
801
809
|
en: {
|
|
802
810
|
basic: {
|
|
803
|
-
upper_outlier:
|
|
804
|
-
lower_outlier:
|
|
805
|
-
min:
|
|
806
|
-
max:
|
|
807
|
-
25:
|
|
808
|
-
50:
|
|
809
|
-
75:
|
|
810
|
-
q1:
|
|
811
|
-
q2:
|
|
812
|
-
q3:
|
|
813
|
-
son_on:
|
|
814
|
-
son_off:
|
|
815
|
-
son_des:
|
|
816
|
-
son_comp:
|
|
817
|
-
son_ch:
|
|
818
|
-
son_sep:
|
|
819
|
-
son_same:
|
|
820
|
-
empty:
|
|
821
|
-
openai:
|
|
822
|
-
gemini:
|
|
823
|
-
multi:
|
|
824
|
-
processing:
|
|
811
|
+
upper_outlier: "Upper Outlier",
|
|
812
|
+
lower_outlier: "Lower Outlier",
|
|
813
|
+
min: "Minimum",
|
|
814
|
+
max: "Maximum",
|
|
815
|
+
25: "25%",
|
|
816
|
+
50: "50%",
|
|
817
|
+
75: "75%",
|
|
818
|
+
q1: "25%",
|
|
819
|
+
q2: "50%",
|
|
820
|
+
q3: "75%",
|
|
821
|
+
son_on: "Sonification on",
|
|
822
|
+
son_off: "Sonification off",
|
|
823
|
+
son_des: "Sonification descrete",
|
|
824
|
+
son_comp: "Sonification compare",
|
|
825
|
+
son_ch: "Sonification chord",
|
|
826
|
+
son_sep: "Sonification separate",
|
|
827
|
+
son_same: "Sonification combined",
|
|
828
|
+
empty: "Empty",
|
|
829
|
+
openai: "OpenAI Vision",
|
|
830
|
+
gemini: "Gemini Pro Vision",
|
|
831
|
+
multi: "Multiple AI",
|
|
832
|
+
processing: "Processing Chart...",
|
|
825
833
|
},
|
|
826
834
|
},
|
|
827
835
|
};
|
|
@@ -936,7 +944,11 @@ class Menu {
|
|
|
936
944
|
<td>Auto-play speed down</td>
|
|
937
945
|
<td>Comma (,)</td>
|
|
938
946
|
</tr>
|
|
939
|
-
|
|
947
|
+
<tr>
|
|
948
|
+
<td>Auto-play speed reset</td>
|
|
949
|
+
<td>Slash (/)</td>
|
|
950
|
+
</tr>
|
|
951
|
+
<tr>
|
|
940
952
|
<td>Check label for the title of current plot</td>
|
|
941
953
|
<td>l t</td>
|
|
942
954
|
</tr>
|
|
@@ -992,12 +1004,12 @@ class Menu {
|
|
|
992
1004
|
<div><fieldset>
|
|
993
1005
|
<legend>Aria Mode</legend>
|
|
994
1006
|
<p><input type="radio" id="aria_mode_assertive" name="aria_mode" value="assertive" ${
|
|
995
|
-
constants.ariaMode ==
|
|
996
|
-
?
|
|
997
|
-
:
|
|
1007
|
+
constants.ariaMode == "assertive"
|
|
1008
|
+
? "checked"
|
|
1009
|
+
: ""
|
|
998
1010
|
}><label for="aria_mode_assertive">Assertive</label></p>
|
|
999
1011
|
<p><input type="radio" id="aria_mode_polite" name="aria_mode" value="polite" ${
|
|
1000
|
-
constants.ariaMode ==
|
|
1012
|
+
constants.ariaMode == "polite" ? "checked" : ""
|
|
1001
1013
|
}><label for="aria_mode_polite">Polite</label></p>
|
|
1002
1014
|
</fieldset></div>
|
|
1003
1015
|
<h5 class="modal-title">LLM Settings</h5>
|
|
@@ -1018,7 +1030,7 @@ class Menu {
|
|
|
1018
1030
|
<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>
|
|
1019
1031
|
</p>
|
|
1020
1032
|
<p><input type="checkbox" ${
|
|
1021
|
-
constants.autoInitLLM ?
|
|
1033
|
+
constants.autoInitLLM ? "checked" : ""
|
|
1022
1034
|
} id="init_llm_on_load" name="init_llm_on_load"><label for="init_llm_on_load">Start first LLM chat chart load</label></p>
|
|
1023
1035
|
<p>
|
|
1024
1036
|
<select id="skill_level">
|
|
@@ -1055,33 +1067,33 @@ class Menu {
|
|
|
1055
1067
|
CreateMenu() {
|
|
1056
1068
|
// menu element creation
|
|
1057
1069
|
document
|
|
1058
|
-
.querySelector(
|
|
1059
|
-
.insertAdjacentHTML(
|
|
1070
|
+
.querySelector("body")
|
|
1071
|
+
.insertAdjacentHTML("beforeend", this.menuHtml);
|
|
1060
1072
|
|
|
1061
1073
|
// menu close events
|
|
1062
|
-
let allClose = document.querySelectorAll(
|
|
1074
|
+
let allClose = document.querySelectorAll("#close_menu, #menu .close");
|
|
1063
1075
|
for (let i = 0; i < allClose.length; i++) {
|
|
1064
1076
|
constants.events.push([
|
|
1065
1077
|
allClose[i],
|
|
1066
|
-
|
|
1078
|
+
"click",
|
|
1067
1079
|
function (e) {
|
|
1068
1080
|
menu.Toggle(false);
|
|
1069
1081
|
},
|
|
1070
1082
|
]);
|
|
1071
1083
|
}
|
|
1072
1084
|
constants.events.push([
|
|
1073
|
-
document.getElementById(
|
|
1074
|
-
|
|
1085
|
+
document.getElementById("save_and_close_menu"),
|
|
1086
|
+
"click",
|
|
1075
1087
|
function (e) {
|
|
1076
1088
|
menu.SaveData();
|
|
1077
1089
|
menu.Toggle(false);
|
|
1078
1090
|
},
|
|
1079
1091
|
]);
|
|
1080
1092
|
constants.events.push([
|
|
1081
|
-
document.getElementById(
|
|
1082
|
-
|
|
1093
|
+
document.getElementById("menu"),
|
|
1094
|
+
"keyup",
|
|
1083
1095
|
function (e) {
|
|
1084
|
-
if (e.key ==
|
|
1096
|
+
if (e.key == "Esc") {
|
|
1085
1097
|
// esc
|
|
1086
1098
|
menu.Toggle(false);
|
|
1087
1099
|
}
|
|
@@ -1091,15 +1103,15 @@ class Menu {
|
|
|
1091
1103
|
// Menu open events
|
|
1092
1104
|
constants.events.push([
|
|
1093
1105
|
document,
|
|
1094
|
-
|
|
1106
|
+
"keyup",
|
|
1095
1107
|
function (e) {
|
|
1096
1108
|
// don't fire on input elements
|
|
1097
1109
|
if (
|
|
1098
|
-
e.target.tagName.toLowerCase() ==
|
|
1099
|
-
e.target.tagName.toLowerCase() ==
|
|
1110
|
+
e.target.tagName.toLowerCase() == "input" ||
|
|
1111
|
+
e.target.tagName.toLowerCase() == "textarea"
|
|
1100
1112
|
) {
|
|
1101
1113
|
return;
|
|
1102
|
-
} else if (e.key ==
|
|
1114
|
+
} else if (e.key == "h") {
|
|
1103
1115
|
menu.Toggle(true);
|
|
1104
1116
|
}
|
|
1105
1117
|
},
|
|
@@ -1107,71 +1119,71 @@ class Menu {
|
|
|
1107
1119
|
|
|
1108
1120
|
// toggle auth key fields
|
|
1109
1121
|
constants.events.push([
|
|
1110
|
-
document.getElementById(
|
|
1111
|
-
|
|
1122
|
+
document.getElementById("LLM_model"),
|
|
1123
|
+
"change",
|
|
1112
1124
|
function (e) {
|
|
1113
|
-
if (e.target.value ==
|
|
1125
|
+
if (e.target.value == "openai") {
|
|
1114
1126
|
document
|
|
1115
|
-
.getElementById(
|
|
1116
|
-
.classList.remove(
|
|
1127
|
+
.getElementById("openai_auth_key_container")
|
|
1128
|
+
.classList.remove("hidden");
|
|
1117
1129
|
document
|
|
1118
|
-
.getElementById(
|
|
1119
|
-
.classList.add(
|
|
1130
|
+
.getElementById("gemini_auth_key_container")
|
|
1131
|
+
.classList.add("hidden");
|
|
1120
1132
|
document
|
|
1121
|
-
.getElementById(
|
|
1122
|
-
.classList.add(
|
|
1133
|
+
.getElementById("openai_multi_container")
|
|
1134
|
+
.classList.add("hidden");
|
|
1123
1135
|
document
|
|
1124
|
-
.getElementById(
|
|
1125
|
-
.classList.add(
|
|
1126
|
-
document.getElementById(
|
|
1127
|
-
document.getElementById(
|
|
1128
|
-
} else if (e.target.value ==
|
|
1136
|
+
.getElementById("gemini_multi_container")
|
|
1137
|
+
.classList.add("hidden");
|
|
1138
|
+
document.getElementById("openai_multi").checked = true;
|
|
1139
|
+
document.getElementById("gemini_multi").checked = false;
|
|
1140
|
+
} else if (e.target.value == "gemini") {
|
|
1129
1141
|
document
|
|
1130
|
-
.getElementById(
|
|
1131
|
-
.classList.add(
|
|
1142
|
+
.getElementById("openai_auth_key_container")
|
|
1143
|
+
.classList.add("hidden");
|
|
1132
1144
|
document
|
|
1133
|
-
.getElementById(
|
|
1134
|
-
.classList.remove(
|
|
1145
|
+
.getElementById("gemini_auth_key_container")
|
|
1146
|
+
.classList.remove("hidden");
|
|
1135
1147
|
document
|
|
1136
|
-
.getElementById(
|
|
1137
|
-
.classList.add(
|
|
1148
|
+
.getElementById("openai_multi_container")
|
|
1149
|
+
.classList.add("hidden");
|
|
1138
1150
|
document
|
|
1139
|
-
.getElementById(
|
|
1140
|
-
.classList.add(
|
|
1141
|
-
document.getElementById(
|
|
1142
|
-
document.getElementById(
|
|
1143
|
-
} else if (e.target.value ==
|
|
1151
|
+
.getElementById("gemini_multi_container")
|
|
1152
|
+
.classList.add("hidden");
|
|
1153
|
+
document.getElementById("openai_multi").checked = false;
|
|
1154
|
+
document.getElementById("gemini_multi").checked = true;
|
|
1155
|
+
} else if (e.target.value == "multi") {
|
|
1144
1156
|
document
|
|
1145
|
-
.getElementById(
|
|
1146
|
-
.classList.remove(
|
|
1157
|
+
.getElementById("openai_auth_key_container")
|
|
1158
|
+
.classList.remove("hidden");
|
|
1147
1159
|
document
|
|
1148
|
-
.getElementById(
|
|
1149
|
-
.classList.remove(
|
|
1160
|
+
.getElementById("gemini_auth_key_container")
|
|
1161
|
+
.classList.remove("hidden");
|
|
1150
1162
|
document
|
|
1151
|
-
.getElementById(
|
|
1152
|
-
.classList.remove(
|
|
1163
|
+
.getElementById("openai_multi_container")
|
|
1164
|
+
.classList.remove("hidden");
|
|
1153
1165
|
document
|
|
1154
|
-
.getElementById(
|
|
1155
|
-
.classList.remove(
|
|
1156
|
-
document.getElementById(
|
|
1157
|
-
document.getElementById(
|
|
1166
|
+
.getElementById("gemini_multi_container")
|
|
1167
|
+
.classList.remove("hidden");
|
|
1168
|
+
document.getElementById("openai_multi").checked = true;
|
|
1169
|
+
document.getElementById("gemini_multi").checked = true;
|
|
1158
1170
|
}
|
|
1159
1171
|
},
|
|
1160
1172
|
]);
|
|
1161
1173
|
|
|
1162
1174
|
// Skill level other events
|
|
1163
1175
|
constants.events.push([
|
|
1164
|
-
document.getElementById(
|
|
1165
|
-
|
|
1176
|
+
document.getElementById("skill_level"),
|
|
1177
|
+
"change",
|
|
1166
1178
|
function (e) {
|
|
1167
|
-
if (e.target.value ==
|
|
1179
|
+
if (e.target.value == "other") {
|
|
1168
1180
|
document
|
|
1169
|
-
.getElementById(
|
|
1170
|
-
.classList.remove(
|
|
1181
|
+
.getElementById("skill_level_other_container")
|
|
1182
|
+
.classList.remove("hidden");
|
|
1171
1183
|
} else {
|
|
1172
1184
|
document
|
|
1173
|
-
.getElementById(
|
|
1174
|
-
.classList.add(
|
|
1185
|
+
.getElementById("skill_level_other_container")
|
|
1186
|
+
.classList.add("hidden");
|
|
1175
1187
|
}
|
|
1176
1188
|
},
|
|
1177
1189
|
]);
|
|
@@ -1179,16 +1191,16 @@ class Menu {
|
|
|
1179
1191
|
// trigger notification that LLM will be reset
|
|
1180
1192
|
// this is done on change of LLM model, multi settings, or skill level
|
|
1181
1193
|
let LLMResetIds = [
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1194
|
+
"LLM_model",
|
|
1195
|
+
"openai_multi",
|
|
1196
|
+
"gemini_multi",
|
|
1197
|
+
"skill_level",
|
|
1198
|
+
"LLM_preferences",
|
|
1187
1199
|
];
|
|
1188
1200
|
for (let i = 0; i < LLMResetIds.length; i++) {
|
|
1189
1201
|
constants.events.push([
|
|
1190
1202
|
document.getElementById(LLMResetIds[i]),
|
|
1191
|
-
|
|
1203
|
+
"change",
|
|
1192
1204
|
function (e) {
|
|
1193
1205
|
menu.NotifyOfLLMReset();
|
|
1194
1206
|
},
|
|
@@ -1202,11 +1214,11 @@ class Menu {
|
|
|
1202
1214
|
*/
|
|
1203
1215
|
Destroy() {
|
|
1204
1216
|
// menu element destruction
|
|
1205
|
-
let menu = document.getElementById(
|
|
1217
|
+
let menu = document.getElementById("menu");
|
|
1206
1218
|
if (menu) {
|
|
1207
1219
|
menu.remove();
|
|
1208
1220
|
}
|
|
1209
|
-
let backdrop = document.getElementById(
|
|
1221
|
+
let backdrop = document.getElementById("menu_modal_backdrop");
|
|
1210
1222
|
if (backdrop) {
|
|
1211
1223
|
backdrop.remove();
|
|
1212
1224
|
}
|
|
@@ -1218,16 +1230,16 @@ class Menu {
|
|
|
1218
1230
|
* @return {void}
|
|
1219
1231
|
*/
|
|
1220
1232
|
Toggle(onoff = false) {
|
|
1221
|
-
if (typeof onoff ==
|
|
1222
|
-
if (document.getElementById(
|
|
1233
|
+
if (typeof onoff == "undefined") {
|
|
1234
|
+
if (document.getElementById("menu").classList.contains("hidden")) {
|
|
1223
1235
|
onoff = true;
|
|
1224
1236
|
} else {
|
|
1225
1237
|
onoff = false;
|
|
1226
1238
|
}
|
|
1227
1239
|
}
|
|
1228
1240
|
// don't open if we have another modal open already
|
|
1229
|
-
if (onoff && document.getElementById(
|
|
1230
|
-
if (!document.getElementById(
|
|
1241
|
+
if (onoff && document.getElementById("chatLLM")) {
|
|
1242
|
+
if (!document.getElementById("chatLLM").classList.contains("hidden")) {
|
|
1231
1243
|
return;
|
|
1232
1244
|
}
|
|
1233
1245
|
}
|
|
@@ -1236,13 +1248,13 @@ class Menu {
|
|
|
1236
1248
|
this.whereWasMyFocus = document.activeElement;
|
|
1237
1249
|
this.PopulateData();
|
|
1238
1250
|
constants.tabMovement = 0;
|
|
1239
|
-
document.getElementById(
|
|
1240
|
-
document.getElementById(
|
|
1241
|
-
document.querySelector(
|
|
1251
|
+
document.getElementById("menu").classList.remove("hidden");
|
|
1252
|
+
document.getElementById("menu_modal_backdrop").classList.remove("hidden");
|
|
1253
|
+
document.querySelector("#menu .close").focus();
|
|
1242
1254
|
} else {
|
|
1243
1255
|
// close
|
|
1244
|
-
document.getElementById(
|
|
1245
|
-
document.getElementById(
|
|
1256
|
+
document.getElementById("menu").classList.add("hidden");
|
|
1257
|
+
document.getElementById("menu_modal_backdrop").classList.add("hidden");
|
|
1246
1258
|
this.whereWasMyFocus.focus();
|
|
1247
1259
|
this.whereWasMyFocus = null;
|
|
1248
1260
|
}
|
|
@@ -1253,89 +1265,89 @@ class Menu {
|
|
|
1253
1265
|
* @return {void}
|
|
1254
1266
|
*/
|
|
1255
1267
|
PopulateData() {
|
|
1256
|
-
document.getElementById(
|
|
1257
|
-
document.getElementById(
|
|
1258
|
-
document.getElementById(
|
|
1268
|
+
document.getElementById("vol").value = constants.vol;
|
|
1269
|
+
document.getElementById("autoplay_rate").value = constants.autoPlayRate;
|
|
1270
|
+
document.getElementById("braille_display_length").value =
|
|
1259
1271
|
constants.brailleDisplayLength;
|
|
1260
|
-
document.getElementById(
|
|
1261
|
-
document.getElementById(
|
|
1262
|
-
document.getElementById(
|
|
1263
|
-
document.getElementById(
|
|
1272
|
+
document.getElementById("color_selected").value = constants.colorSelected;
|
|
1273
|
+
document.getElementById("min_freq").value = constants.MIN_FREQUENCY;
|
|
1274
|
+
document.getElementById("max_freq").value = constants.MAX_FREQUENCY;
|
|
1275
|
+
document.getElementById("keypress_interval").value =
|
|
1264
1276
|
constants.keypressInterval;
|
|
1265
|
-
if (typeof constants.openAIAuthKey ==
|
|
1266
|
-
document.getElementById(
|
|
1277
|
+
if (typeof constants.openAIAuthKey == "string") {
|
|
1278
|
+
document.getElementById("openai_auth_key").value =
|
|
1267
1279
|
constants.openAIAuthKey;
|
|
1268
1280
|
}
|
|
1269
|
-
if (typeof constants.geminiAuthKey ==
|
|
1270
|
-
document.getElementById(
|
|
1281
|
+
if (typeof constants.geminiAuthKey == "string") {
|
|
1282
|
+
document.getElementById("gemini_auth_key").value =
|
|
1271
1283
|
constants.geminiAuthKey;
|
|
1272
1284
|
}
|
|
1273
|
-
document.getElementById(
|
|
1285
|
+
document.getElementById("skill_level").value = constants.skillLevel;
|
|
1274
1286
|
if (constants.skillLevelOther) {
|
|
1275
|
-
document.getElementById(
|
|
1287
|
+
document.getElementById("skill_level_other").value =
|
|
1276
1288
|
constants.skillLevelOther;
|
|
1277
1289
|
}
|
|
1278
|
-
document.getElementById(
|
|
1290
|
+
document.getElementById("LLM_model").value = constants.LLMModel;
|
|
1279
1291
|
|
|
1280
1292
|
// aria mode
|
|
1281
|
-
if (constants.ariaMode ==
|
|
1282
|
-
document.getElementById(
|
|
1283
|
-
document.getElementById(
|
|
1293
|
+
if (constants.ariaMode == "assertive") {
|
|
1294
|
+
document.getElementById("aria_mode_assertive").checked = true;
|
|
1295
|
+
document.getElementById("aria_mode_polite").checked = false;
|
|
1284
1296
|
} else {
|
|
1285
|
-
document.getElementById(
|
|
1286
|
-
document.getElementById(
|
|
1297
|
+
document.getElementById("aria_mode_polite").checked = true;
|
|
1298
|
+
document.getElementById("aria_mode_assertive").checked = false;
|
|
1287
1299
|
}
|
|
1288
1300
|
// hide either openai or gemini auth key field
|
|
1289
|
-
if (constants.LLMModel ==
|
|
1301
|
+
if (constants.LLMModel == "openai") {
|
|
1290
1302
|
document
|
|
1291
|
-
.getElementById(
|
|
1292
|
-
.classList.remove(
|
|
1303
|
+
.getElementById("openai_auth_key_container")
|
|
1304
|
+
.classList.remove("hidden");
|
|
1293
1305
|
document
|
|
1294
|
-
.getElementById(
|
|
1295
|
-
.classList.add(
|
|
1296
|
-
} else if (constants.LLMModel ==
|
|
1306
|
+
.getElementById("gemini_auth_key_container")
|
|
1307
|
+
.classList.add("hidden");
|
|
1308
|
+
} else if (constants.LLMModel == "gemini") {
|
|
1297
1309
|
document
|
|
1298
|
-
.getElementById(
|
|
1299
|
-
.classList.add(
|
|
1310
|
+
.getElementById("openai_auth_key_container")
|
|
1311
|
+
.classList.add("hidden");
|
|
1300
1312
|
document
|
|
1301
|
-
.getElementById(
|
|
1302
|
-
.classList.remove(
|
|
1303
|
-
} else if (constants.LLMModel ==
|
|
1313
|
+
.getElementById("gemini_auth_key_container")
|
|
1314
|
+
.classList.remove("hidden");
|
|
1315
|
+
} else if (constants.LLMModel == "multi") {
|
|
1304
1316
|
// multi LLM mode
|
|
1305
1317
|
document
|
|
1306
|
-
.getElementById(
|
|
1307
|
-
.classList.remove(
|
|
1318
|
+
.getElementById("openai_auth_key_container")
|
|
1319
|
+
.classList.remove("hidden");
|
|
1308
1320
|
document
|
|
1309
|
-
.getElementById(
|
|
1310
|
-
.classList.remove(
|
|
1321
|
+
.getElementById("gemini_auth_key_container")
|
|
1322
|
+
.classList.remove("hidden");
|
|
1311
1323
|
document
|
|
1312
|
-
.getElementById(
|
|
1313
|
-
.classList.remove(
|
|
1324
|
+
.getElementById("openai_multi_container")
|
|
1325
|
+
.classList.remove("hidden");
|
|
1314
1326
|
document
|
|
1315
|
-
.getElementById(
|
|
1316
|
-
.classList.remove(
|
|
1317
|
-
document.getElementById(
|
|
1327
|
+
.getElementById("gemini_multi_container")
|
|
1328
|
+
.classList.remove("hidden");
|
|
1329
|
+
document.getElementById("openai_multi").checked = false;
|
|
1318
1330
|
if (constants.LLMOpenAiMulti) {
|
|
1319
|
-
document.getElementById(
|
|
1331
|
+
document.getElementById("openai_multi").checked = true;
|
|
1320
1332
|
}
|
|
1321
|
-
document.getElementById(
|
|
1333
|
+
document.getElementById("gemini_multi").checked = false;
|
|
1322
1334
|
if (constants.LLMGeminiMulti) {
|
|
1323
|
-
document.getElementById(
|
|
1335
|
+
document.getElementById("gemini_multi").checked = true;
|
|
1324
1336
|
}
|
|
1325
1337
|
}
|
|
1326
1338
|
// skill level other
|
|
1327
|
-
if (constants.skillLevel ==
|
|
1339
|
+
if (constants.skillLevel == "other") {
|
|
1328
1340
|
document
|
|
1329
|
-
.getElementById(
|
|
1330
|
-
.classList.remove(
|
|
1341
|
+
.getElementById("skill_level_other_container")
|
|
1342
|
+
.classList.remove("hidden");
|
|
1331
1343
|
}
|
|
1332
1344
|
// LLM preferences
|
|
1333
1345
|
if (constants.LLMPreferences) {
|
|
1334
|
-
document.getElementById(
|
|
1346
|
+
document.getElementById("LLM_preferences").value =
|
|
1335
1347
|
constants.LLMPreferences;
|
|
1336
1348
|
}
|
|
1337
|
-
if (document.getElementById(
|
|
1338
|
-
document.getElementById(
|
|
1349
|
+
if (document.getElementById("LLM_reset_notification")) {
|
|
1350
|
+
document.getElementById("LLM_reset_notification").remove();
|
|
1339
1351
|
}
|
|
1340
1352
|
}
|
|
1341
1353
|
|
|
@@ -1346,33 +1358,33 @@ class Menu {
|
|
|
1346
1358
|
SaveData() {
|
|
1347
1359
|
let shouldReset = this.ShouldLLMReset();
|
|
1348
1360
|
|
|
1349
|
-
constants.vol = document.getElementById(
|
|
1350
|
-
constants.autoPlayRate = document.getElementById(
|
|
1361
|
+
constants.vol = document.getElementById("vol").value;
|
|
1362
|
+
constants.autoPlayRate = document.getElementById("autoplay_rate").value;
|
|
1351
1363
|
constants.brailleDisplayLength = document.getElementById(
|
|
1352
|
-
|
|
1364
|
+
"braille_display_length"
|
|
1353
1365
|
).value;
|
|
1354
|
-
constants.colorSelected = document.getElementById(
|
|
1355
|
-
constants.MIN_FREQUENCY = document.getElementById(
|
|
1356
|
-
constants.MAX_FREQUENCY = document.getElementById(
|
|
1366
|
+
constants.colorSelected = document.getElementById("color_selected").value;
|
|
1367
|
+
constants.MIN_FREQUENCY = document.getElementById("min_freq").value;
|
|
1368
|
+
constants.MAX_FREQUENCY = document.getElementById("max_freq").value;
|
|
1357
1369
|
constants.keypressInterval =
|
|
1358
|
-
document.getElementById(
|
|
1370
|
+
document.getElementById("keypress_interval").value;
|
|
1359
1371
|
|
|
1360
|
-
constants.openAIAuthKey = document.getElementById(
|
|
1361
|
-
constants.geminiAuthKey = document.getElementById(
|
|
1362
|
-
constants.skillLevel = document.getElementById(
|
|
1372
|
+
constants.openAIAuthKey = document.getElementById("openai_auth_key").value;
|
|
1373
|
+
constants.geminiAuthKey = document.getElementById("gemini_auth_key").value;
|
|
1374
|
+
constants.skillLevel = document.getElementById("skill_level").value;
|
|
1363
1375
|
constants.skillLevelOther =
|
|
1364
|
-
document.getElementById(
|
|
1365
|
-
constants.LLMModel = document.getElementById(
|
|
1366
|
-
constants.LLMPreferences = document.getElementById(
|
|
1367
|
-
constants.LLMOpenAiMulti = document.getElementById(
|
|
1368
|
-
constants.LLMGeminiMulti = document.getElementById(
|
|
1369
|
-
constants.autoInitLLM = document.getElementById(
|
|
1376
|
+
document.getElementById("skill_level_other").value;
|
|
1377
|
+
constants.LLMModel = document.getElementById("LLM_model").value;
|
|
1378
|
+
constants.LLMPreferences = document.getElementById("LLM_preferences").value;
|
|
1379
|
+
constants.LLMOpenAiMulti = document.getElementById("openai_multi").checked;
|
|
1380
|
+
constants.LLMGeminiMulti = document.getElementById("gemini_multi").checked;
|
|
1381
|
+
constants.autoInitLLM = document.getElementById("init_llm_on_load").checked;
|
|
1370
1382
|
|
|
1371
1383
|
// aria
|
|
1372
|
-
if (document.getElementById(
|
|
1373
|
-
constants.ariaMode =
|
|
1374
|
-
} else if (document.getElementById(
|
|
1375
|
-
constants.ariaMode =
|
|
1384
|
+
if (document.getElementById("aria_mode_assertive").checked) {
|
|
1385
|
+
constants.ariaMode = "assertive";
|
|
1386
|
+
} else if (document.getElementById("aria_mode_polite").checked) {
|
|
1387
|
+
constants.ariaMode = "polite";
|
|
1376
1388
|
}
|
|
1377
1389
|
|
|
1378
1390
|
this.SaveDataToLocalStorage();
|
|
@@ -1391,29 +1403,29 @@ class Menu {
|
|
|
1391
1403
|
*/
|
|
1392
1404
|
UpdateHtml() {
|
|
1393
1405
|
// set aria attributes
|
|
1394
|
-
constants.infoDiv.setAttribute(
|
|
1406
|
+
constants.infoDiv.setAttribute("aria-live", constants.ariaMode);
|
|
1395
1407
|
document
|
|
1396
1408
|
.getElementById(constants.announcement_container_id)
|
|
1397
|
-
.setAttribute(
|
|
1409
|
+
.setAttribute("aria-live", constants.ariaMode);
|
|
1398
1410
|
|
|
1399
|
-
document.getElementById(
|
|
1400
|
-
const scatter = document.getElementsByClassName(
|
|
1401
|
-
const heatmap = document.getElementById(
|
|
1402
|
-
const line = document.getElementById(
|
|
1411
|
+
document.getElementById("init_llm_on_load").checked = constants.autoInitLLM;
|
|
1412
|
+
const scatter = document.getElementsByClassName("highlight_point");
|
|
1413
|
+
const heatmap = document.getElementById("highlight_rect");
|
|
1414
|
+
const line = document.getElementById("highlight_point");
|
|
1403
1415
|
|
|
1404
1416
|
if (scatter !== null && scatter.length > 0) {
|
|
1405
1417
|
for (let i = 0; i < scatter.length; i++) {
|
|
1406
|
-
scatter[i].setAttribute(
|
|
1407
|
-
scatter[i].setAttribute(
|
|
1418
|
+
scatter[i].setAttribute("stroke", constants.colorSelected);
|
|
1419
|
+
scatter[i].setAttribute("fill", constants.colorSelected);
|
|
1408
1420
|
}
|
|
1409
1421
|
}
|
|
1410
1422
|
|
|
1411
1423
|
if (heatmap !== null) {
|
|
1412
|
-
heatmap.setAttribute(
|
|
1424
|
+
heatmap.setAttribute("stroke", constants.colorSelected);
|
|
1413
1425
|
}
|
|
1414
1426
|
|
|
1415
1427
|
if (line !== null) {
|
|
1416
|
-
line.setAttribute(
|
|
1428
|
+
line.setAttribute("stroke", constants.colorSelected);
|
|
1417
1429
|
}
|
|
1418
1430
|
}
|
|
1419
1431
|
|
|
@@ -1425,19 +1437,19 @@ class Menu {
|
|
|
1425
1437
|
let html =
|
|
1426
1438
|
'<p id="LLM_reset_notification">Note: Changes in LLM settings will reset any existing conversation.</p>';
|
|
1427
1439
|
|
|
1428
|
-
if (document.getElementById(
|
|
1429
|
-
document.getElementById(
|
|
1440
|
+
if (document.getElementById("LLM_reset_notification")) {
|
|
1441
|
+
document.getElementById("LLM_reset_notification").remove();
|
|
1430
1442
|
}
|
|
1431
1443
|
document
|
|
1432
|
-
.getElementById(
|
|
1433
|
-
.parentElement.insertAdjacentHTML(
|
|
1444
|
+
.getElementById("save_and_close_menu")
|
|
1445
|
+
.parentElement.insertAdjacentHTML("afterend", html);
|
|
1434
1446
|
|
|
1435
1447
|
// add to aria button text
|
|
1436
1448
|
document
|
|
1437
|
-
.getElementById(
|
|
1449
|
+
.getElementById("save_and_close_menu")
|
|
1438
1450
|
.setAttribute(
|
|
1439
|
-
|
|
1440
|
-
|
|
1451
|
+
"aria-labelledby",
|
|
1452
|
+
"save_and_close_text LLM_reset_notification"
|
|
1441
1453
|
);
|
|
1442
1454
|
}
|
|
1443
1455
|
/**
|
|
@@ -1449,29 +1461,29 @@ class Menu {
|
|
|
1449
1461
|
let shouldReset = false;
|
|
1450
1462
|
if (
|
|
1451
1463
|
!shouldReset &&
|
|
1452
|
-
constants.skillLevel != document.getElementById(
|
|
1464
|
+
constants.skillLevel != document.getElementById("skill_level").value
|
|
1453
1465
|
) {
|
|
1454
1466
|
shouldReset = true;
|
|
1455
1467
|
}
|
|
1456
1468
|
if (
|
|
1457
1469
|
!shouldReset &&
|
|
1458
1470
|
constants.LLMPreferences !=
|
|
1459
|
-
document.getElementById(
|
|
1471
|
+
document.getElementById("LLM_preferences").value
|
|
1460
1472
|
) {
|
|
1461
1473
|
shouldReset = true;
|
|
1462
1474
|
}
|
|
1463
1475
|
if (
|
|
1464
1476
|
!shouldReset &&
|
|
1465
|
-
constants.LLMModel != document.getElementById(
|
|
1477
|
+
constants.LLMModel != document.getElementById("LLM_model").value
|
|
1466
1478
|
) {
|
|
1467
1479
|
shouldReset = true;
|
|
1468
1480
|
}
|
|
1469
1481
|
if (
|
|
1470
1482
|
!shouldReset &&
|
|
1471
1483
|
(constants.LLMOpenAiMulti !=
|
|
1472
|
-
document.getElementById(
|
|
1484
|
+
document.getElementById("openai_multi").checked ||
|
|
1473
1485
|
constants.LLMGeminiMulti !=
|
|
1474
|
-
document.getElementById(
|
|
1486
|
+
document.getElementById("gemini_multi").checked)
|
|
1475
1487
|
) {
|
|
1476
1488
|
shouldReset = true;
|
|
1477
1489
|
}
|
|
@@ -1489,23 +1501,23 @@ class Menu {
|
|
|
1489
1501
|
data[constants.userSettingsKeys[i]] =
|
|
1490
1502
|
constants[constants.userSettingsKeys[i]];
|
|
1491
1503
|
}
|
|
1492
|
-
localStorage.setItem(
|
|
1504
|
+
localStorage.setItem("settings_data", JSON.stringify(data));
|
|
1493
1505
|
|
|
1494
1506
|
// also save to tracking if we're doing that
|
|
1495
1507
|
if (constants.isTracking) {
|
|
1496
1508
|
// but not auth keys
|
|
1497
|
-
data.openAIAuthKey =
|
|
1498
|
-
data.geminiAuthKey =
|
|
1509
|
+
data.openAIAuthKey = "hidden";
|
|
1510
|
+
data.geminiAuthKey = "hidden";
|
|
1499
1511
|
// and need a timestamp
|
|
1500
1512
|
data.timestamp = new Date().toISOString();
|
|
1501
|
-
tracker.SetData(
|
|
1513
|
+
tracker.SetData("settings", data);
|
|
1502
1514
|
}
|
|
1503
1515
|
}
|
|
1504
1516
|
/**
|
|
1505
1517
|
* Loads data from 'settings_data' localStorage, and updates contants variables
|
|
1506
1518
|
*/
|
|
1507
1519
|
LoadDataFromLocalStorage() {
|
|
1508
|
-
let data = JSON.parse(localStorage.getItem(
|
|
1520
|
+
let data = JSON.parse(localStorage.getItem("settings_data"));
|
|
1509
1521
|
if (data) {
|
|
1510
1522
|
for (let i = 0; i < constants.userSettingsKeys.length; i++) {
|
|
1511
1523
|
constants[constants.userSettingsKeys[i]] =
|
|
@@ -1531,9 +1543,9 @@ class ChatLLM {
|
|
|
1531
1543
|
if (constants.autoInitLLM) {
|
|
1532
1544
|
// only run if we have API keys set
|
|
1533
1545
|
if (
|
|
1534
|
-
(constants.LLMModel ==
|
|
1535
|
-
(constants.LLMModel ==
|
|
1536
|
-
(constants.LLMModel ==
|
|
1546
|
+
(constants.LLMModel == "openai" && constants.openAIAuthKey) ||
|
|
1547
|
+
(constants.LLMModel == "gemini" && constants.geminiAuthKey) ||
|
|
1548
|
+
(constants.LLMModel == "multi" &&
|
|
1537
1549
|
constants.openAIAuthKey &&
|
|
1538
1550
|
constants.geminiAuthKey)
|
|
1539
1551
|
) {
|
|
@@ -1588,7 +1600,7 @@ class ChatLLM {
|
|
|
1588
1600
|
</div>
|
|
1589
1601
|
<div id="chatLLM_modal_backdrop" class="modal-backdrop hidden"></div>
|
|
1590
1602
|
`;
|
|
1591
|
-
document.querySelector(
|
|
1603
|
+
document.querySelector("body").insertAdjacentHTML("beforeend", html);
|
|
1592
1604
|
}
|
|
1593
1605
|
|
|
1594
1606
|
/**
|
|
@@ -1597,21 +1609,21 @@ class ChatLLM {
|
|
|
1597
1609
|
*/
|
|
1598
1610
|
SetEvents() {
|
|
1599
1611
|
// chatLLM close events
|
|
1600
|
-
let allClose = document.querySelectorAll(
|
|
1612
|
+
let allClose = document.querySelectorAll("#close_chatLLM, #chatLLM .close");
|
|
1601
1613
|
for (let i = 0; i < allClose.length; i++) {
|
|
1602
1614
|
constants.events.push([
|
|
1603
1615
|
allClose[i],
|
|
1604
|
-
|
|
1616
|
+
"click",
|
|
1605
1617
|
function (e) {
|
|
1606
1618
|
chatLLM.Toggle(false);
|
|
1607
1619
|
},
|
|
1608
1620
|
]);
|
|
1609
1621
|
}
|
|
1610
1622
|
constants.events.push([
|
|
1611
|
-
document.getElementById(
|
|
1612
|
-
|
|
1623
|
+
document.getElementById("chatLLM"),
|
|
1624
|
+
"keyup",
|
|
1613
1625
|
function (e) {
|
|
1614
|
-
if (e.key ==
|
|
1626
|
+
if (e.key == "Esc") {
|
|
1615
1627
|
// esc
|
|
1616
1628
|
chatLLM.Toggle(false);
|
|
1617
1629
|
}
|
|
@@ -1621,9 +1633,9 @@ class ChatLLM {
|
|
|
1621
1633
|
// ChatLLM open/close toggle
|
|
1622
1634
|
constants.events.push([
|
|
1623
1635
|
document,
|
|
1624
|
-
|
|
1636
|
+
"keyup",
|
|
1625
1637
|
function (e) {
|
|
1626
|
-
if ((e.key ==
|
|
1638
|
+
if ((e.key == "?" && (e.ctrlKey || e.metaKey)) || e.key == "¿") {
|
|
1627
1639
|
chatLLM.Toggle();
|
|
1628
1640
|
}
|
|
1629
1641
|
},
|
|
@@ -1631,21 +1643,21 @@ class ChatLLM {
|
|
|
1631
1643
|
|
|
1632
1644
|
// ChatLLM request events
|
|
1633
1645
|
constants.events.push([
|
|
1634
|
-
document.getElementById(
|
|
1635
|
-
|
|
1646
|
+
document.getElementById("chatLLM_submit"),
|
|
1647
|
+
"click",
|
|
1636
1648
|
function (e) {
|
|
1637
|
-
let text = document.getElementById(
|
|
1638
|
-
chatLLM.DisplayChatMessage(
|
|
1649
|
+
let text = document.getElementById("chatLLM_input").value;
|
|
1650
|
+
chatLLM.DisplayChatMessage("User", text);
|
|
1639
1651
|
chatLLM.Submit(text);
|
|
1640
1652
|
},
|
|
1641
1653
|
]);
|
|
1642
1654
|
constants.events.push([
|
|
1643
|
-
document.getElementById(
|
|
1644
|
-
|
|
1655
|
+
document.getElementById("chatLLM_input"),
|
|
1656
|
+
"keyup",
|
|
1645
1657
|
function (e) {
|
|
1646
|
-
if (e.key ==
|
|
1647
|
-
let text = document.getElementById(
|
|
1648
|
-
chatLLM.DisplayChatMessage(
|
|
1658
|
+
if (e.key == "Enter" && !e.shiftKey) {
|
|
1659
|
+
let text = document.getElementById("chatLLM_input").value;
|
|
1660
|
+
chatLLM.DisplayChatMessage("User", text);
|
|
1649
1661
|
chatLLM.Submit(text);
|
|
1650
1662
|
}
|
|
1651
1663
|
},
|
|
@@ -1654,15 +1666,15 @@ class ChatLLM {
|
|
|
1654
1666
|
// ChatLLM suggestion events
|
|
1655
1667
|
// actual suggestions:
|
|
1656
1668
|
let suggestions = document.querySelectorAll(
|
|
1657
|
-
|
|
1669
|
+
"#chatLLM .LLM_suggestions button:not(#more_suggestions)"
|
|
1658
1670
|
);
|
|
1659
1671
|
for (let i = 0; i < suggestions.length; i++) {
|
|
1660
1672
|
constants.events.push([
|
|
1661
1673
|
suggestions[i],
|
|
1662
|
-
|
|
1674
|
+
"click",
|
|
1663
1675
|
function (e) {
|
|
1664
1676
|
let text = e.target.innerHTML;
|
|
1665
|
-
chatLLM.DisplayChatMessage(
|
|
1677
|
+
chatLLM.DisplayChatMessage("User", text);
|
|
1666
1678
|
chatLLM.Submit(text);
|
|
1667
1679
|
},
|
|
1668
1680
|
]);
|
|
@@ -1670,24 +1682,24 @@ class ChatLLM {
|
|
|
1670
1682
|
|
|
1671
1683
|
// Delete OpenAI and Gemini keys
|
|
1672
1684
|
constants.events.push([
|
|
1673
|
-
document.getElementById(
|
|
1674
|
-
|
|
1685
|
+
document.getElementById("delete_openai_key"),
|
|
1686
|
+
"click",
|
|
1675
1687
|
function (e) {
|
|
1676
|
-
document.getElementById(
|
|
1688
|
+
document.getElementById("openai_auth_key").value = "";
|
|
1677
1689
|
},
|
|
1678
1690
|
]);
|
|
1679
1691
|
constants.events.push([
|
|
1680
|
-
document.getElementById(
|
|
1681
|
-
|
|
1692
|
+
document.getElementById("delete_gemini_key"),
|
|
1693
|
+
"click",
|
|
1682
1694
|
function (e) {
|
|
1683
|
-
document.getElementById(
|
|
1695
|
+
document.getElementById("gemini_auth_key").value = "";
|
|
1684
1696
|
},
|
|
1685
1697
|
]);
|
|
1686
1698
|
|
|
1687
1699
|
// Reset chatLLM
|
|
1688
1700
|
constants.events.push([
|
|
1689
|
-
document.getElementById(
|
|
1690
|
-
|
|
1701
|
+
document.getElementById("reset_chatLLM"),
|
|
1702
|
+
"click",
|
|
1691
1703
|
function (e) {
|
|
1692
1704
|
chatLLM.ResetLLM();
|
|
1693
1705
|
},
|
|
@@ -1695,15 +1707,15 @@ class ChatLLM {
|
|
|
1695
1707
|
|
|
1696
1708
|
// copy to clipboard
|
|
1697
1709
|
constants.events.push([
|
|
1698
|
-
document.getElementById(
|
|
1699
|
-
|
|
1710
|
+
document.getElementById("chatLLM"),
|
|
1711
|
+
"click",
|
|
1700
1712
|
function (e) {
|
|
1701
1713
|
chatLLM.CopyChatHistory(e);
|
|
1702
1714
|
},
|
|
1703
1715
|
]);
|
|
1704
1716
|
constants.events.push([
|
|
1705
|
-
document.getElementById(
|
|
1706
|
-
|
|
1717
|
+
document.getElementById("chatLLM"),
|
|
1718
|
+
"keyup",
|
|
1707
1719
|
function (e) {
|
|
1708
1720
|
chatLLM.CopyChatHistory(e);
|
|
1709
1721
|
},
|
|
@@ -1721,96 +1733,96 @@ class ChatLLM {
|
|
|
1721
1733
|
* @param {Event|undefined} e - The event that triggered the copy action. If undefined, the entire chat history is copied.
|
|
1722
1734
|
*/
|
|
1723
1735
|
CopyChatHistory(e) {
|
|
1724
|
-
let text =
|
|
1725
|
-
if (typeof e ==
|
|
1736
|
+
let text = "";
|
|
1737
|
+
if (typeof e == "undefined") {
|
|
1726
1738
|
// check for passthrough
|
|
1727
1739
|
// get html of the full chat history
|
|
1728
|
-
text = document.getElementById(
|
|
1729
|
-
} else if (e.type ==
|
|
1740
|
+
text = document.getElementById("chatLLM_chat_history").innerHTML;
|
|
1741
|
+
} else if (e.type == "click") {
|
|
1730
1742
|
// check for buttons
|
|
1731
|
-
if (e.target.id ==
|
|
1743
|
+
if (e.target.id == "chatLLM_copy_all") {
|
|
1732
1744
|
// get html of the full chat history
|
|
1733
|
-
text = document.getElementById(
|
|
1734
|
-
} else if (e.target.classList.contains(
|
|
1745
|
+
text = document.getElementById("chatLLM_chat_history").innerHTML;
|
|
1746
|
+
} else if (e.target.classList.contains("chatLLM_message_copy_button")) {
|
|
1735
1747
|
// get the text of the element before the button
|
|
1736
|
-
text = e.target.closest(
|
|
1748
|
+
text = e.target.closest("p").previousElementSibling.innerHTML;
|
|
1737
1749
|
}
|
|
1738
|
-
} else if (e.type ==
|
|
1750
|
+
} else if (e.type == "keyup") {
|
|
1739
1751
|
// check for alt shift c or ctrl shift c
|
|
1740
|
-
if (e.key ==
|
|
1752
|
+
if (e.key == "C" && (e.ctrlKey || e.metaKey || e.altKey) && e.shiftKey) {
|
|
1741
1753
|
e.preventDefault();
|
|
1742
1754
|
// get the last message
|
|
1743
1755
|
let elem = document.querySelector(
|
|
1744
|
-
|
|
1756
|
+
"#chatLLM_chat_history > .chatLLM_message_other:last-of-type"
|
|
1745
1757
|
);
|
|
1746
1758
|
if (elem) {
|
|
1747
1759
|
text = elem.innerHTML;
|
|
1748
1760
|
}
|
|
1749
1761
|
} else if (
|
|
1750
|
-
e.key ==
|
|
1762
|
+
e.key == "A" &&
|
|
1751
1763
|
(e.ctrlKey || e.metaKey || e.altKey) &&
|
|
1752
1764
|
e.shiftKey
|
|
1753
1765
|
) {
|
|
1754
1766
|
e.preventDefault();
|
|
1755
1767
|
// get html of the full chat history
|
|
1756
|
-
text = document.getElementById(
|
|
1768
|
+
text = document.getElementById("chatLLM_chat_history").innerHTML;
|
|
1757
1769
|
}
|
|
1758
1770
|
}
|
|
1759
1771
|
|
|
1760
|
-
if (text ==
|
|
1772
|
+
if (text == "") {
|
|
1761
1773
|
return;
|
|
1762
1774
|
} else {
|
|
1763
1775
|
// clear the html, removing buttons etc
|
|
1764
|
-
let cleanElems = document.createElement(
|
|
1776
|
+
let cleanElems = document.createElement("div");
|
|
1765
1777
|
cleanElems.innerHTML = text;
|
|
1766
|
-
let removeThese = cleanElems.querySelectorAll(
|
|
1778
|
+
let removeThese = cleanElems.querySelectorAll(".chatLLM_message_copy");
|
|
1767
1779
|
removeThese.forEach((elem) => elem.remove());
|
|
1768
1780
|
|
|
1769
1781
|
// convert from html to markdown
|
|
1770
1782
|
let markdown = this.htmlToMarkdown(cleanElems);
|
|
1771
1783
|
// this messes up a bit with spacing, so kill more than 2 newlines in a row
|
|
1772
|
-
markdown = markdown.replace(/\n{3,}/g,
|
|
1784
|
+
markdown = markdown.replace(/\n{3,}/g, "\n\n");
|
|
1773
1785
|
|
|
1774
1786
|
try {
|
|
1775
1787
|
navigator.clipboard.writeText(markdown); // note: this fails if you're on the inspector. That's fine as it'll never happen to real users
|
|
1776
1788
|
} catch (err) {
|
|
1777
|
-
console.error(
|
|
1789
|
+
console.error("Failed to copy: ", err);
|
|
1778
1790
|
}
|
|
1779
1791
|
return markdown;
|
|
1780
1792
|
}
|
|
1781
1793
|
}
|
|
1782
1794
|
|
|
1783
1795
|
htmlToMarkdown(element) {
|
|
1784
|
-
let markdown =
|
|
1796
|
+
let markdown = "";
|
|
1785
1797
|
|
|
1786
1798
|
const convertElementToMarkdown = (element) => {
|
|
1787
1799
|
switch (element.tagName) {
|
|
1788
|
-
case
|
|
1800
|
+
case "H1":
|
|
1789
1801
|
return `# ${element.textContent}`;
|
|
1790
|
-
case
|
|
1802
|
+
case "H2":
|
|
1791
1803
|
return `## ${element.textContent}`;
|
|
1792
|
-
case
|
|
1804
|
+
case "H3":
|
|
1793
1805
|
return `### ${element.textContent}`;
|
|
1794
|
-
case
|
|
1806
|
+
case "H4":
|
|
1795
1807
|
return `#### ${element.textContent}`;
|
|
1796
|
-
case
|
|
1808
|
+
case "H5":
|
|
1797
1809
|
return `##### ${element.textContent}`;
|
|
1798
|
-
case
|
|
1810
|
+
case "H6":
|
|
1799
1811
|
return `###### ${element.textContent}`;
|
|
1800
|
-
case
|
|
1812
|
+
case "P":
|
|
1801
1813
|
return element.textContent;
|
|
1802
|
-
case
|
|
1814
|
+
case "DIV":
|
|
1803
1815
|
// For divs, process each child and add newlines as needed
|
|
1804
1816
|
return (
|
|
1805
1817
|
Array.from(element.childNodes)
|
|
1806
1818
|
.map((child) => convertElementToMarkdown(child))
|
|
1807
|
-
.join(
|
|
1819
|
+
.join("\n") + "\n\n"
|
|
1808
1820
|
);
|
|
1809
1821
|
default:
|
|
1810
1822
|
// For any other element, process its children recursively
|
|
1811
1823
|
return Array.from(element.childNodes)
|
|
1812
1824
|
.map((child) => convertElementToMarkdown(child))
|
|
1813
|
-
.join(
|
|
1825
|
+
.join("");
|
|
1814
1826
|
}
|
|
1815
1827
|
};
|
|
1816
1828
|
|
|
@@ -1818,7 +1830,7 @@ class ChatLLM {
|
|
|
1818
1830
|
markdown += convertElementToMarkdown(element);
|
|
1819
1831
|
} else if (
|
|
1820
1832
|
element.nodeType === Node.TEXT_NODE &&
|
|
1821
|
-
element.textContent.trim() !==
|
|
1833
|
+
element.textContent.trim() !== ""
|
|
1822
1834
|
) {
|
|
1823
1835
|
markdown += element.textContent.trim();
|
|
1824
1836
|
}
|
|
@@ -1843,14 +1855,14 @@ class ChatLLM {
|
|
|
1843
1855
|
|
|
1844
1856
|
// 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
|
|
1845
1857
|
if (
|
|
1846
|
-
(this.firstOpen || constants.LLMModel ==
|
|
1858
|
+
(this.firstOpen || constants.LLMModel == "gemini") &&
|
|
1847
1859
|
!firsttime &&
|
|
1848
1860
|
constants.verboseText.length > 0
|
|
1849
1861
|
) {
|
|
1850
1862
|
text =
|
|
1851
1863
|
"Here is the current position in the chart; no response necessarily needed, use this info only if it's relevant to future questions: " +
|
|
1852
1864
|
constants.verboseText +
|
|
1853
|
-
|
|
1865
|
+
". My question is: " +
|
|
1854
1866
|
text;
|
|
1855
1867
|
|
|
1856
1868
|
this.firstOpen = false;
|
|
@@ -1861,15 +1873,15 @@ class ChatLLM {
|
|
|
1861
1873
|
this.WaitingSound(true);
|
|
1862
1874
|
}
|
|
1863
1875
|
|
|
1864
|
-
if (constants.LLMOpenAiMulti || constants.LLMModel ==
|
|
1876
|
+
if (constants.LLMOpenAiMulti || constants.LLMModel == "openai") {
|
|
1865
1877
|
if (firsttime) {
|
|
1866
|
-
img = await this.ConvertSVGtoJPG(singleMaidr.id,
|
|
1878
|
+
img = await this.ConvertSVGtoJPG(singleMaidr.id, "openai");
|
|
1867
1879
|
}
|
|
1868
1880
|
chatLLM.OpenAIPrompt(text, img);
|
|
1869
1881
|
}
|
|
1870
|
-
if (constants.LLMGeminiMulti || constants.LLMModel ==
|
|
1882
|
+
if (constants.LLMGeminiMulti || constants.LLMModel == "gemini") {
|
|
1871
1883
|
if (firsttime) {
|
|
1872
|
-
img = await this.ConvertSVGtoJPG(singleMaidr.id,
|
|
1884
|
+
img = await this.ConvertSVGtoJPG(singleMaidr.id, "gemini");
|
|
1873
1885
|
}
|
|
1874
1886
|
chatLLM.GeminiPrompt(text, img);
|
|
1875
1887
|
}
|
|
@@ -1930,7 +1942,7 @@ class ChatLLM {
|
|
|
1930
1942
|
}, 30000);
|
|
1931
1943
|
|
|
1932
1944
|
// set queue for multi
|
|
1933
|
-
if (constants.LLMModel !=
|
|
1945
|
+
if (constants.LLMModel != "multi") {
|
|
1934
1946
|
constants.waitingQueue = 1;
|
|
1935
1947
|
} else {
|
|
1936
1948
|
constants.waitingQueue = 0;
|
|
@@ -1962,7 +1974,7 @@ class ChatLLM {
|
|
|
1962
1974
|
// get name from resource
|
|
1963
1975
|
let LLMName = resources.GetString(constants.LLMModel);
|
|
1964
1976
|
this.firstTime = false;
|
|
1965
|
-
this.DisplayChatMessage(LLMName, resources.GetString(
|
|
1977
|
+
this.DisplayChatMessage(LLMName, resources.GetString("processing"), true);
|
|
1966
1978
|
let defaultPrompt = this.GetDefaultPrompt();
|
|
1967
1979
|
this.Submit(defaultPrompt, true);
|
|
1968
1980
|
}
|
|
@@ -1974,35 +1986,35 @@ class ChatLLM {
|
|
|
1974
1986
|
*/
|
|
1975
1987
|
ProcessLLMResponse(data, model) {
|
|
1976
1988
|
chatLLM.WaitingSound(false);
|
|
1977
|
-
console.log(
|
|
1978
|
-
let text =
|
|
1989
|
+
console.log("LLM response: ", data);
|
|
1990
|
+
let text = "";
|
|
1979
1991
|
let LLMName = resources.GetString(model);
|
|
1980
1992
|
|
|
1981
|
-
if (model ==
|
|
1993
|
+
if (model == "openai") {
|
|
1982
1994
|
text = data.choices[0].message.content;
|
|
1983
1995
|
let i = this.requestJson.messages.length;
|
|
1984
1996
|
this.requestJson.messages[i] = {};
|
|
1985
|
-
this.requestJson.messages[i].role =
|
|
1997
|
+
this.requestJson.messages[i].role = "assistant";
|
|
1986
1998
|
this.requestJson.messages[i].content = text;
|
|
1987
1999
|
|
|
1988
2000
|
if (data.error) {
|
|
1989
|
-
chatLLM.DisplayChatMessage(LLMName,
|
|
2001
|
+
chatLLM.DisplayChatMessage(LLMName, "Error processing request.", true);
|
|
1990
2002
|
chatLLM.WaitingSound(false);
|
|
1991
2003
|
} else {
|
|
1992
2004
|
chatLLM.DisplayChatMessage(LLMName, text);
|
|
1993
2005
|
}
|
|
1994
|
-
} else if (model ==
|
|
2006
|
+
} else if (model == "gemini") {
|
|
1995
2007
|
if (data.text()) {
|
|
1996
2008
|
text = data.text();
|
|
1997
2009
|
chatLLM.DisplayChatMessage(LLMName, text);
|
|
1998
2010
|
} else {
|
|
1999
2011
|
if (!data.error) {
|
|
2000
|
-
data.error =
|
|
2012
|
+
data.error = "Error processing request.";
|
|
2001
2013
|
chatLLM.WaitingSound(false);
|
|
2002
2014
|
}
|
|
2003
2015
|
}
|
|
2004
2016
|
if (data.error) {
|
|
2005
|
-
chatLLM.DisplayChatMessage(LLMName,
|
|
2017
|
+
chatLLM.DisplayChatMessage(LLMName, "Error processing request.", true);
|
|
2006
2018
|
chatLLM.WaitingSound(false);
|
|
2007
2019
|
} else {
|
|
2008
2020
|
// todo: display actual response
|
|
@@ -2012,7 +2024,7 @@ class ChatLLM {
|
|
|
2012
2024
|
// if we're tracking, log the data
|
|
2013
2025
|
if (constants.isTracking) {
|
|
2014
2026
|
let chatHist = chatLLM.CopyChatHistory();
|
|
2015
|
-
tracker.SetData(
|
|
2027
|
+
tracker.SetData("ChatHistory", chatHist);
|
|
2016
2028
|
}
|
|
2017
2029
|
}
|
|
2018
2030
|
|
|
@@ -2026,11 +2038,11 @@ class ChatLLM {
|
|
|
2026
2038
|
if (this.requestJson.messages.length > 2) {
|
|
2027
2039
|
// subsequent responses
|
|
2028
2040
|
responseText = {
|
|
2029
|
-
id:
|
|
2030
|
-
object:
|
|
2041
|
+
id: "chatcmpl-8Y44iRCRrohYbAqm8rfBbJqTUADC7",
|
|
2042
|
+
object: "chat.completion",
|
|
2031
2043
|
created: 1703129508,
|
|
2032
2044
|
//model: 'gpt-4-1106-vision-preview',
|
|
2033
|
-
model:
|
|
2045
|
+
model: "gpt4-o",
|
|
2034
2046
|
usage: {
|
|
2035
2047
|
prompt_tokens: 451,
|
|
2036
2048
|
completion_tokens: 16,
|
|
@@ -2039,10 +2051,10 @@ class ChatLLM {
|
|
|
2039
2051
|
choices: [
|
|
2040
2052
|
{
|
|
2041
2053
|
message: {
|
|
2042
|
-
role:
|
|
2043
|
-
content:
|
|
2054
|
+
role: "assistant",
|
|
2055
|
+
content: "A fake response from the LLM. Nice.",
|
|
2044
2056
|
},
|
|
2045
|
-
finish_reason:
|
|
2057
|
+
finish_reason: "length",
|
|
2046
2058
|
index: 0,
|
|
2047
2059
|
},
|
|
2048
2060
|
],
|
|
@@ -2050,10 +2062,10 @@ class ChatLLM {
|
|
|
2050
2062
|
} else {
|
|
2051
2063
|
// first response
|
|
2052
2064
|
responseText = {
|
|
2053
|
-
id:
|
|
2054
|
-
object:
|
|
2065
|
+
id: "chatcmpl-8Y44iRCRrohYbAqm8rfBbJqTUADC7",
|
|
2066
|
+
object: "chat.completion",
|
|
2055
2067
|
created: 1703129508,
|
|
2056
|
-
model:
|
|
2068
|
+
model: "gpt-4-1106-vision-preview",
|
|
2057
2069
|
usage: {
|
|
2058
2070
|
prompt_tokens: 451,
|
|
2059
2071
|
completion_tokens: 16,
|
|
@@ -2062,11 +2074,11 @@ class ChatLLM {
|
|
|
2062
2074
|
choices: [
|
|
2063
2075
|
{
|
|
2064
2076
|
message: {
|
|
2065
|
-
role:
|
|
2077
|
+
role: "assistant",
|
|
2066
2078
|
content:
|
|
2067
|
-
|
|
2079
|
+
"The chart you're referring to is a bar graph titled \"The Number of Diamonds",
|
|
2068
2080
|
},
|
|
2069
|
-
finish_reason:
|
|
2081
|
+
finish_reason: "length",
|
|
2070
2082
|
index: 0,
|
|
2071
2083
|
},
|
|
2072
2084
|
],
|
|
@@ -2085,49 +2097,49 @@ class ChatLLM {
|
|
|
2085
2097
|
*/
|
|
2086
2098
|
OpenAIPrompt(text, img = null) {
|
|
2087
2099
|
// request init
|
|
2088
|
-
let url =
|
|
2100
|
+
let url = "https://api.openai.com/v1/chat/completions";
|
|
2089
2101
|
let auth = constants.openAIAuthKey;
|
|
2090
2102
|
let requestJson = chatLLM.OpenAIJson(text, img);
|
|
2091
|
-
console.log(
|
|
2103
|
+
console.log("LLM request: ", requestJson);
|
|
2092
2104
|
|
|
2093
2105
|
fetch(url, {
|
|
2094
|
-
method:
|
|
2106
|
+
method: "POST",
|
|
2095
2107
|
headers: {
|
|
2096
|
-
|
|
2097
|
-
Authorization:
|
|
2108
|
+
"Content-Type": "application/json",
|
|
2109
|
+
Authorization: "Bearer " + auth,
|
|
2098
2110
|
},
|
|
2099
2111
|
body: JSON.stringify(requestJson),
|
|
2100
2112
|
})
|
|
2101
2113
|
.then((response) => response.json())
|
|
2102
2114
|
.then((data) => {
|
|
2103
|
-
chatLLM.ProcessLLMResponse(data,
|
|
2115
|
+
chatLLM.ProcessLLMResponse(data, "openai");
|
|
2104
2116
|
})
|
|
2105
2117
|
.catch((error) => {
|
|
2106
2118
|
chatLLM.WaitingSound(false);
|
|
2107
|
-
console.error(
|
|
2108
|
-
chatLLM.DisplayChatMessage(
|
|
2119
|
+
console.error("Error:", error);
|
|
2120
|
+
chatLLM.DisplayChatMessage("OpenAI", "Error processing request.", true);
|
|
2109
2121
|
// also todo: handle errors somehow
|
|
2110
2122
|
});
|
|
2111
2123
|
}
|
|
2112
2124
|
OpenAIJson(text, img = null) {
|
|
2113
2125
|
let sysMessage = constants.LLMSystemMessage;
|
|
2114
2126
|
let backupMessage =
|
|
2115
|
-
|
|
2127
|
+
"Describe " + singleMaidr.type + " charts to a blind person";
|
|
2116
2128
|
// headers and sys message
|
|
2117
2129
|
if (!this.requestJson) {
|
|
2118
2130
|
this.requestJson = {};
|
|
2119
2131
|
//this.requestJson.model = 'gpt-4-vision-preview';
|
|
2120
|
-
this.requestJson.model =
|
|
2132
|
+
this.requestJson.model = "gpt-4o-2024-08-06";
|
|
2121
2133
|
this.requestJson.max_tokens = constants.LLMmaxResponseTokens; // note: if this is too short (tested with less than 200), the response gets cut off
|
|
2122
2134
|
|
|
2123
2135
|
// sys message
|
|
2124
2136
|
this.requestJson.messages = [];
|
|
2125
2137
|
this.requestJson.messages[0] = {};
|
|
2126
|
-
this.requestJson.messages[0].role =
|
|
2138
|
+
this.requestJson.messages[0].role = "system";
|
|
2127
2139
|
this.requestJson.messages[0].content = sysMessage;
|
|
2128
2140
|
if (constants.LLMPreferences) {
|
|
2129
2141
|
this.requestJson.messages[1] = {};
|
|
2130
|
-
this.requestJson.messages[1].role =
|
|
2142
|
+
this.requestJson.messages[1].role = "system";
|
|
2131
2143
|
this.requestJson.messages[1].content = constants.LLMPreferences;
|
|
2132
2144
|
}
|
|
2133
2145
|
}
|
|
@@ -2136,16 +2148,16 @@ class ChatLLM {
|
|
|
2136
2148
|
// if we have an image (first time only), send the image and the text, otherwise just the text
|
|
2137
2149
|
let i = this.requestJson.messages.length;
|
|
2138
2150
|
this.requestJson.messages[i] = {};
|
|
2139
|
-
this.requestJson.messages[i].role =
|
|
2151
|
+
this.requestJson.messages[i].role = "user";
|
|
2140
2152
|
if (img) {
|
|
2141
2153
|
// first message, include the img
|
|
2142
2154
|
this.requestJson.messages[i].content = [
|
|
2143
2155
|
{
|
|
2144
|
-
type:
|
|
2156
|
+
type: "text",
|
|
2145
2157
|
text: text,
|
|
2146
2158
|
},
|
|
2147
2159
|
{
|
|
2148
|
-
type:
|
|
2160
|
+
type: "image_url",
|
|
2149
2161
|
image_url: { url: img },
|
|
2150
2162
|
},
|
|
2151
2163
|
];
|
|
@@ -2170,12 +2182,12 @@ class ChatLLM {
|
|
|
2170
2182
|
|
|
2171
2183
|
// Import the module
|
|
2172
2184
|
const { GoogleGenerativeAI } = await import(
|
|
2173
|
-
|
|
2185
|
+
"https://esm.run/@google/generative-ai"
|
|
2174
2186
|
);
|
|
2175
2187
|
const API_KEY = constants.geminiAuthKey;
|
|
2176
2188
|
const genAI = new GoogleGenerativeAI(API_KEY);
|
|
2177
2189
|
const model = genAI.getGenerativeModel({
|
|
2178
|
-
model:
|
|
2190
|
+
model: "gemini-1.5-pro-latest",
|
|
2179
2191
|
}); // old model was 'gemini-pro-vision'
|
|
2180
2192
|
|
|
2181
2193
|
// Create the prompt
|
|
@@ -2183,25 +2195,25 @@ class ChatLLM {
|
|
|
2183
2195
|
if (constants.LLMPreferences) {
|
|
2184
2196
|
prompt += constants.LLMPreferences;
|
|
2185
2197
|
}
|
|
2186
|
-
prompt +=
|
|
2198
|
+
prompt += "\n\n" + text; // Use the text parameter as the prompt
|
|
2187
2199
|
const image = {
|
|
2188
2200
|
inlineData: {
|
|
2189
2201
|
data: imgBase64, // Use the base64 image string
|
|
2190
|
-
mimeType:
|
|
2202
|
+
mimeType: "image/png", // Or the appropriate mime type of your image
|
|
2191
2203
|
},
|
|
2192
2204
|
};
|
|
2193
2205
|
|
|
2194
2206
|
// Generate the content
|
|
2195
|
-
console.log(
|
|
2207
|
+
console.log("LLM request: ", prompt, image);
|
|
2196
2208
|
const result = await model.generateContent([prompt, image]);
|
|
2197
2209
|
console.log(result.response.text());
|
|
2198
2210
|
|
|
2199
2211
|
// Process the response
|
|
2200
|
-
chatLLM.ProcessLLMResponse(result.response,
|
|
2212
|
+
chatLLM.ProcessLLMResponse(result.response, "gemini");
|
|
2201
2213
|
} catch (error) {
|
|
2202
2214
|
chatLLM.WaitingSound(false);
|
|
2203
|
-
chatLLM.DisplayChatMessage(
|
|
2204
|
-
console.error(
|
|
2215
|
+
chatLLM.DisplayChatMessage("Gemini", "Error processing request.", true);
|
|
2216
|
+
console.error("Error in GeminiPrompt:", error);
|
|
2205
2217
|
throw error; // Rethrow the error for further handling if necessary
|
|
2206
2218
|
}
|
|
2207
2219
|
}
|
|
@@ -2213,11 +2225,11 @@ class ChatLLM {
|
|
|
2213
2225
|
* @memberof module:constants
|
|
2214
2226
|
* @returns {void}
|
|
2215
2227
|
*/
|
|
2216
|
-
DisplayChatMessage(user =
|
|
2217
|
-
let hLevel =
|
|
2218
|
-
if (!isSystem && constants.LLMModel ==
|
|
2228
|
+
DisplayChatMessage(user = "User", text = "", isSystem = false) {
|
|
2229
|
+
let hLevel = "h3";
|
|
2230
|
+
if (!isSystem && constants.LLMModel == "multi" && user != "User") {
|
|
2219
2231
|
if (this.firstMulti) {
|
|
2220
|
-
let multiAIName = resources.GetString(
|
|
2232
|
+
let multiAIName = resources.GetString("multi");
|
|
2221
2233
|
let titleHtml = `
|
|
2222
2234
|
<div class="chatLLM_message chatLLM_message_other">
|
|
2223
2235
|
<h3 class="chatLLM_message_user">${multiAIName} Responses</h3>
|
|
@@ -2226,20 +2238,20 @@ class ChatLLM {
|
|
|
2226
2238
|
this.RenderChatMessage(titleHtml);
|
|
2227
2239
|
this.firstMulti = false;
|
|
2228
2240
|
}
|
|
2229
|
-
hLevel =
|
|
2241
|
+
hLevel = "h4";
|
|
2230
2242
|
}
|
|
2231
2243
|
let html = `
|
|
2232
2244
|
<div class="chatLLM_message ${
|
|
2233
|
-
user ==
|
|
2245
|
+
user == "User" ? "chatLLM_message_self" : "chatLLM_message_other"
|
|
2234
2246
|
}">`;
|
|
2235
|
-
if (text != resources.GetString(
|
|
2247
|
+
if (text != resources.GetString("processing")) {
|
|
2236
2248
|
html += `<${hLevel} class="chatLLM_message_user">${user}</${hLevel}>`;
|
|
2237
2249
|
}
|
|
2238
2250
|
html += `<p class="chatLLM_message_text">${text}</p>
|
|
2239
2251
|
</div>
|
|
2240
2252
|
`;
|
|
2241
2253
|
// add a copy button to actual messages
|
|
2242
|
-
if (user !=
|
|
2254
|
+
if (user != "User" && text != resources.GetString("processing")) {
|
|
2243
2255
|
html += `
|
|
2244
2256
|
<p class="chatLLM_message_copy"><button class="chatLLM_message_copy_button">Copy</button></p>
|
|
2245
2257
|
`;
|
|
@@ -2249,13 +2261,13 @@ class ChatLLM {
|
|
|
2249
2261
|
}
|
|
2250
2262
|
RenderChatMessage(html) {
|
|
2251
2263
|
document
|
|
2252
|
-
.getElementById(
|
|
2253
|
-
.insertAdjacentHTML(
|
|
2254
|
-
document.getElementById(
|
|
2264
|
+
.getElementById("chatLLM_chat_history")
|
|
2265
|
+
.insertAdjacentHTML("beforeend", html);
|
|
2266
|
+
document.getElementById("chatLLM_input").value = "";
|
|
2255
2267
|
|
|
2256
2268
|
// scroll to bottom
|
|
2257
|
-
document.getElementById(
|
|
2258
|
-
document.getElementById(
|
|
2269
|
+
document.getElementById("chatLLM_chat_history").scrollTop =
|
|
2270
|
+
document.getElementById("chatLLM_chat_history").scrollHeight;
|
|
2259
2271
|
}
|
|
2260
2272
|
|
|
2261
2273
|
/**
|
|
@@ -2263,7 +2275,7 @@ class ChatLLM {
|
|
|
2263
2275
|
*/
|
|
2264
2276
|
ResetLLM() {
|
|
2265
2277
|
// clear the main chat history
|
|
2266
|
-
document.getElementById(
|
|
2278
|
+
document.getElementById("chatLLM_chat_history").innerHTML = "";
|
|
2267
2279
|
|
|
2268
2280
|
// reset the data
|
|
2269
2281
|
this.requestJson = null;
|
|
@@ -2284,11 +2296,11 @@ class ChatLLM {
|
|
|
2284
2296
|
*/
|
|
2285
2297
|
Destroy() {
|
|
2286
2298
|
// chatLLM element destruction
|
|
2287
|
-
let chatLLM = document.getElementById(
|
|
2299
|
+
let chatLLM = document.getElementById("chatLLM");
|
|
2288
2300
|
if (chatLLM) {
|
|
2289
2301
|
chatLLM.remove();
|
|
2290
2302
|
}
|
|
2291
|
-
let backdrop = document.getElementById(
|
|
2303
|
+
let backdrop = document.getElementById("chatLLM_modal_backdrop");
|
|
2292
2304
|
if (backdrop) {
|
|
2293
2305
|
backdrop.remove();
|
|
2294
2306
|
}
|
|
@@ -2299,8 +2311,8 @@ class ChatLLM {
|
|
|
2299
2311
|
* @param {boolean} [onoff=false] - Whether to turn the chatLLM on or off. Defaults to false (close).
|
|
2300
2312
|
*/
|
|
2301
2313
|
Toggle(onoff) {
|
|
2302
|
-
if (typeof onoff ==
|
|
2303
|
-
if (document.getElementById(
|
|
2314
|
+
if (typeof onoff == "undefined") {
|
|
2315
|
+
if (document.getElementById("chatLLM").classList.contains("hidden")) {
|
|
2304
2316
|
onoff = true;
|
|
2305
2317
|
} else {
|
|
2306
2318
|
onoff = false;
|
|
@@ -2311,19 +2323,19 @@ class ChatLLM {
|
|
|
2311
2323
|
// open
|
|
2312
2324
|
this.whereWasMyFocus = document.activeElement;
|
|
2313
2325
|
constants.tabMovement = 0;
|
|
2314
|
-
document.getElementById(
|
|
2326
|
+
document.getElementById("chatLLM").classList.remove("hidden");
|
|
2315
2327
|
document
|
|
2316
|
-
.getElementById(
|
|
2317
|
-
.classList.remove(
|
|
2318
|
-
document.querySelector(
|
|
2328
|
+
.getElementById("chatLLM_modal_backdrop")
|
|
2329
|
+
.classList.remove("hidden");
|
|
2330
|
+
document.querySelector("#chatLLM .close").focus();
|
|
2319
2331
|
|
|
2320
2332
|
if (this.firstTime) {
|
|
2321
2333
|
this.InitChatMessage();
|
|
2322
2334
|
}
|
|
2323
2335
|
} else {
|
|
2324
2336
|
// close
|
|
2325
|
-
document.getElementById(
|
|
2326
|
-
document.getElementById(
|
|
2337
|
+
document.getElementById("chatLLM").classList.add("hidden");
|
|
2338
|
+
document.getElementById("chatLLM_modal_backdrop").classList.add("hidden");
|
|
2327
2339
|
this.whereWasMyFocus.focus();
|
|
2328
2340
|
this.whereWasMyFocus = null;
|
|
2329
2341
|
this.firstOpen = true;
|
|
@@ -2337,11 +2349,11 @@ class ChatLLM {
|
|
|
2337
2349
|
async ConvertSVGtoJPG(id, model) {
|
|
2338
2350
|
let svgElement = document.getElementById(id);
|
|
2339
2351
|
return new Promise((resolve, reject) => {
|
|
2340
|
-
var canvas = document.createElement(
|
|
2341
|
-
var ctx = canvas.getContext(
|
|
2352
|
+
var canvas = document.createElement("canvas");
|
|
2353
|
+
var ctx = canvas.getContext("2d");
|
|
2342
2354
|
|
|
2343
2355
|
var svgData = new XMLSerializer().serializeToString(svgElement);
|
|
2344
|
-
if (!svgData.startsWith(
|
|
2356
|
+
if (!svgData.startsWith("<svg xmlns")) {
|
|
2345
2357
|
svgData = `<svg xmlns="http://www.w3.org/2000/svg" ${svgData.slice(4)}`;
|
|
2346
2358
|
}
|
|
2347
2359
|
|
|
@@ -2353,11 +2365,11 @@ class ChatLLM {
|
|
|
2353
2365
|
var img = new Image();
|
|
2354
2366
|
img.onload = function () {
|
|
2355
2367
|
ctx.drawImage(img, 0, 0, svgSize.width, svgSize.height);
|
|
2356
|
-
var jpegData = canvas.toDataURL(
|
|
2357
|
-
if (model ==
|
|
2368
|
+
var jpegData = canvas.toDataURL("image/jpeg", 0.9); // 0.9 is the quality parameter
|
|
2369
|
+
if (model == "openai") {
|
|
2358
2370
|
resolve(jpegData);
|
|
2359
|
-
} else if (model ==
|
|
2360
|
-
let base64Data = jpegData.split(
|
|
2371
|
+
} else if (model == "gemini") {
|
|
2372
|
+
let base64Data = jpegData.split(",")[1];
|
|
2361
2373
|
resolve(base64Data);
|
|
2362
2374
|
//resolve(jpegData);
|
|
2363
2375
|
}
|
|
@@ -2365,11 +2377,11 @@ class ChatLLM {
|
|
|
2365
2377
|
};
|
|
2366
2378
|
|
|
2367
2379
|
img.onerror = function () {
|
|
2368
|
-
reject(new Error(
|
|
2380
|
+
reject(new Error("Error loading SVG"));
|
|
2369
2381
|
};
|
|
2370
2382
|
|
|
2371
2383
|
var svgBlob = new Blob([svgData], {
|
|
2372
|
-
type:
|
|
2384
|
+
type: "image/svg+xml;charset=utf-8",
|
|
2373
2385
|
});
|
|
2374
2386
|
var url = URL.createObjectURL(svgBlob);
|
|
2375
2387
|
img.src = url;
|
|
@@ -2382,25 +2394,25 @@ class ChatLLM {
|
|
|
2382
2394
|
* The prompt includes information about the blind person's skill level and the chart's image and raw data, if available.
|
|
2383
2395
|
*/
|
|
2384
2396
|
GetDefaultPrompt() {
|
|
2385
|
-
let text =
|
|
2397
|
+
let text = "Describe this chart to a blind person";
|
|
2386
2398
|
if (constants.skillLevel) {
|
|
2387
|
-
if (constants.skillLevel ==
|
|
2399
|
+
if (constants.skillLevel == "other" && constants.skillLevelOther) {
|
|
2388
2400
|
text +=
|
|
2389
|
-
|
|
2401
|
+
" who has a " +
|
|
2390
2402
|
constants.skillLevelOther +
|
|
2391
|
-
|
|
2403
|
+
" understanding of statistical charts. ";
|
|
2392
2404
|
} else {
|
|
2393
2405
|
text +=
|
|
2394
|
-
|
|
2406
|
+
" who has a " +
|
|
2395
2407
|
constants.skillLevel +
|
|
2396
|
-
|
|
2408
|
+
" understanding of statistical charts. ";
|
|
2397
2409
|
}
|
|
2398
2410
|
} else {
|
|
2399
|
-
text +=
|
|
2411
|
+
text += " who has a basic understanding of statistical charts. ";
|
|
2400
2412
|
}
|
|
2401
|
-
text +=
|
|
2413
|
+
text += "Here is a chart in image format";
|
|
2402
2414
|
if (singleMaidr) {
|
|
2403
|
-
text +=
|
|
2415
|
+
text += " and raw data in json format: \n";
|
|
2404
2416
|
text += JSON.stringify(singleMaidr);
|
|
2405
2417
|
}
|
|
2406
2418
|
|
|
@@ -2455,26 +2467,26 @@ class Description {
|
|
|
2455
2467
|
|
|
2456
2468
|
`;
|
|
2457
2469
|
|
|
2458
|
-
document.querySelector(
|
|
2470
|
+
document.querySelector("body").insertAdjacentHTML("beforeend", html);
|
|
2459
2471
|
|
|
2460
2472
|
// close events
|
|
2461
2473
|
let allClose = document.querySelectorAll(
|
|
2462
|
-
|
|
2474
|
+
"#close_desc, #description .close"
|
|
2463
2475
|
);
|
|
2464
2476
|
for (let i = 0; i < allClose.length; i++) {
|
|
2465
2477
|
constants.events.push([
|
|
2466
2478
|
allClose[i],
|
|
2467
|
-
|
|
2479
|
+
"click",
|
|
2468
2480
|
function (e) {
|
|
2469
2481
|
description.Toggle(false);
|
|
2470
2482
|
},
|
|
2471
2483
|
]);
|
|
2472
2484
|
}
|
|
2473
2485
|
constants.events.push([
|
|
2474
|
-
document.getElementById(
|
|
2475
|
-
|
|
2486
|
+
document.getElementById("description"),
|
|
2487
|
+
"keyup",
|
|
2476
2488
|
function (e) {
|
|
2477
|
-
if (e.key ==
|
|
2489
|
+
if (e.key == "Esc") {
|
|
2478
2490
|
// esc
|
|
2479
2491
|
description.Toggle(false);
|
|
2480
2492
|
}
|
|
@@ -2484,9 +2496,9 @@ class Description {
|
|
|
2484
2496
|
// open events
|
|
2485
2497
|
constants.events.push([
|
|
2486
2498
|
document,
|
|
2487
|
-
|
|
2499
|
+
"keyup",
|
|
2488
2500
|
function (e) {
|
|
2489
|
-
if (e.key ==
|
|
2501
|
+
if (e.key == "d") {
|
|
2490
2502
|
description.Toggle(true);
|
|
2491
2503
|
}
|
|
2492
2504
|
},
|
|
@@ -2498,11 +2510,11 @@ class Description {
|
|
|
2498
2510
|
*/
|
|
2499
2511
|
Destroy() {
|
|
2500
2512
|
// description element destruction
|
|
2501
|
-
let description = document.getElementById(
|
|
2513
|
+
let description = document.getElementById("menu");
|
|
2502
2514
|
if (description) {
|
|
2503
2515
|
description.remove();
|
|
2504
2516
|
}
|
|
2505
|
-
let backdrop = document.getElementById(
|
|
2517
|
+
let backdrop = document.getElementById("desc_modal_backdrop");
|
|
2506
2518
|
if (backdrop) {
|
|
2507
2519
|
backdrop.remove();
|
|
2508
2520
|
}
|
|
@@ -2513,8 +2525,8 @@ class Description {
|
|
|
2513
2525
|
* @param {boolean} [onoff=false] - Whether to turn the description element on or off.
|
|
2514
2526
|
*/
|
|
2515
2527
|
Toggle(onoff = false) {
|
|
2516
|
-
if (typeof onoff ==
|
|
2517
|
-
if (document.getElementById(
|
|
2528
|
+
if (typeof onoff == "undefined") {
|
|
2529
|
+
if (document.getElementById("description").classList.contains("hidden")) {
|
|
2518
2530
|
onoff = true;
|
|
2519
2531
|
} else {
|
|
2520
2532
|
onoff = false;
|
|
@@ -2525,13 +2537,13 @@ class Description {
|
|
|
2525
2537
|
this.whereWasMyFocus = document.activeElement;
|
|
2526
2538
|
constants.tabMovement = 0;
|
|
2527
2539
|
this.PopulateData();
|
|
2528
|
-
document.getElementById(
|
|
2529
|
-
document.getElementById(
|
|
2530
|
-
document.querySelector(
|
|
2540
|
+
document.getElementById("description").classList.remove("hidden");
|
|
2541
|
+
document.getElementById("desc_modal_backdrop").classList.remove("hidden");
|
|
2542
|
+
document.querySelector("#description .close").focus();
|
|
2531
2543
|
} else {
|
|
2532
2544
|
// close
|
|
2533
|
-
document.getElementById(
|
|
2534
|
-
document.getElementById(
|
|
2545
|
+
document.getElementById("description").classList.add("hidden");
|
|
2546
|
+
document.getElementById("desc_modal_backdrop").classList.add("hidden");
|
|
2535
2547
|
this.whereWasMyFocus.focus();
|
|
2536
2548
|
this.whereWasMyFocus = null;
|
|
2537
2549
|
}
|
|
@@ -2541,22 +2553,22 @@ class Description {
|
|
|
2541
2553
|
* Populates the data for the chart and table based on the chart type and plot data.
|
|
2542
2554
|
*/
|
|
2543
2555
|
PopulateData() {
|
|
2544
|
-
let descHtml =
|
|
2556
|
+
let descHtml = "";
|
|
2545
2557
|
|
|
2546
2558
|
// chart labels and descriptions
|
|
2547
|
-
let descType =
|
|
2548
|
-
if (constants.chartType ==
|
|
2549
|
-
descType =
|
|
2550
|
-
} else if (constants.chartType ==
|
|
2551
|
-
descType =
|
|
2552
|
-
} else if (constants.chartType ==
|
|
2553
|
-
descType =
|
|
2554
|
-
} else if (constants.chartType ==
|
|
2555
|
-
descType =
|
|
2556
|
-
} else if (constants.chartType ==
|
|
2557
|
-
descType =
|
|
2558
|
-
} else if (constants.chartType ==
|
|
2559
|
-
descType =
|
|
2559
|
+
let descType = "";
|
|
2560
|
+
if (constants.chartType == "bar") {
|
|
2561
|
+
descType = "Bar chart";
|
|
2562
|
+
} else if (constants.chartType == "heat") {
|
|
2563
|
+
descType = "Heatmap";
|
|
2564
|
+
} else if (constants.chartType == "box") {
|
|
2565
|
+
descType = "Box plot";
|
|
2566
|
+
} else if (constants.chartType == "scatter") {
|
|
2567
|
+
descType = "Scatter plot";
|
|
2568
|
+
} else if (constants.chartType == "line") {
|
|
2569
|
+
descType = "Line chart";
|
|
2570
|
+
} else if (constants.chartType == "hist") {
|
|
2571
|
+
descType = "Histogram";
|
|
2560
2572
|
}
|
|
2561
2573
|
|
|
2562
2574
|
if (descType) {
|
|
@@ -2573,7 +2585,7 @@ class Description {
|
|
|
2573
2585
|
}
|
|
2574
2586
|
|
|
2575
2587
|
// table of data, prep
|
|
2576
|
-
let descTableHtml =
|
|
2588
|
+
let descTableHtml = "";
|
|
2577
2589
|
let descLabelX = null;
|
|
2578
2590
|
let descLabelY = null;
|
|
2579
2591
|
let descTickX = null;
|
|
@@ -2583,7 +2595,7 @@ class Description {
|
|
|
2583
2595
|
let descNumColsWithLabels = 0;
|
|
2584
2596
|
let descNumRows = 0;
|
|
2585
2597
|
let descNumRowsWithLabels = 0;
|
|
2586
|
-
if (constants.chartType ==
|
|
2598
|
+
if (constants.chartType == "bar") {
|
|
2587
2599
|
if (plot.plotLegend.x != null) {
|
|
2588
2600
|
descLabelX = plot.plotLegend.x;
|
|
2589
2601
|
descNumColsWithLabels += 1;
|
|
@@ -2608,43 +2620,43 @@ class Description {
|
|
|
2608
2620
|
|
|
2609
2621
|
// table of data, create
|
|
2610
2622
|
if (descData != null) {
|
|
2611
|
-
descTableHtml +=
|
|
2623
|
+
descTableHtml += "<table>";
|
|
2612
2624
|
|
|
2613
2625
|
// header rows
|
|
2614
2626
|
if (descLabelX != null || descTickX != null) {
|
|
2615
|
-
descTableHtml +=
|
|
2627
|
+
descTableHtml += "<thead>";
|
|
2616
2628
|
if (descLabelX != null) {
|
|
2617
|
-
descTableHtml +=
|
|
2629
|
+
descTableHtml += "<tr>";
|
|
2618
2630
|
if (descLabelY != null) {
|
|
2619
|
-
descTableHtml +=
|
|
2631
|
+
descTableHtml += "<td></td>";
|
|
2620
2632
|
}
|
|
2621
2633
|
if (descTickY != null) {
|
|
2622
|
-
descTableHtml +=
|
|
2634
|
+
descTableHtml += "<td></td>";
|
|
2623
2635
|
}
|
|
2624
2636
|
descTableHtml += `<th scope="col" colspan="${descNumCols}">${descLabelX}</th>`;
|
|
2625
|
-
descTableHtml +=
|
|
2637
|
+
descTableHtml += "</tr>";
|
|
2626
2638
|
}
|
|
2627
2639
|
if (descTickX != null) {
|
|
2628
|
-
descTableHtml +=
|
|
2640
|
+
descTableHtml += "<tr>";
|
|
2629
2641
|
if (descLabelY != null) {
|
|
2630
|
-
descTableHtml +=
|
|
2642
|
+
descTableHtml += "<td></td>";
|
|
2631
2643
|
}
|
|
2632
2644
|
if (descTickY != null) {
|
|
2633
|
-
descTableHtml +=
|
|
2645
|
+
descTableHtml += "<td></td>";
|
|
2634
2646
|
}
|
|
2635
2647
|
for (let i = 0; i < descNumCols; i++) {
|
|
2636
2648
|
descTableHtml += `<th scope="col">${descTickX[i]}</th>`;
|
|
2637
2649
|
}
|
|
2638
|
-
descTableHtml +=
|
|
2650
|
+
descTableHtml += "</tr>";
|
|
2639
2651
|
}
|
|
2640
|
-
descTableHtml +=
|
|
2652
|
+
descTableHtml += "</thead>";
|
|
2641
2653
|
}
|
|
2642
2654
|
|
|
2643
2655
|
// body rows
|
|
2644
2656
|
if (descNumRows > 0) {
|
|
2645
|
-
descTableHtml +=
|
|
2657
|
+
descTableHtml += "<tbody>";
|
|
2646
2658
|
for (let i = 0; i < descNumRows; i++) {
|
|
2647
|
-
descTableHtml +=
|
|
2659
|
+
descTableHtml += "<tr>";
|
|
2648
2660
|
if (descLabelY != null && i == 0) {
|
|
2649
2661
|
descTableHtml += `<th scope="row" rowspan="${descNumRows}">${descLabelY}</th>`;
|
|
2650
2662
|
}
|
|
@@ -2654,19 +2666,19 @@ class Description {
|
|
|
2654
2666
|
for (let j = 0; j < descNumCols; j++) {
|
|
2655
2667
|
descTableHtml += `<td>${descData[i][j]}</td>`;
|
|
2656
2668
|
}
|
|
2657
|
-
descTableHtml +=
|
|
2669
|
+
descTableHtml += "</tr>";
|
|
2658
2670
|
}
|
|
2659
|
-
descTableHtml +=
|
|
2671
|
+
descTableHtml += "</tbody>";
|
|
2660
2672
|
}
|
|
2661
2673
|
|
|
2662
|
-
descTableHtml +=
|
|
2674
|
+
descTableHtml += "</table>";
|
|
2663
2675
|
}
|
|
2664
2676
|
|
|
2665
2677
|
// bar: don't need colspan or rowspan stuff, put legendX and Y as headers
|
|
2666
2678
|
|
|
2667
|
-
document.getElementById(
|
|
2668
|
-
document.getElementById(
|
|
2669
|
-
document.getElementById(
|
|
2679
|
+
document.getElementById("desc_title").innerHTML = descType + " description";
|
|
2680
|
+
document.getElementById("desc_content").innerHTML = descHtml;
|
|
2681
|
+
document.getElementById("desc_table").innerHTML = descTableHtml;
|
|
2670
2682
|
}
|
|
2671
2683
|
}
|
|
2672
2684
|
|
|
@@ -2736,11 +2748,11 @@ class Tracker {
|
|
|
2736
2748
|
* Downloads the tracker data as a JSON file.
|
|
2737
2749
|
*/
|
|
2738
2750
|
DownloadTrackerData() {
|
|
2739
|
-
let link = document.createElement(
|
|
2751
|
+
let link = document.createElement("a");
|
|
2740
2752
|
let data = this.GetTrackerData();
|
|
2741
|
-
let fileStr = new Blob([JSON.stringify(data)], { type:
|
|
2753
|
+
let fileStr = new Blob([JSON.stringify(data)], { type: "text/plain" });
|
|
2742
2754
|
link.href = URL.createObjectURL(fileStr);
|
|
2743
|
-
link.download =
|
|
2755
|
+
link.download = "tracking.json";
|
|
2744
2756
|
link.click();
|
|
2745
2757
|
}
|
|
2746
2758
|
|
|
@@ -2769,7 +2781,7 @@ class Tracker {
|
|
|
2769
2781
|
this.data = null;
|
|
2770
2782
|
|
|
2771
2783
|
if (constants.debugLevel > 0) {
|
|
2772
|
-
console.log(
|
|
2784
|
+
console.log("tracking data cleared");
|
|
2773
2785
|
}
|
|
2774
2786
|
|
|
2775
2787
|
this.DataSetup();
|
|
@@ -2777,12 +2789,12 @@ class Tracker {
|
|
|
2777
2789
|
|
|
2778
2790
|
SaveSettings() {
|
|
2779
2791
|
// fetch all settings, push to data.settings
|
|
2780
|
-
let settings = JSON.parse(localStorage.getItem(
|
|
2792
|
+
let settings = JSON.parse(localStorage.getItem("settings_data"));
|
|
2781
2793
|
if (settings) {
|
|
2782
2794
|
// don't store their auth keys
|
|
2783
|
-
settings.openAIAuthKey =
|
|
2784
|
-
settings.geminiAuthKey =
|
|
2785
|
-
this.SetData(
|
|
2795
|
+
settings.openAIAuthKey = "hidden";
|
|
2796
|
+
settings.geminiAuthKey = "hidden";
|
|
2797
|
+
this.SetData("settings", settings);
|
|
2786
2798
|
}
|
|
2787
2799
|
}
|
|
2788
2800
|
|
|
@@ -2876,7 +2888,7 @@ class Tracker {
|
|
|
2876
2888
|
}
|
|
2877
2889
|
if (!this.isUndefinedOrNull(constants.infoDiv.innerHTML)) {
|
|
2878
2890
|
let textDisplay = Object.assign(constants.infoDiv.innerHTML);
|
|
2879
|
-
textDisplay = textDisplay.replaceAll(/<[^>]*>?/gm,
|
|
2891
|
+
textDisplay = textDisplay.replaceAll(/<[^>]*>?/gm, "");
|
|
2880
2892
|
eventToLog.text_display = textDisplay;
|
|
2881
2893
|
}
|
|
2882
2894
|
if (!this.isUndefinedOrNull(location.href)) {
|
|
@@ -2884,13 +2896,13 @@ class Tracker {
|
|
|
2884
2896
|
}
|
|
2885
2897
|
|
|
2886
2898
|
// chart specific values
|
|
2887
|
-
let x_tickmark =
|
|
2888
|
-
let y_tickmark =
|
|
2889
|
-
let x_label =
|
|
2890
|
-
let y_label =
|
|
2891
|
-
let value =
|
|
2892
|
-
let fill_value =
|
|
2893
|
-
if (constants.chartType ==
|
|
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 == "bar") {
|
|
2894
2906
|
if (!this.isUndefinedOrNull(plot.columnLabels[position.x])) {
|
|
2895
2907
|
x_tickmark = plot.columnLabels[position.x];
|
|
2896
2908
|
}
|
|
@@ -2903,7 +2915,7 @@ class Tracker {
|
|
|
2903
2915
|
if (!this.isUndefinedOrNull(plot.plotData[position.x])) {
|
|
2904
2916
|
value = plot.plotData[position.x];
|
|
2905
2917
|
}
|
|
2906
|
-
} else if (constants.chartType ==
|
|
2918
|
+
} else if (constants.chartType == "heat") {
|
|
2907
2919
|
if (!this.isUndefinedOrNull(plot.x_labels[position.x])) {
|
|
2908
2920
|
x_tickmark = plot.x_labels[position.x].trim();
|
|
2909
2921
|
}
|
|
@@ -2924,11 +2936,11 @@ class Tracker {
|
|
|
2924
2936
|
if (!this.isUndefinedOrNull(plot.group_labels[2])) {
|
|
2925
2937
|
fill_value = plot.group_labels[2];
|
|
2926
2938
|
}
|
|
2927
|
-
} else if (constants.chartType ==
|
|
2939
|
+
} else if (constants.chartType == "box") {
|
|
2928
2940
|
let plotPos =
|
|
2929
|
-
constants.plotOrientation ==
|
|
2941
|
+
constants.plotOrientation == "vert" ? position.x : position.y;
|
|
2930
2942
|
let sectionPos =
|
|
2931
|
-
constants.plotOrientation ==
|
|
2943
|
+
constants.plotOrientation == "vert" ? position.y : position.x;
|
|
2932
2944
|
let sectionLabel = plot.sections[sectionPos];
|
|
2933
2945
|
|
|
2934
2946
|
if (!this.isUndefinedOrNull(plot.x_group_label)) {
|
|
@@ -2937,7 +2949,7 @@ class Tracker {
|
|
|
2937
2949
|
if (!this.isUndefinedOrNull(plot.y_group_label)) {
|
|
2938
2950
|
y_label = plot.y_group_label;
|
|
2939
2951
|
}
|
|
2940
|
-
if (constants.plotOrientation ==
|
|
2952
|
+
if (constants.plotOrientation == "vert") {
|
|
2941
2953
|
if (plotPos > -1 && sectionPos > -1) {
|
|
2942
2954
|
if (!this.isUndefinedOrNull(sectionLabel)) {
|
|
2943
2955
|
y_tickmark = sectionLabel;
|
|
@@ -2962,7 +2974,7 @@ class Tracker {
|
|
|
2962
2974
|
}
|
|
2963
2975
|
}
|
|
2964
2976
|
}
|
|
2965
|
-
} else if (constants.chartType ==
|
|
2977
|
+
} else if (constants.chartType == "point") {
|
|
2966
2978
|
if (!this.isUndefinedOrNull(plot.x_group_label)) {
|
|
2967
2979
|
x_label = plot.x_group_label;
|
|
2968
2980
|
}
|
|
@@ -2989,13 +3001,13 @@ class Tracker {
|
|
|
2989
3001
|
|
|
2990
3002
|
//console.log("x_tickmark: '", x_tickmark, "', y_tickmark: '", y_tickmark, "', x_label: '", x_label, "', y_label: '", y_label, "', value: '", value, "', fill_value: '", fill_value);
|
|
2991
3003
|
|
|
2992
|
-
this.SetData(
|
|
3004
|
+
this.SetData("events", eventToLog);
|
|
2993
3005
|
//console.log('logged an event');
|
|
2994
3006
|
}
|
|
2995
3007
|
|
|
2996
3008
|
SetData(key, value) {
|
|
2997
3009
|
let data = this.GetTrackerData();
|
|
2998
|
-
let arrayKeys = [
|
|
3010
|
+
let arrayKeys = ["events", "ChatHistory", "settings"];
|
|
2999
3011
|
if (!arrayKeys.includes(key)) {
|
|
3000
3012
|
data[key] = value;
|
|
3001
3013
|
} else {
|
|
@@ -3036,20 +3048,20 @@ class Review {
|
|
|
3036
3048
|
// true means on or show
|
|
3037
3049
|
if (onoff) {
|
|
3038
3050
|
constants.reviewSaveSpot = document.activeElement;
|
|
3039
|
-
constants.review_container.classList.remove(
|
|
3051
|
+
constants.review_container.classList.remove("hidden");
|
|
3040
3052
|
constants.reviewSaveBrailleMode = constants.brailleMode;
|
|
3041
3053
|
constants.review.focus();
|
|
3042
3054
|
|
|
3043
|
-
display.announceText(
|
|
3055
|
+
display.announceText("Review on");
|
|
3044
3056
|
} else {
|
|
3045
|
-
constants.review_container.classList.add(
|
|
3046
|
-
if (constants.reviewSaveBrailleMode ==
|
|
3057
|
+
constants.review_container.classList.add("hidden");
|
|
3058
|
+
if (constants.reviewSaveBrailleMode == "on") {
|
|
3047
3059
|
// we have to turn braille mode back on
|
|
3048
|
-
display.toggleBrailleMode(
|
|
3060
|
+
display.toggleBrailleMode("on");
|
|
3049
3061
|
} else {
|
|
3050
3062
|
constants.reviewSaveSpot.focus();
|
|
3051
3063
|
}
|
|
3052
|
-
display.announceText(
|
|
3064
|
+
display.announceText("Review off");
|
|
3053
3065
|
}
|
|
3054
3066
|
}
|
|
3055
3067
|
}
|
|
@@ -3066,7 +3078,7 @@ class LogError {
|
|
|
3066
3078
|
* @param {string} a - The absent element to log.
|
|
3067
3079
|
*/
|
|
3068
3080
|
LogAbsentElement(a) {
|
|
3069
|
-
console.log(a,
|
|
3081
|
+
console.log(a, "not found. Visual highlighting is turned off.");
|
|
3070
3082
|
}
|
|
3071
3083
|
|
|
3072
3084
|
/**
|
|
@@ -3074,7 +3086,7 @@ class LogError {
|
|
|
3074
3086
|
* @param {string} a - The critical element to log.
|
|
3075
3087
|
*/
|
|
3076
3088
|
LogCriticalElement(a) {
|
|
3077
|
-
consolelog(a,
|
|
3089
|
+
consolelog(a, "is critical. MAIDR unable to run");
|
|
3078
3090
|
}
|
|
3079
3091
|
|
|
3080
3092
|
/**
|
|
@@ -3085,9 +3097,9 @@ class LogError {
|
|
|
3085
3097
|
LogDifferentLengths(a, b) {
|
|
3086
3098
|
console.log(
|
|
3087
3099
|
a,
|
|
3088
|
-
|
|
3100
|
+
"and",
|
|
3089
3101
|
b,
|
|
3090
|
-
|
|
3102
|
+
"do not have the same length. Visual highlighting is turned off."
|
|
3091
3103
|
);
|
|
3092
3104
|
}
|
|
3093
3105
|
|
|
@@ -3098,11 +3110,11 @@ class LogError {
|
|
|
3098
3110
|
*/
|
|
3099
3111
|
LogTooManyElements(a, b) {
|
|
3100
3112
|
console.log(
|
|
3101
|
-
|
|
3113
|
+
"Too many",
|
|
3102
3114
|
a,
|
|
3103
|
-
|
|
3115
|
+
"elements. Only the first",
|
|
3104
3116
|
b,
|
|
3105
|
-
|
|
3117
|
+
"will be highlighted."
|
|
3106
3118
|
);
|
|
3107
3119
|
}
|
|
3108
3120
|
|
|
@@ -3111,7 +3123,7 @@ class LogError {
|
|
|
3111
3123
|
* @param {*} a - The parameter that is not an array.
|
|
3112
3124
|
*/
|
|
3113
3125
|
LogNotArray(a) {
|
|
3114
|
-
console.log(a,
|
|
3126
|
+
console.log(a, "is not an array. Visual highlighting is turned off.");
|
|
3115
3127
|
}
|
|
3116
3128
|
}
|
|
3117
3129
|
|
|
@@ -3782,6 +3794,7 @@ class Display {
|
|
|
3782
3794
|
}
|
|
3783
3795
|
}
|
|
3784
3796
|
if (onoff == 'on') {
|
|
3797
|
+
constants.lockSelection = true;
|
|
3785
3798
|
if (constants.chartType == 'box') {
|
|
3786
3799
|
// braille mode is on before any plot is selected
|
|
3787
3800
|
if (
|
|
@@ -3819,6 +3832,9 @@ class Display {
|
|
|
3819
3832
|
if (position.x == -1 && position.y == -1) {
|
|
3820
3833
|
constants.brailleInput.setSelectionRange(0, 0);
|
|
3821
3834
|
}
|
|
3835
|
+
setTimeout(function () {
|
|
3836
|
+
constants.lockSelection = false;
|
|
3837
|
+
}, 50);
|
|
3822
3838
|
} else {
|
|
3823
3839
|
constants.brailleMode = 'off';
|
|
3824
3840
|
document
|
|
@@ -3925,6 +3941,7 @@ class Display {
|
|
|
3925
3941
|
* Updates the position of the cursor in the braille display based on the current chart type and position.
|
|
3926
3942
|
*/
|
|
3927
3943
|
UpdateBraillePos() {
|
|
3944
|
+
constants.lockSelection = true;
|
|
3928
3945
|
if (
|
|
3929
3946
|
constants.chartType == 'bar' ||
|
|
3930
3947
|
constants.chartType == 'hist' ||
|
|
@@ -3954,6 +3971,11 @@ class Display {
|
|
|
3954
3971
|
let targetLabel = this.boxplotGridPlaceholders[sectionPos];
|
|
3955
3972
|
let haveTargetLabel = false;
|
|
3956
3973
|
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
|
+
|
|
3957
3979
|
if (constants.brailleData) {
|
|
3958
3980
|
for (let i = 0; i < constants.brailleData.length; i++) {
|
|
3959
3981
|
if (constants.brailleData[i].type != 'blank') {
|
|
@@ -3982,6 +4004,9 @@ class Display {
|
|
|
3982
4004
|
) {
|
|
3983
4005
|
constants.brailleInput.setSelectionRange(positionL1.x, positionL1.x);
|
|
3984
4006
|
}
|
|
4007
|
+
setTimeout(function () {
|
|
4008
|
+
constants.lockSelection = false;
|
|
4009
|
+
}, 50);
|
|
3985
4010
|
}
|
|
3986
4011
|
|
|
3987
4012
|
/**
|
|
@@ -4286,16 +4311,20 @@ class Display {
|
|
|
4286
4311
|
constants.verboseText = verboseText;
|
|
4287
4312
|
// aria live hack. If we're repeating (Space), aria won't detect if text is the same, so we modify vey slightly by adding / removing period at the end
|
|
4288
4313
|
if (output == constants.infoDiv.innerHTML) {
|
|
4289
|
-
if (constants.infoDiv.
|
|
4290
|
-
if (output.endsWith('
|
|
4291
|
-
output = output.
|
|
4314
|
+
if (constants.infoDiv.textContent.endsWith('.')) {
|
|
4315
|
+
if (output.endsWith('.</p>')) {
|
|
4316
|
+
output = output.replace('.</p>', '</p>');
|
|
4292
4317
|
}
|
|
4293
4318
|
} else {
|
|
4294
|
-
output = output
|
|
4319
|
+
output = output.replace('</p>', '.</p>');
|
|
4295
4320
|
}
|
|
4296
4321
|
}
|
|
4297
4322
|
|
|
4298
|
-
|
|
4323
|
+
// could also try this hack, but we'll need a time gap
|
|
4324
|
+
if (constants.infoDiv) {
|
|
4325
|
+
constants.infoDiv.innerHTML = '';
|
|
4326
|
+
constants.infoDiv.innerHTML = output;
|
|
4327
|
+
}
|
|
4299
4328
|
if (constants.review) {
|
|
4300
4329
|
if (output.length > 0) {
|
|
4301
4330
|
constants.review.value = output.replace(/<[^>]*>?/gm, '');
|
|
@@ -8307,7 +8336,7 @@ class Control {
|
|
|
8307
8336
|
*
|
|
8308
8337
|
* @returns {void}
|
|
8309
8338
|
*/
|
|
8310
|
-
SetControls() {
|
|
8339
|
+
async SetControls() {
|
|
8311
8340
|
constants.events.push([
|
|
8312
8341
|
document,
|
|
8313
8342
|
'keydown',
|
|
@@ -8406,6 +8435,12 @@ class Control {
|
|
|
8406
8435
|
constants.brailleMode == 'on' &&
|
|
8407
8436
|
constants.brailleInput.selectionStart
|
|
8408
8437
|
) {
|
|
8438
|
+
if (constants.lockSelection) {
|
|
8439
|
+
return;
|
|
8440
|
+
}
|
|
8441
|
+
// we lock the selection while we're changing stuff so it doesn't loop
|
|
8442
|
+
constants.lockSelection = true;
|
|
8443
|
+
|
|
8409
8444
|
let cursorPos = constants.brailleInput.selectionStart;
|
|
8410
8445
|
// we're using braille cursor, update the selection from what was clicked
|
|
8411
8446
|
cursorPos = constants.brailleInput.selectionStart;
|
|
@@ -8447,18 +8482,29 @@ class Control {
|
|
|
8447
8482
|
|
|
8448
8483
|
// update display / text / audio
|
|
8449
8484
|
if (testEnd) {
|
|
8485
|
+
this.lockPosition = true;
|
|
8450
8486
|
control.UpdateAll();
|
|
8487
|
+
this.lockPosition = false;
|
|
8451
8488
|
}
|
|
8452
8489
|
if (testEnd) {
|
|
8453
8490
|
audio.playEnd();
|
|
8454
8491
|
}
|
|
8455
8492
|
}
|
|
8493
|
+
setTimeout(function () {
|
|
8494
|
+
constants.lockSelection = false;
|
|
8495
|
+
}, 50);
|
|
8456
8496
|
}
|
|
8457
8497
|
});
|
|
8458
8498
|
} else if ([].concat(singleMaidr.type).includes('heat')) {
|
|
8459
8499
|
document.addEventListener('selectionchange', function (e) {
|
|
8460
8500
|
if (constants.brailleMode == 'on') {
|
|
8501
|
+
if (constants.lockSelection) {
|
|
8502
|
+
return;
|
|
8503
|
+
}
|
|
8504
|
+
|
|
8461
8505
|
let pos = constants.brailleInput.selectionStart;
|
|
8506
|
+
// we lock the selection while we're changing stuff so it doesn't loop
|
|
8507
|
+
constants.lockSelection = true;
|
|
8462
8508
|
|
|
8463
8509
|
// exception: don't let users click the seperator char
|
|
8464
8510
|
let seperatorPositions = constants.brailleInput.value
|
|
@@ -8490,6 +8536,9 @@ class Control {
|
|
|
8490
8536
|
if (testEnd) {
|
|
8491
8537
|
audio.playEnd();
|
|
8492
8538
|
}
|
|
8539
|
+
setTimeout(function () {
|
|
8540
|
+
constants.lockSelection = false;
|
|
8541
|
+
}, 50);
|
|
8493
8542
|
} else {
|
|
8494
8543
|
// we're using normal cursor, let the default handle it
|
|
8495
8544
|
}
|
|
@@ -8505,23 +8554,35 @@ class Control {
|
|
|
8505
8554
|
) {
|
|
8506
8555
|
document.addEventListener('selectionchange', function (e) {
|
|
8507
8556
|
if (constants.brailleMode == 'on') {
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
pos = constants.brailleInput.selectionStart;
|
|
8511
|
-
if (pos < 0) {
|
|
8512
|
-
pos = 0;
|
|
8557
|
+
if (constants.lockSelection) {
|
|
8558
|
+
return;
|
|
8513
8559
|
}
|
|
8514
|
-
position.x = pos;
|
|
8515
|
-
control.lockPosition(); // bar etc is default, no need to supply values
|
|
8516
|
-
let testEnd = true;
|
|
8517
8560
|
|
|
8518
|
-
//
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8561
|
+
// we lock the selection while we're changing stuff so it doesn't loop
|
|
8562
|
+
constants.lockSelection = true;
|
|
8563
|
+
|
|
8564
|
+
if (constants.brailleInput) {
|
|
8565
|
+
let pos = constants.brailleInput.selectionStart;
|
|
8566
|
+
// we're using braille cursor, update the selection from what was clicked
|
|
8567
|
+
pos = constants.brailleInput.selectionStart;
|
|
8568
|
+
if (pos < 0) {
|
|
8569
|
+
pos = 0;
|
|
8570
|
+
}
|
|
8571
|
+
position.x = pos;
|
|
8572
|
+
control.lockPosition(); // bar etc is default, no need to supply values
|
|
8573
|
+
let testEnd = true;
|
|
8574
|
+
|
|
8575
|
+
// update display / text / audio
|
|
8576
|
+
if (testEnd) {
|
|
8577
|
+
control.UpdateAll();
|
|
8578
|
+
}
|
|
8579
|
+
if (testEnd) {
|
|
8580
|
+
audio.playEnd();
|
|
8581
|
+
}
|
|
8524
8582
|
}
|
|
8583
|
+
setTimeout(function () {
|
|
8584
|
+
constants.lockSelection = false;
|
|
8585
|
+
}, 50);
|
|
8525
8586
|
} else {
|
|
8526
8587
|
// we're using normal cursor, let the default handle it
|
|
8527
8588
|
}
|
|
@@ -10081,8 +10142,14 @@ class Control {
|
|
|
10081
10142
|
// braille cursor routing
|
|
10082
10143
|
document.addEventListener('selectionchange', function (e) {
|
|
10083
10144
|
if (constants.brailleMode == 'on') {
|
|
10084
|
-
|
|
10145
|
+
if (constants.lockSelection) {
|
|
10146
|
+
return;
|
|
10147
|
+
}
|
|
10148
|
+
// we lock the selection while we're changing stuff so it doesn't loop
|
|
10149
|
+
constants.lockSelection = true;
|
|
10150
|
+
|
|
10085
10151
|
// we're using braille cursor, update the selection from what was clicked
|
|
10152
|
+
let pos = constants.brailleInput.selectionStart;
|
|
10086
10153
|
pos = constants.brailleInput.selectionStart;
|
|
10087
10154
|
if (pos < 0) {
|
|
10088
10155
|
pos = 0;
|
|
@@ -10098,6 +10165,9 @@ class Control {
|
|
|
10098
10165
|
if (testEnd) {
|
|
10099
10166
|
audio.playEnd();
|
|
10100
10167
|
}
|
|
10168
|
+
setTimeout(function () {
|
|
10169
|
+
constants.lockSelection = false;
|
|
10170
|
+
}, 50);
|
|
10101
10171
|
}
|
|
10102
10172
|
});
|
|
10103
10173
|
|
|
@@ -11135,10 +11205,18 @@ function InitMaidr(thisMaidr) {
|
|
|
11135
11205
|
// actually do eventlisteners for all events
|
|
11136
11206
|
this.SetEvents();
|
|
11137
11207
|
|
|
11208
|
+
// Set img role for chart
|
|
11209
|
+
constants.chart.setAttribute('role', 'img');
|
|
11210
|
+
|
|
11138
11211
|
// once everything is set up, announce the chart name (or title as a backup) to the user
|
|
11139
|
-
setTimeout(function () {
|
|
11140
|
-
|
|
11141
|
-
|
|
11212
|
+
// setTimeout(function () {
|
|
11213
|
+
if ('name' in singleMaidr) {
|
|
11214
|
+
|
|
11215
|
+
// Add the aria-label and title attributes to the chart
|
|
11216
|
+
constants.chart.setAttribute('aria-label', announceText);
|
|
11217
|
+
constants.chart.setAttribute('title', announceText);
|
|
11218
|
+
|
|
11219
|
+
// display.announceText(singleMaidr.name);
|
|
11142
11220
|
} else if (
|
|
11143
11221
|
'title' in singleMaidr ||
|
|
11144
11222
|
('labels' in singleMaidr && 'title' in singleMaidr.labels)
|
|
@@ -11166,10 +11244,16 @@ function InitMaidr(thisMaidr) {
|
|
|
11166
11244
|
isMultiLayered ? multiLayerInstruction : ' '
|
|
11167
11245
|
}Toggle B for Braille, T for Text, S for Sonification, and R for Review mode. Use H for Help.`;
|
|
11168
11246
|
|
|
11247
|
+
// Add the aria-label and title attributes to the chart
|
|
11248
|
+
constants.chart.setAttribute('aria-label', announceText);
|
|
11249
|
+
constants.chart.setAttribute('title', announceText);
|
|
11250
|
+
|
|
11251
|
+
|
|
11252
|
+
|
|
11169
11253
|
// Display the announcement text
|
|
11170
|
-
display.announceText(announceText);
|
|
11254
|
+
// display.announceText(announceText);
|
|
11171
11255
|
}
|
|
11172
|
-
}, 100);
|
|
11256
|
+
// }, 100);
|
|
11173
11257
|
}
|
|
11174
11258
|
}
|
|
11175
11259
|
|