maidr 1.2.2 → 1.3.1
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 +170 -174
- package/dist/maidr.js +562 -587
- package/dist/maidr.min.js +1 -1
- package/package.json +2 -2
package/dist/maidr.js
CHANGED
|
@@ -74,13 +74,16 @@ class Constants {
|
|
|
74
74
|
visualBraille = false; // do we want to represent braille based on what's visually there or actually there. Like if we have 2 outliers with the same position, do we show 1 (visualBraille true) or 2 (false)
|
|
75
75
|
globalMinMax = true;
|
|
76
76
|
ariaMode = 'assertive'; // assertive (default) / polite
|
|
77
|
-
playLLMWaitingSound = true;
|
|
78
77
|
|
|
79
78
|
// LLM settings
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
openAIAuthKey = null; // OpenAI authentication key, set in menu
|
|
80
|
+
geminiAuthKey = null; // Gemini authentication key, set in menu
|
|
82
81
|
LLMmaxResponseTokens = 1000; // max tokens to send to LLM, 20 for testing, 1000 ish for real
|
|
82
|
+
playLLMWaitingSound = true;
|
|
83
83
|
LLMDetail = 'high'; // low (default for testing, like 100 tokens) / high (default for real, like 1000 tokens)
|
|
84
|
+
LLMModel = 'openai'; // openai (default) / gemini
|
|
85
|
+
LLMSystemMessage =
|
|
86
|
+
'You are a helpful assistant describing the chart to a blind person';
|
|
84
87
|
skillLevel = 'basic'; // basic / intermediate / expert
|
|
85
88
|
skillLevelOther = ''; // custom skill level
|
|
86
89
|
|
|
@@ -297,7 +300,7 @@ class Menu {
|
|
|
297
300
|
<div class="modal-dialog" role="document" tabindex="0">
|
|
298
301
|
<div class="modal-content">
|
|
299
302
|
<div class="modal-header">
|
|
300
|
-
<
|
|
303
|
+
<h2 class="modal-title">Menu</h2>
|
|
301
304
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
302
305
|
<span aria-hidden="true">×</span>
|
|
303
306
|
</button>
|
|
@@ -406,7 +409,15 @@ class Menu {
|
|
|
406
409
|
}><label for="aria_mode_polite">Polite</label></p>
|
|
407
410
|
</fieldset></div>
|
|
408
411
|
<h5 class="modal-title">LLM Settings</h5>
|
|
409
|
-
<p
|
|
412
|
+
<p>
|
|
413
|
+
<select id="LLM_model">
|
|
414
|
+
<option value="openai">OpenAI Vision</option>
|
|
415
|
+
<option value="gemini">Gemini Pro Vision</option>
|
|
416
|
+
</select>
|
|
417
|
+
<label for="LLM_model">LLM Model</label>
|
|
418
|
+
</p>
|
|
419
|
+
<p id="openai_auth_key_container" class="hidden"><input type="password" id="openai_auth_key"> <label for="openai_auth_key">OpenAI Authentication Key</label></p>
|
|
420
|
+
<p id="gemini_auth_key_container" class="hidden"><input type="password" id="gemini_auth_key"> <label for="gemini_auth_key">Gemini Authentication Key</label></p>
|
|
410
421
|
<p>
|
|
411
422
|
<select id="skill_level">
|
|
412
423
|
<option value="basic">Basic</option>
|
|
@@ -416,7 +427,7 @@ class Menu {
|
|
|
416
427
|
</select>
|
|
417
428
|
<label for="skill_level">Level of skill in statistical charts</label>
|
|
418
429
|
</p>
|
|
419
|
-
<p id="skill_level_other_container" class="hidden"><input type="text" id="skill_level_other"> <label for="skill_level_other">
|
|
430
|
+
<p id="skill_level_other_container" class="hidden"><input type="text" placeholder="Very basic" id="skill_level_other"> <label for="skill_level_other">Describe your level of skill in statistical charts</label></p>
|
|
420
431
|
</div>
|
|
421
432
|
</div>
|
|
422
433
|
<div class="modal-footer">
|
|
@@ -483,6 +494,29 @@ class Menu {
|
|
|
483
494
|
},
|
|
484
495
|
]);
|
|
485
496
|
|
|
497
|
+
// toggle auth key fields
|
|
498
|
+
constants.events.push([
|
|
499
|
+
document.getElementById('LLM_model'),
|
|
500
|
+
'change',
|
|
501
|
+
function (e) {
|
|
502
|
+
if (e.target.value == 'openai') {
|
|
503
|
+
document
|
|
504
|
+
.getElementById('openai_auth_key_container')
|
|
505
|
+
.classList.remove('hidden');
|
|
506
|
+
document
|
|
507
|
+
.getElementById('gemini_auth_key_container')
|
|
508
|
+
.classList.add('hidden');
|
|
509
|
+
} else if (e.target.value == 'gemini') {
|
|
510
|
+
document
|
|
511
|
+
.getElementById('openai_auth_key_container')
|
|
512
|
+
.classList.add('hidden');
|
|
513
|
+
document
|
|
514
|
+
.getElementById('gemini_auth_key_container')
|
|
515
|
+
.classList.remove('hidden');
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
]);
|
|
519
|
+
|
|
486
520
|
// Skill level other events
|
|
487
521
|
constants.events.push([
|
|
488
522
|
document.getElementById('skill_level'),
|
|
@@ -560,7 +594,6 @@ class Menu {
|
|
|
560
594
|
*/
|
|
561
595
|
PopulateData() {
|
|
562
596
|
document.getElementById('vol').value = constants.vol;
|
|
563
|
-
//document.getElementById('show_rect').checked = constants.showRect;
|
|
564
597
|
document.getElementById('autoplay_rate').value = constants.autoPlayRate;
|
|
565
598
|
document.getElementById('braille_display_length').value =
|
|
566
599
|
constants.brailleDisplayLength;
|
|
@@ -569,14 +602,20 @@ class Menu {
|
|
|
569
602
|
document.getElementById('max_freq').value = constants.MAX_FREQUENCY;
|
|
570
603
|
document.getElementById('keypress_interval').value =
|
|
571
604
|
constants.keypressInterval;
|
|
572
|
-
if (typeof constants.
|
|
573
|
-
document.getElementById('
|
|
605
|
+
if (typeof constants.openAIAuthKey == 'string') {
|
|
606
|
+
document.getElementById('openai_auth_key').value =
|
|
607
|
+
constants.openAIAuthKey;
|
|
608
|
+
}
|
|
609
|
+
if (typeof constants.geminiAuthKey == 'string') {
|
|
610
|
+
document.getElementById('gemini_auth_key').value =
|
|
611
|
+
constants.geminiAuthKey;
|
|
574
612
|
}
|
|
575
613
|
document.getElementById('skill_level').value = constants.skillLevel;
|
|
576
614
|
if (constants.skillLevelOther) {
|
|
577
615
|
document.getElementById('skill_level_other').value =
|
|
578
616
|
constants.skillLevelOther;
|
|
579
617
|
}
|
|
618
|
+
document.getElementById('LLM_model').value = constants.LLMModel;
|
|
580
619
|
|
|
581
620
|
// aria mode
|
|
582
621
|
if (constants.ariaMode == 'assertive') {
|
|
@@ -586,6 +625,22 @@ class Menu {
|
|
|
586
625
|
document.getElementById('aria_mode_polite').checked = true;
|
|
587
626
|
document.getElementById('aria_mode_assertive').checked = false;
|
|
588
627
|
}
|
|
628
|
+
// hide either openai or gemini auth key field
|
|
629
|
+
if (constants.LLMModel == 'openai') {
|
|
630
|
+
document
|
|
631
|
+
.getElementById('openai_auth_key_container')
|
|
632
|
+
.classList.remove('hidden');
|
|
633
|
+
document
|
|
634
|
+
.getElementById('gemini_auth_key_container')
|
|
635
|
+
.classList.add('hidden');
|
|
636
|
+
} else if (constants.LLMModel == 'gemini') {
|
|
637
|
+
document
|
|
638
|
+
.getElementById('openai_auth_key_container')
|
|
639
|
+
.classList.add('hidden');
|
|
640
|
+
document
|
|
641
|
+
.getElementById('gemini_auth_key_container')
|
|
642
|
+
.classList.remove('hidden');
|
|
643
|
+
}
|
|
589
644
|
// skill level other
|
|
590
645
|
if (constants.skillLevel == 'other') {
|
|
591
646
|
document
|
|
@@ -599,7 +654,6 @@ class Menu {
|
|
|
599
654
|
*/
|
|
600
655
|
SaveData() {
|
|
601
656
|
constants.vol = document.getElementById('vol').value;
|
|
602
|
-
//constants.showRect = document.getElementById('show_rect').checked;
|
|
603
657
|
constants.autoPlayRate = document.getElementById('autoplay_rate').value;
|
|
604
658
|
constants.brailleDisplayLength = document.getElementById(
|
|
605
659
|
'braille_display_length'
|
|
@@ -609,10 +663,12 @@ class Menu {
|
|
|
609
663
|
constants.MAX_FREQUENCY = document.getElementById('max_freq').value;
|
|
610
664
|
constants.keypressInterval =
|
|
611
665
|
document.getElementById('keypress_interval').value;
|
|
612
|
-
constants.
|
|
666
|
+
constants.openAIAuthKey = document.getElementById('openai_auth_key').value;
|
|
667
|
+
constants.geminiAuthKey = document.getElementById('gemini_auth_key').value;
|
|
613
668
|
constants.skillLevel = document.getElementById('skill_level').value;
|
|
614
669
|
constants.skillLevelOther =
|
|
615
670
|
document.getElementById('skill_level_other').value;
|
|
671
|
+
constants.LLMModel = document.getElementById('LLM_model').value;
|
|
616
672
|
|
|
617
673
|
// aria
|
|
618
674
|
if (document.getElementById('aria_mode_assertive').checked) {
|
|
@@ -650,7 +706,6 @@ class Menu {
|
|
|
650
706
|
SaveDataToLocalStorage() {
|
|
651
707
|
let data = {};
|
|
652
708
|
data.vol = constants.vol;
|
|
653
|
-
//data.showRect = constants.showRect;
|
|
654
709
|
data.autoPlayRate = constants.autoPlayRate;
|
|
655
710
|
data.brailleDisplayLength = constants.brailleDisplayLength;
|
|
656
711
|
data.colorSelected = constants.colorSelected;
|
|
@@ -658,9 +713,11 @@ class Menu {
|
|
|
658
713
|
data.MAX_FREQUENCY = constants.MAX_FREQUENCY;
|
|
659
714
|
data.keypressInterval = constants.keypressInterval;
|
|
660
715
|
data.ariaMode = constants.ariaMode;
|
|
661
|
-
data.
|
|
716
|
+
data.openAIAuthKey = constants.openAIAuthKey;
|
|
717
|
+
data.geminiAuthKey = constants.geminiAuthKey;
|
|
662
718
|
data.skillLevel = constants.skillLevel;
|
|
663
719
|
data.skillLevelOther = constants.skillLevelOther;
|
|
720
|
+
data.LLMModel = constants.LLMModel;
|
|
664
721
|
localStorage.setItem('settings_data', JSON.stringify(data));
|
|
665
722
|
}
|
|
666
723
|
/**
|
|
@@ -670,7 +727,6 @@ class Menu {
|
|
|
670
727
|
let data = JSON.parse(localStorage.getItem('settings_data'));
|
|
671
728
|
if (data) {
|
|
672
729
|
constants.vol = data.vol;
|
|
673
|
-
//constants.showRect = data.showRect;
|
|
674
730
|
constants.autoPlayRate = data.autoPlayRate;
|
|
675
731
|
constants.brailleDisplayLength = data.brailleDisplayLength;
|
|
676
732
|
constants.colorSelected = data.colorSelected;
|
|
@@ -678,9 +734,11 @@ class Menu {
|
|
|
678
734
|
constants.MAX_FREQUENCY = data.MAX_FREQUENCY;
|
|
679
735
|
constants.keypressInterval = data.keypressInterval;
|
|
680
736
|
constants.ariaMode = data.ariaMode;
|
|
681
|
-
constants.
|
|
737
|
+
constants.openAIAuthKey = data.openAIAuthKey;
|
|
738
|
+
constants.geminiAuthKey = data.geminiAuthKey;
|
|
682
739
|
constants.skillLevel = data.skillLevel;
|
|
683
740
|
constants.skillLevelOther = data.skillLevelOther;
|
|
741
|
+
constants.LLMModel = data.LLMModel ? data.LLMModel : constants.LLMModel;
|
|
684
742
|
}
|
|
685
743
|
this.PopulateData();
|
|
686
744
|
this.UpdateHtml();
|
|
@@ -709,7 +767,7 @@ class ChatLLM {
|
|
|
709
767
|
<div class="modal-dialog" role="document" tabindex="0">
|
|
710
768
|
<div class="modal-content">
|
|
711
769
|
<div class="modal-header">
|
|
712
|
-
<
|
|
770
|
+
<h2 id="chatLLM_title" class="modal-title">Ask a Question</h2>
|
|
713
771
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
714
772
|
<span aria-hidden="true">×</span>
|
|
715
773
|
</button>
|
|
@@ -769,7 +827,7 @@ class ChatLLM {
|
|
|
769
827
|
document,
|
|
770
828
|
'keyup',
|
|
771
829
|
function (e) {
|
|
772
|
-
if (e.key == '?') {
|
|
830
|
+
if (e.key == '?' && (e.ctrlKey || e.metaKey)) {
|
|
773
831
|
chatLLM.Toggle(true);
|
|
774
832
|
}
|
|
775
833
|
},
|
|
@@ -824,43 +882,15 @@ class ChatLLM {
|
|
|
824
882
|
* @returns {void}
|
|
825
883
|
*/
|
|
826
884
|
Submit(text, img = null) {
|
|
827
|
-
// send text to LLM
|
|
828
|
-
let url = 'https://api.openai.com/v1/chat/completions';
|
|
829
|
-
//let url = 'temp';
|
|
830
|
-
|
|
831
|
-
let requestJson = this.GetLLMJRequestJson(text, img);
|
|
832
|
-
console.log(requestJson);
|
|
833
|
-
|
|
834
|
-
let xhr = new XMLHttpRequest();
|
|
835
|
-
|
|
836
885
|
// start waiting sound
|
|
837
886
|
if (constants.playLLMWaitingSound) {
|
|
838
887
|
chatLLM.WaitingSound(true);
|
|
839
888
|
}
|
|
840
889
|
|
|
841
|
-
if (constants.
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
}, 5000);
|
|
846
|
-
} else {
|
|
847
|
-
fetch(url, {
|
|
848
|
-
method: 'POST',
|
|
849
|
-
headers: {
|
|
850
|
-
'Content-Type': 'application/json',
|
|
851
|
-
Authorization: 'Bearer ' + constants.authKey,
|
|
852
|
-
},
|
|
853
|
-
body: JSON.stringify(requestJson),
|
|
854
|
-
})
|
|
855
|
-
.then((response) => response.json())
|
|
856
|
-
.then((data) => {
|
|
857
|
-
chatLLM.ProcessLLMResponse(data);
|
|
858
|
-
})
|
|
859
|
-
.catch((error) => {
|
|
860
|
-
chatLLM.WaitingSound(false);
|
|
861
|
-
console.error('Error:', error);
|
|
862
|
-
// also todo: handle errors somehow
|
|
863
|
-
});
|
|
890
|
+
if (constants.LLMModel == 'gemini') {
|
|
891
|
+
chatLLM.GeminiPrompt(text, img);
|
|
892
|
+
} else if (constants.LLMModel == 'openai') {
|
|
893
|
+
chatLLM.OpenAIPrompt(text, img);
|
|
864
894
|
}
|
|
865
895
|
}
|
|
866
896
|
|
|
@@ -910,8 +940,38 @@ class ChatLLM {
|
|
|
910
940
|
ProcessLLMResponse(data) {
|
|
911
941
|
chatLLM.WaitingSound(false);
|
|
912
942
|
console.log('LLM response: ', data);
|
|
913
|
-
let text =
|
|
914
|
-
|
|
943
|
+
let text = '';
|
|
944
|
+
let LLMName = '';
|
|
945
|
+
|
|
946
|
+
if (constants.LLMModel == 'openai') {
|
|
947
|
+
LLMName = 'OpenAI';
|
|
948
|
+
text = data.choices[0].message.content;
|
|
949
|
+
let i = this.requestJson.messages.length;
|
|
950
|
+
this.requestJson.messages[i] = {};
|
|
951
|
+
this.requestJson.messages[i].role = 'assistant';
|
|
952
|
+
this.requestJson.messages[i].content = text;
|
|
953
|
+
|
|
954
|
+
if (data.error) {
|
|
955
|
+
chatLLM.DisplayChatMessage(LLMName, 'Error processing request.');
|
|
956
|
+
} else {
|
|
957
|
+
chatLLM.DisplayChatMessage(LLMName, text);
|
|
958
|
+
}
|
|
959
|
+
} else if (constants.LLMModel == 'gemini') {
|
|
960
|
+
LLMName = 'Gemini';
|
|
961
|
+
if (data.text()) {
|
|
962
|
+
text = data.text();
|
|
963
|
+
chatLLM.DisplayChatMessage(LLMName, text);
|
|
964
|
+
} else {
|
|
965
|
+
if (!data.error) {
|
|
966
|
+
data.error = 'Error processing request.';
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (data.error) {
|
|
970
|
+
chatLLM.DisplayChatMessage(LLMName, 'Error processing request.');
|
|
971
|
+
} else {
|
|
972
|
+
// todo: display actual response
|
|
973
|
+
}
|
|
974
|
+
}
|
|
915
975
|
}
|
|
916
976
|
|
|
917
977
|
/**
|
|
@@ -976,32 +1036,60 @@ class ChatLLM {
|
|
|
976
1036
|
/**
|
|
977
1037
|
* Gets running prompt info, appends the latest request, and packages it into a JSON object for the LLM.
|
|
978
1038
|
* @function
|
|
979
|
-
* @name
|
|
1039
|
+
* @name OpenAIPrompt
|
|
980
1040
|
* @memberof module:constants
|
|
981
1041
|
* @returns {json}
|
|
982
1042
|
*/
|
|
983
|
-
|
|
1043
|
+
OpenAIPrompt(text, img) {
|
|
1044
|
+
// request init
|
|
1045
|
+
let url = 'https://api.openai.com/v1/chat/completions';
|
|
1046
|
+
let auth = constants.openAIAuthKey;
|
|
1047
|
+
let requestJson = chatLLM.OpenAIJson(text, img);
|
|
1048
|
+
console.log('LLM request: ', requestJson);
|
|
1049
|
+
|
|
1050
|
+
fetch(url, {
|
|
1051
|
+
method: 'POST',
|
|
1052
|
+
headers: {
|
|
1053
|
+
'Content-Type': 'application/json',
|
|
1054
|
+
Authorization: 'Bearer ' + auth,
|
|
1055
|
+
},
|
|
1056
|
+
body: JSON.stringify(requestJson),
|
|
1057
|
+
})
|
|
1058
|
+
.then((response) => response.json())
|
|
1059
|
+
.then((data) => {
|
|
1060
|
+
chatLLM.ProcessLLMResponse(data);
|
|
1061
|
+
})
|
|
1062
|
+
.catch((error) => {
|
|
1063
|
+
chatLLM.WaitingSound(false);
|
|
1064
|
+
console.error('Error:', error);
|
|
1065
|
+
chatLLM.DisplayChatMessage('LLM', 'Error processing request.');
|
|
1066
|
+
// also todo: handle errors somehow
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
OpenAIJson(text, img) {
|
|
1070
|
+
let sysMessage = constants.LLMSystemMessage;
|
|
1071
|
+
let backupMessage =
|
|
1072
|
+
'Describe ' + singleMaidr.type + ' charts to a blind person';
|
|
1073
|
+
// headers and sys message
|
|
984
1074
|
if (!this.requestJson) {
|
|
985
1075
|
this.requestJson = {};
|
|
986
1076
|
this.requestJson.model = 'gpt-4-vision-preview';
|
|
987
1077
|
this.requestJson.max_tokens = constants.LLMmaxResponseTokens; // note: if this is too short (tested with less than 200), the response gets cut off
|
|
988
|
-
|
|
1078
|
+
|
|
1079
|
+
// sys message
|
|
989
1080
|
this.requestJson.messages = [];
|
|
990
1081
|
this.requestJson.messages[0] = {};
|
|
991
1082
|
this.requestJson.messages[0].role = 'system';
|
|
992
|
-
this.requestJson.messages[0].content =
|
|
993
|
-
'You are a helpful assistant describing the chart to a blind person';
|
|
1083
|
+
this.requestJson.messages[0].content = sysMessage;
|
|
994
1084
|
}
|
|
995
1085
|
|
|
1086
|
+
// user message
|
|
1087
|
+
// if we have an image (first time only), send the image and the text, otherwise just the text
|
|
996
1088
|
let i = this.requestJson.messages.length;
|
|
997
1089
|
this.requestJson.messages[i] = {};
|
|
998
1090
|
this.requestJson.messages[i].role = 'user';
|
|
999
|
-
if (
|
|
1000
|
-
//
|
|
1001
|
-
this.requestJson.messages[i].content =
|
|
1002
|
-
'Describe bar charts to a blind person';
|
|
1003
|
-
} else if (img) {
|
|
1004
|
-
let image_url = img;
|
|
1091
|
+
if (img) {
|
|
1092
|
+
// first message, include the img
|
|
1005
1093
|
this.requestJson.messages[i].content = [
|
|
1006
1094
|
{
|
|
1007
1095
|
type: 'text',
|
|
@@ -1009,7 +1097,7 @@ class ChatLLM {
|
|
|
1009
1097
|
},
|
|
1010
1098
|
{
|
|
1011
1099
|
type: 'image_url',
|
|
1012
|
-
image_url: { url:
|
|
1100
|
+
image_url: { url: img },
|
|
1013
1101
|
},
|
|
1014
1102
|
];
|
|
1015
1103
|
} else {
|
|
@@ -1020,6 +1108,47 @@ class ChatLLM {
|
|
|
1020
1108
|
return this.requestJson;
|
|
1021
1109
|
}
|
|
1022
1110
|
|
|
1111
|
+
// Assuming this function is part of your existing JavaScript file
|
|
1112
|
+
async GeminiPrompt(text, imgBase64 = null) {
|
|
1113
|
+
try {
|
|
1114
|
+
// Save the image for next time
|
|
1115
|
+
if (imgBase64 == null) {
|
|
1116
|
+
imgBase64 = constants.LLMImage;
|
|
1117
|
+
} else {
|
|
1118
|
+
constants.LLMImage = imgBase64;
|
|
1119
|
+
}
|
|
1120
|
+
constants.LLMImage = imgBase64;
|
|
1121
|
+
|
|
1122
|
+
// Import the module
|
|
1123
|
+
const { GoogleGenerativeAI } = await import(
|
|
1124
|
+
'https://esm.run/@google/generative-ai'
|
|
1125
|
+
);
|
|
1126
|
+
const API_KEY = constants.geminiAuthKey;
|
|
1127
|
+
const genAI = new GoogleGenerativeAI(API_KEY);
|
|
1128
|
+
const model = genAI.getGenerativeModel({ model: 'gemini-pro-vision' });
|
|
1129
|
+
|
|
1130
|
+
// Create the prompt
|
|
1131
|
+
const prompt = constants.LLMSystemMessage + '\n\n' + text; // Use the text parameter as the prompt
|
|
1132
|
+
const image = {
|
|
1133
|
+
inlineData: {
|
|
1134
|
+
data: imgBase64, // Use the base64 image string
|
|
1135
|
+
mimeType: 'image/png', // Or the appropriate mime type of your image
|
|
1136
|
+
},
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
// Generate the content
|
|
1140
|
+
console.log('LLM request: ', prompt, image);
|
|
1141
|
+
const result = await model.generateContent([prompt, image]);
|
|
1142
|
+
console.log(result.response.text());
|
|
1143
|
+
|
|
1144
|
+
// Process the response
|
|
1145
|
+
chatLLM.ProcessLLMResponse(result.response);
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
console.error('Error in GeminiPrompt:', error);
|
|
1148
|
+
throw error; // Rethrow the error for further handling if necessary
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1023
1152
|
/**
|
|
1024
1153
|
* Displays chat message from the user and LLM in a chat history window
|
|
1025
1154
|
* @function
|
|
@@ -1032,7 +1161,7 @@ class ChatLLM {
|
|
|
1032
1161
|
<div class="chatLLM_message ${
|
|
1033
1162
|
user == 'User' ? 'chatLLM_message_self' : 'chatLLM_message_other'
|
|
1034
1163
|
}">
|
|
1035
|
-
<
|
|
1164
|
+
<h3 class="chatLLM_message_user">${user}</h3>
|
|
1036
1165
|
<p class="chatLLM_message_text">${text}</p>
|
|
1037
1166
|
</div>
|
|
1038
1167
|
`;
|
|
@@ -1089,8 +1218,9 @@ class ChatLLM {
|
|
|
1089
1218
|
|
|
1090
1219
|
// first time, send default query
|
|
1091
1220
|
if (this.firstTime) {
|
|
1221
|
+
let LLMName = constants.LLMModel == 'openai' ? 'OpenAI' : 'Gemini';
|
|
1092
1222
|
this.firstTime = false;
|
|
1093
|
-
this.DisplayChatMessage(
|
|
1223
|
+
this.DisplayChatMessage(LLMName, 'Processing Chart...');
|
|
1094
1224
|
this.RunDefaultPrompt();
|
|
1095
1225
|
}
|
|
1096
1226
|
} else {
|
|
@@ -1109,36 +1239,30 @@ class ChatLLM {
|
|
|
1109
1239
|
async ConvertSVGtoJPG(id) {
|
|
1110
1240
|
let svgElement = document.getElementById(id);
|
|
1111
1241
|
return new Promise((resolve, reject) => {
|
|
1112
|
-
// Create a canvas
|
|
1113
1242
|
var canvas = document.createElement('canvas');
|
|
1114
1243
|
var ctx = canvas.getContext('2d');
|
|
1115
1244
|
|
|
1116
|
-
// Get dimensions from the SVG element
|
|
1117
|
-
var svgRect = svgElement.getBoundingClientRect();
|
|
1118
|
-
canvas.width = svgRect.width;
|
|
1119
|
-
canvas.height = svgRect.height;
|
|
1120
|
-
|
|
1121
|
-
// Create an image to draw the SVG
|
|
1122
|
-
var img = new Image();
|
|
1123
|
-
|
|
1124
|
-
// Convert SVG element to a data URL
|
|
1125
1245
|
var svgData = new XMLSerializer().serializeToString(svgElement);
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
var url = URL.createObjectURL(svgBlob);
|
|
1130
|
-
|
|
1131
|
-
img.onload = function () {
|
|
1132
|
-
// Draw the SVG on the canvas
|
|
1133
|
-
ctx.drawImage(img, 0, 0, svgRect.width, svgRect.height);
|
|
1134
|
-
|
|
1135
|
-
// Convert the canvas to JPEG
|
|
1136
|
-
var jpegData = canvas.toDataURL('image/jpeg');
|
|
1246
|
+
if (!svgData.startsWith('<svg xmlns')) {
|
|
1247
|
+
svgData = `<svg xmlns="http://www.w3.org/2000/svg" ${svgData.slice(4)}`;
|
|
1248
|
+
}
|
|
1137
1249
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1250
|
+
var svgSize =
|
|
1251
|
+
svgElement.viewBox.baseVal || svgElement.getBoundingClientRect();
|
|
1252
|
+
canvas.width = svgSize.width;
|
|
1253
|
+
canvas.height = svgSize.height;
|
|
1140
1254
|
|
|
1141
|
-
|
|
1255
|
+
var img = new Image();
|
|
1256
|
+
img.onload = function () {
|
|
1257
|
+
ctx.drawImage(img, 0, 0, svgSize.width, svgSize.height);
|
|
1258
|
+
var jpegData = canvas.toDataURL('image/jpeg', 0.9); // 0.9 is the quality parameter
|
|
1259
|
+
if (constants.LLMModel == 'openai') {
|
|
1260
|
+
resolve(jpegData);
|
|
1261
|
+
} else if (constants.LLMModel == 'gemini') {
|
|
1262
|
+
let base64Data = jpegData.split(',')[1];
|
|
1263
|
+
resolve(base64Data);
|
|
1264
|
+
//resolve(jpegData);
|
|
1265
|
+
}
|
|
1142
1266
|
URL.revokeObjectURL(url);
|
|
1143
1267
|
};
|
|
1144
1268
|
|
|
@@ -1146,30 +1270,14 @@ class ChatLLM {
|
|
|
1146
1270
|
reject(new Error('Error loading SVG'));
|
|
1147
1271
|
};
|
|
1148
1272
|
|
|
1273
|
+
var svgBlob = new Blob([svgData], {
|
|
1274
|
+
type: 'image/svg+xml;charset=utf-8',
|
|
1275
|
+
});
|
|
1276
|
+
var url = URL.createObjectURL(svgBlob);
|
|
1149
1277
|
img.src = url;
|
|
1150
1278
|
});
|
|
1151
1279
|
}
|
|
1152
1280
|
|
|
1153
|
-
downloadJPEG(base64Data, filename) {
|
|
1154
|
-
// Create a link element
|
|
1155
|
-
var link = document.createElement('a');
|
|
1156
|
-
|
|
1157
|
-
// Set the download attribute with a filename
|
|
1158
|
-
link.download = filename;
|
|
1159
|
-
|
|
1160
|
-
// Convert Base64 data to a data URL and set it as the href
|
|
1161
|
-
link.href = base64Data;
|
|
1162
|
-
|
|
1163
|
-
// Append the link to the body (required for Firefox)
|
|
1164
|
-
document.body.appendChild(link);
|
|
1165
|
-
|
|
1166
|
-
// Trigger the download
|
|
1167
|
-
link.click();
|
|
1168
|
-
|
|
1169
|
-
// Clean up
|
|
1170
|
-
document.body.removeChild(link);
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
1281
|
/**
|
|
1174
1282
|
* RunDefaultPrompt is an asynchronous function that generates a prompt for describing a chart to a blind person.
|
|
1175
1283
|
* It converts the chart to a JPG image using the ConvertSVGtoJPG method and then submits the prompt to the chatLLM function.
|
|
@@ -1178,7 +1286,6 @@ class ChatLLM {
|
|
|
1178
1286
|
async RunDefaultPrompt() {
|
|
1179
1287
|
//let img = await this.ConvertSVGtoImg(singleMaidr.id);
|
|
1180
1288
|
let img = await this.ConvertSVGtoJPG(singleMaidr.id);
|
|
1181
|
-
//this.downloadJPEG(img, 'test.jpg'); // test download
|
|
1182
1289
|
let text = 'Describe this chart to a blind person';
|
|
1183
1290
|
if (constants.skillLevel) {
|
|
1184
1291
|
if (constants.skillLevel == 'other' && constants.skillLevelOther) {
|
|
@@ -1230,7 +1337,7 @@ class Description {
|
|
|
1230
1337
|
<div class="modal-dialog" role="document" tabindex="0">
|
|
1231
1338
|
<div class="modal-content">
|
|
1232
1339
|
<div class="modal-header">
|
|
1233
|
-
<
|
|
1340
|
+
<h2 id="desc_title" class="modal-title">Description</h2>
|
|
1234
1341
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
1235
1342
|
<span aria-hidden="true">×</span>
|
|
1236
1343
|
</button>
|
|
@@ -2022,7 +2129,7 @@ class Audio {
|
|
|
2022
2129
|
panning = 0;
|
|
2023
2130
|
}
|
|
2024
2131
|
} else if (constants.chartType == 'heat') {
|
|
2025
|
-
rawFreq = plot.
|
|
2132
|
+
rawFreq = plot.data[position.y][position.x];
|
|
2026
2133
|
rawPanning = position.x;
|
|
2027
2134
|
frequency = this.SlideBetween(
|
|
2028
2135
|
rawFreq,
|
|
@@ -2818,7 +2925,7 @@ class Display {
|
|
|
2818
2925
|
plot.fill +
|
|
2819
2926
|
' is ';
|
|
2820
2927
|
// if (constants.hasRect) {
|
|
2821
|
-
verboseText += plot.
|
|
2928
|
+
verboseText += plot.data[position.y][position.x];
|
|
2822
2929
|
// }
|
|
2823
2930
|
} else {
|
|
2824
2931
|
verboseText +=
|
|
@@ -2833,7 +2940,7 @@ class Display {
|
|
|
2833
2940
|
plot.fill +
|
|
2834
2941
|
' is ';
|
|
2835
2942
|
// if (constants.hasRect) {
|
|
2836
|
-
verboseText += plot.
|
|
2943
|
+
verboseText += plot.data[position.y][position.x];
|
|
2837
2944
|
// }
|
|
2838
2945
|
}
|
|
2839
2946
|
// terse and verbose alternate between columns and rows
|
|
@@ -2847,7 +2954,7 @@ class Display {
|
|
|
2847
2954
|
'<p>' +
|
|
2848
2955
|
plot.x_labels[position.x] +
|
|
2849
2956
|
', ' +
|
|
2850
|
-
plot.
|
|
2957
|
+
plot.data[position.y][position.x] +
|
|
2851
2958
|
'</p>\n';
|
|
2852
2959
|
} else {
|
|
2853
2960
|
// row navigation
|
|
@@ -2855,7 +2962,7 @@ class Display {
|
|
|
2855
2962
|
'<p>' +
|
|
2856
2963
|
plot.y_labels[position.y] +
|
|
2857
2964
|
', ' +
|
|
2858
|
-
plot.
|
|
2965
|
+
plot.data[position.y][position.x] +
|
|
2859
2966
|
'</p>\n';
|
|
2860
2967
|
}
|
|
2861
2968
|
} else if (constants.textMode == 'verbose') {
|
|
@@ -3032,11 +3139,11 @@ class Display {
|
|
|
3032
3139
|
} else if (constants.chartType == 'line') {
|
|
3033
3140
|
// line layer
|
|
3034
3141
|
verboseText +=
|
|
3035
|
-
plot.
|
|
3142
|
+
plot.plotLegend.x +
|
|
3036
3143
|
' is ' +
|
|
3037
3144
|
plot.pointValuesX[position.x] +
|
|
3038
3145
|
', ' +
|
|
3039
|
-
plot.
|
|
3146
|
+
plot.plotLegend.y +
|
|
3040
3147
|
' is ' +
|
|
3041
3148
|
plot.pointValuesY[position.x];
|
|
3042
3149
|
|
|
@@ -3709,6 +3816,8 @@ class BarChart {
|
|
|
3709
3816
|
let elements = null;
|
|
3710
3817
|
if ('selector' in singleMaidr) {
|
|
3711
3818
|
elements = document.querySelectorAll(singleMaidr.selector);
|
|
3819
|
+
} else if ('elements' in singleMaidr) {
|
|
3820
|
+
elements = singleMaidr.elements;
|
|
3712
3821
|
}
|
|
3713
3822
|
|
|
3714
3823
|
if (xlevel && data && elements) {
|
|
@@ -3745,15 +3854,6 @@ class BarChart {
|
|
|
3745
3854
|
logError.LogAbsentElement('elements');
|
|
3746
3855
|
}
|
|
3747
3856
|
|
|
3748
|
-
// bars. The actual bar elements in the SVG. Used to highlight visually
|
|
3749
|
-
// if ('elements' in singleMaidr) {
|
|
3750
|
-
// this.bars = singleMaidr.elements;
|
|
3751
|
-
// constants.hasRect = 1;
|
|
3752
|
-
// } else {
|
|
3753
|
-
// // this.bars = constants.chart.querySelectorAll('g[id^="geom_rect"] > rect'); // if we use plot.plotData.length instead of plot.bars.length, we don't have to include this
|
|
3754
|
-
// constants.hasRect = 0;
|
|
3755
|
-
// }
|
|
3756
|
-
|
|
3757
3857
|
// column labels, both legend and tick
|
|
3758
3858
|
this.columnLabels = [];
|
|
3759
3859
|
let legendX = '';
|
|
@@ -3829,10 +3929,8 @@ class BarChart {
|
|
|
3829
3929
|
|
|
3830
3930
|
if (Array.isArray(singleMaidr)) {
|
|
3831
3931
|
this.plotData = singleMaidr;
|
|
3832
|
-
} else {
|
|
3833
|
-
|
|
3834
|
-
this.plotData = singleMaidr.data;
|
|
3835
|
-
}
|
|
3932
|
+
} else if ('data' in singleMaidr) {
|
|
3933
|
+
this.plotData = singleMaidr.data;
|
|
3836
3934
|
}
|
|
3837
3935
|
|
|
3838
3936
|
// set the max and min values for the plot
|
|
@@ -4023,8 +4121,6 @@ class BoxPlot {
|
|
|
4023
4121
|
* @constructor
|
|
4024
4122
|
*/
|
|
4025
4123
|
constructor() {
|
|
4026
|
-
constants.plotOrientation = 'horz'; // default
|
|
4027
|
-
|
|
4028
4124
|
// the default sections for all boxplots
|
|
4029
4125
|
this.sections = [
|
|
4030
4126
|
'lower_outlier',
|
|
@@ -4036,6 +4132,8 @@ class BoxPlot {
|
|
|
4036
4132
|
'upper_outlier',
|
|
4037
4133
|
];
|
|
4038
4134
|
|
|
4135
|
+
// set orientation
|
|
4136
|
+
constants.plotOrientation = 'horz';
|
|
4039
4137
|
if ('axes' in singleMaidr) {
|
|
4040
4138
|
if ('x' in singleMaidr.axes) {
|
|
4041
4139
|
if ('level' in singleMaidr.axes.x) {
|
|
@@ -4116,7 +4214,11 @@ class BoxPlot {
|
|
|
4116
4214
|
|
|
4117
4215
|
// bounds data
|
|
4118
4216
|
if ('selector' in singleMaidr) {
|
|
4119
|
-
|
|
4217
|
+
let elements = document.querySelector(singleMaidr.selector);
|
|
4218
|
+
this.plotBounds = this.GetPlotBounds(elements);
|
|
4219
|
+
constants.hasRect = true;
|
|
4220
|
+
} else if ('elements' in singleMaidr) {
|
|
4221
|
+
this.plotBounds = this.GetPlotBounds(singleMaidr.elements);
|
|
4120
4222
|
constants.hasRect = true;
|
|
4121
4223
|
} else {
|
|
4122
4224
|
constants.hasRect = false;
|
|
@@ -4202,7 +4304,7 @@ class BoxPlot {
|
|
|
4202
4304
|
* Calculates the bounding boxes for all elements in the parent element, including outliers, whiskers, and range.
|
|
4203
4305
|
* @returns {Array} An array of bounding boxes for all elements.
|
|
4204
4306
|
*/
|
|
4205
|
-
GetPlotBounds() {
|
|
4307
|
+
GetPlotBounds(elements) {
|
|
4206
4308
|
// we fetch the elements in our parent,
|
|
4207
4309
|
// and similar to old GetData we run through and get bounding boxes (or blanks) for everything,
|
|
4208
4310
|
// and store in an identical structure
|
|
@@ -4210,7 +4312,6 @@ class BoxPlot {
|
|
|
4210
4312
|
let plotBounds = [];
|
|
4211
4313
|
let allWeNeed = this.GetAllSegmentTypes();
|
|
4212
4314
|
let re = /(?:\d+(?:\.\d*)?|\.\d+)/g;
|
|
4213
|
-
let elements = document.querySelector(singleMaidr.selector);
|
|
4214
4315
|
|
|
4215
4316
|
// get initial set of elements, a parent element for all outliers, whiskers, and range
|
|
4216
4317
|
let initialElemSet = [];
|
|
@@ -4781,100 +4882,36 @@ class HeatMap {
|
|
|
4781
4882
|
}
|
|
4782
4883
|
}
|
|
4783
4884
|
}
|
|
4784
|
-
let data = null;
|
|
4785
|
-
let dataLength = 0;
|
|
4786
4885
|
if ('data' in singleMaidr) {
|
|
4787
|
-
data = singleMaidr.data;
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4886
|
+
this.data = singleMaidr.data;
|
|
4887
|
+
this.num_rows = this.data.length;
|
|
4888
|
+
this.num_cols = this.data[0].length;
|
|
4889
|
+
} else {
|
|
4890
|
+
// critical error, no data
|
|
4891
|
+
console.error('No data found in singleMaidr object');
|
|
4791
4892
|
}
|
|
4792
|
-
let elements = null;
|
|
4793
4893
|
if ('selector' in singleMaidr) {
|
|
4794
|
-
elements = document.querySelectorAll(singleMaidr.selector);
|
|
4894
|
+
this.elements = document.querySelectorAll(singleMaidr.selector);
|
|
4895
|
+
constants.hasRect = 1;
|
|
4896
|
+
} else if ('elements' in singleMaidr) {
|
|
4897
|
+
this.elements = singleMaidr.elements;
|
|
4898
|
+
constants.hasRect = 1;
|
|
4899
|
+
} else {
|
|
4900
|
+
this.elements = null;
|
|
4901
|
+
constants.hasRect = 0;
|
|
4795
4902
|
}
|
|
4796
4903
|
|
|
4797
|
-
// if (xlevel && ylevel && data && elements) {
|
|
4798
|
-
// if (elements.length != dataLength) {
|
|
4799
|
-
// // I didn't throw an error but give a warning
|
|
4800
|
-
// constants.hasRect = 0;
|
|
4801
|
-
// logError.LogDifferentLengths('data', 'elements');
|
|
4802
|
-
// } else if (ylevel.length != data.length) {
|
|
4803
|
-
// constants.hasRect = 0;
|
|
4804
|
-
// logError.logDifferentLengths('y level', 'rows');
|
|
4805
|
-
// } else if (data[0].length != xlevel.length) {
|
|
4806
|
-
// constants.hasRect = 0;
|
|
4807
|
-
// logError.logDifferentLengths('x level', 'columns');
|
|
4808
|
-
// } else {
|
|
4809
|
-
// this.plots = elements;
|
|
4810
|
-
// constants.hasRect = 1;
|
|
4811
|
-
// }
|
|
4812
|
-
// } else if (ylevel && data && elements) {
|
|
4813
|
-
// if (dataLength != elements.length) {
|
|
4814
|
-
// constants.hasRect = 0;
|
|
4815
|
-
// logError.logDifferentLengths('data', 'elements');
|
|
4816
|
-
// } else if (ylevel.length != data.length) {
|
|
4817
|
-
// constants.hasRect = 0;
|
|
4818
|
-
// logError.logDifferentLengths('y level', 'rows');
|
|
4819
|
-
// } else {
|
|
4820
|
-
// this.plots = elements;
|
|
4821
|
-
// constants.hasRect = 1;
|
|
4822
|
-
// }
|
|
4823
|
-
// } else if (xlevel && data && elements) {
|
|
4824
|
-
// if (dataLength != elements.length) {
|
|
4825
|
-
// constants.hasRect = 0;
|
|
4826
|
-
// logError.logDifferentLengths('data', 'elements');
|
|
4827
|
-
// } else if (xlevel.length != data[0].length) {
|
|
4828
|
-
// constants.hasRect = 0;
|
|
4829
|
-
// logError.logDifferentLengths('x level', 'columns');
|
|
4830
|
-
// } else {
|
|
4831
|
-
// this.plots = elements;
|
|
4832
|
-
// constants.hasRect = 1;
|
|
4833
|
-
// }
|
|
4834
|
-
// }
|
|
4835
|
-
// else if (xlevel && ylevel && data) {
|
|
4836
|
-
// constants.hasRect = 0;
|
|
4837
|
-
// if (ylevel.length != data.length) {
|
|
4838
|
-
// logError.logDifferentLengths('y level', 'rows');
|
|
4839
|
-
// } else if (data[0].length != xlevel.length) {
|
|
4840
|
-
// logError.logDifferentLengths('x level', 'columns');
|
|
4841
|
-
// }
|
|
4842
|
-
// logError.LogAbsentElement('elements');
|
|
4843
|
-
// }
|
|
4844
|
-
// else if (data && elements) {
|
|
4845
|
-
// if (dataLength != elements.length) {
|
|
4846
|
-
// constants.hasRect = 0;
|
|
4847
|
-
// logError.logDifferentLengths('data', 'elements');
|
|
4848
|
-
// } else {
|
|
4849
|
-
// this.plots = elements;
|
|
4850
|
-
// constants.hasRect = 1;
|
|
4851
|
-
// }
|
|
4852
|
-
// } else if (data) {
|
|
4853
|
-
// constants.hasRect = 0;
|
|
4854
|
-
// if (!xlevel) logError.LogAbsentElement('x level');
|
|
4855
|
-
// if (!ylevel) logError.LogAbsentElement('y level');
|
|
4856
|
-
// if (!elements) logError.LogAbsentElement('elements');
|
|
4857
|
-
// }
|
|
4858
|
-
|
|
4859
|
-
this.plots = elements;
|
|
4860
|
-
constants.hasRect = 1;
|
|
4861
|
-
|
|
4862
4904
|
this.group_labels = this.getGroupLabels();
|
|
4863
|
-
// this.x_labels = this.getXLabels();
|
|
4864
|
-
// this.y_labels = this.getYLabels();
|
|
4865
4905
|
this.x_labels = xlevel;
|
|
4866
4906
|
this.y_labels = ylevel;
|
|
4867
4907
|
this.title = this.getTitle();
|
|
4868
4908
|
this.fill = this.getFill();
|
|
4869
4909
|
|
|
4870
|
-
|
|
4871
|
-
|
|
4910
|
+
if (constants.hasRect) {
|
|
4911
|
+
this.SetHeatmapRectData();
|
|
4912
|
+
}
|
|
4872
4913
|
|
|
4873
|
-
this.
|
|
4874
|
-
this.y_coord = this.plotData[1];
|
|
4875
|
-
this.values = this.plotData[2];
|
|
4876
|
-
this.num_rows = this.plotData[3];
|
|
4877
|
-
this.num_cols = this.plotData[4];
|
|
4914
|
+
this.updateConstants();
|
|
4878
4915
|
|
|
4879
4916
|
this.x_group_label = this.group_labels[0].trim();
|
|
4880
4917
|
this.y_group_label = this.group_labels[1].trim();
|
|
@@ -4885,111 +4922,77 @@ class HeatMap {
|
|
|
4885
4922
|
* If 'data' exists in singleMaidr, it returns the norms from the data. Otherwise, it calculates the norms from the unique x and y coordinates.
|
|
4886
4923
|
* @returns {Array} An array of heatmap data containing unique x and y coordinates, norms, number of rows, and number of columns.
|
|
4887
4924
|
*/
|
|
4888
|
-
|
|
4925
|
+
SetHeatmapRectData() {
|
|
4926
|
+
// We get a set of x and y coordinates from the heatmap squares,
|
|
4927
|
+
// which is different and only sometimes connected to the actual data
|
|
4928
|
+
// note, only runs if constants.hasRect is true
|
|
4929
|
+
|
|
4889
4930
|
// get the x_coord and y_coord to check if a square exists at the coordinates
|
|
4890
4931
|
let x_coord_check = [];
|
|
4891
4932
|
let y_coord_check = [];
|
|
4892
|
-
|
|
4893
4933
|
let unique_x_coord = [];
|
|
4894
4934
|
let unique_y_coord = [];
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4935
|
+
for (let i = 0; i < this.elements.length; i++) {
|
|
4936
|
+
if (this.elements[i]) {
|
|
4937
|
+
// heatmap SVG containing path element instead of rect
|
|
4938
|
+
if (this.elements[i] instanceof SVGPathElement) {
|
|
4939
|
+
// Assuming the path data is in the format "M x y L x y L x y L x y"
|
|
4940
|
+
const path_d = this.elements[i].getAttribute('d');
|
|
4941
|
+
const regex = /[ML]\s*(-?\d+(\.\d+)?)\s+(-?\d+(\.\d+)?)/g;
|
|
4942
|
+
const match = regex.exec(path_d);
|
|
4943
|
+
|
|
4944
|
+
const coords = [Number(match[1]), Number(match[3])];
|
|
4945
|
+
const x = coords[0];
|
|
4946
|
+
const y = coords[1];
|
|
4947
|
+
|
|
4948
|
+
x_coord_check.push(parseFloat(x));
|
|
4949
|
+
y_coord_check.push(parseFloat(y));
|
|
4950
|
+
} else {
|
|
4951
|
+
x_coord_check.push(parseFloat(this.elements[i].getAttribute('x')));
|
|
4952
|
+
y_coord_check.push(parseFloat(this.elements[i].getAttribute('y')));
|
|
4913
4953
|
}
|
|
4914
4954
|
}
|
|
4915
|
-
|
|
4916
|
-
// sort the squares to access from left to right, up to down
|
|
4917
|
-
x_coord_check.sort(function (a, b) {
|
|
4918
|
-
return a - b;
|
|
4919
|
-
}); // ascending
|
|
4920
|
-
y_coord_check.sort(function (a, b) {
|
|
4921
|
-
return a - b;
|
|
4922
|
-
});
|
|
4923
|
-
|
|
4924
|
-
let svgScaler = this.GetSVGScaler();
|
|
4925
|
-
// inverse scale if svg is scaled
|
|
4926
|
-
if (svgScaler[0] == -1) {
|
|
4927
|
-
x_coord_check = x_coord_check.reverse();
|
|
4928
|
-
}
|
|
4929
|
-
if (svgScaler[1] == -1) {
|
|
4930
|
-
y_coord_check = y_coord_check.reverse();
|
|
4931
|
-
}
|
|
4932
|
-
|
|
4933
|
-
// get unique elements from x_coord and y_coord
|
|
4934
|
-
unique_x_coord = [...new Set(x_coord_check)];
|
|
4935
|
-
unique_y_coord = [...new Set(y_coord_check)];
|
|
4936
|
-
}
|
|
4937
|
-
|
|
4938
|
-
// get num of rows, num of cols, and total numbers of squares
|
|
4939
|
-
let num_rows = 0;
|
|
4940
|
-
let num_cols = 0;
|
|
4941
|
-
let num_squares = 0;
|
|
4942
|
-
if ('data' in singleMaidr) {
|
|
4943
|
-
num_rows = singleMaidr.data.length;
|
|
4944
|
-
num_cols = singleMaidr.data[0].length;
|
|
4945
|
-
} else {
|
|
4946
|
-
num_rows = unique_y_coord.length;
|
|
4947
|
-
num_cols = unique_x_coord.length;
|
|
4948
4955
|
}
|
|
4949
|
-
num_squares = num_rows * num_cols;
|
|
4950
4956
|
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
}
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
let min_norm = 3 * Math.pow(255, 2);
|
|
4959
|
-
let max_norm = 0;
|
|
4960
|
-
|
|
4961
|
-
for (var i = 0; i < this.plots.length; i++) {
|
|
4962
|
-
var x_index = unique_x_coord.indexOf(x_coord_check[i]);
|
|
4963
|
-
var y_index = unique_y_coord.indexOf(y_coord_check[i]);
|
|
4964
|
-
let norm = this.getRGBNorm(i);
|
|
4965
|
-
norms[y_index][x_index] = norm;
|
|
4957
|
+
// sort the squares to access from left to right, up to down
|
|
4958
|
+
x_coord_check.sort(function (a, b) {
|
|
4959
|
+
return a - b;
|
|
4960
|
+
}); // ascending
|
|
4961
|
+
y_coord_check.sort(function (a, b) {
|
|
4962
|
+
return a - b;
|
|
4963
|
+
});
|
|
4966
4964
|
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4965
|
+
let svgScaler = this.GetSVGScaler();
|
|
4966
|
+
// inverse scale if svg has a negative scale in the actual svg
|
|
4967
|
+
if (svgScaler[0] == -1) {
|
|
4968
|
+
x_coord_check = x_coord_check.reverse();
|
|
4969
|
+
}
|
|
4970
|
+
if (svgScaler[1] == -1) {
|
|
4971
|
+
y_coord_check = y_coord_check.reverse();
|
|
4970
4972
|
}
|
|
4971
4973
|
|
|
4972
|
-
|
|
4974
|
+
// get unique elements from x_coord and y_coord
|
|
4975
|
+
unique_x_coord = [...new Set(x_coord_check)];
|
|
4976
|
+
unique_y_coord = [...new Set(y_coord_check)];
|
|
4973
4977
|
|
|
4974
|
-
|
|
4978
|
+
this.x_coord = unique_x_coord;
|
|
4979
|
+
this.y_coord = unique_y_coord;
|
|
4975
4980
|
}
|
|
4976
4981
|
|
|
4977
4982
|
/**
|
|
4978
4983
|
* Updates the constants used in the heatmap.
|
|
4984
|
+
* minX: 0, always
|
|
4985
|
+
* maxX: the x length of the data array
|
|
4986
|
+
* minY: the minimum value of the data array
|
|
4987
|
+
* maxY: the maximum value of the data array
|
|
4988
|
+
* autoPlayRate: the rate at which the heatmap will autoplay, based on the number of columns
|
|
4989
|
+
*
|
|
4979
4990
|
*/
|
|
4980
4991
|
updateConstants() {
|
|
4981
4992
|
constants.minX = 0;
|
|
4982
|
-
constants.maxX = this.
|
|
4983
|
-
constants.minY = this.
|
|
4984
|
-
constants.maxY = this.
|
|
4985
|
-
for (let i = 0; i < this.plotData[2].length; i++) {
|
|
4986
|
-
for (let j = 0; j < this.plotData[2][i].length; j++) {
|
|
4987
|
-
if (this.plotData[2][i][j] < constants.minY)
|
|
4988
|
-
constants.minY = this.plotData[2][i][j];
|
|
4989
|
-
if (this.plotData[2][i][j] > constants.maxY)
|
|
4990
|
-
constants.maxY = this.plotData[2][i][j];
|
|
4991
|
-
}
|
|
4992
|
-
}
|
|
4993
|
+
constants.maxX = this.data[0].length - 1;
|
|
4994
|
+
constants.minY = Math.min(...this.data.map((row) => Math.min(...row)));
|
|
4995
|
+
constants.maxY = Math.max(...this.data.map((row) => Math.max(...row)));
|
|
4993
4996
|
constants.autoPlayRate = Math.min(
|
|
4994
4997
|
Math.ceil(constants.AUTOPLAY_DURATION / (constants.maxX + 1)),
|
|
4995
4998
|
constants.MAX_SPEED
|
|
@@ -5008,7 +5011,7 @@ class HeatMap {
|
|
|
5008
5011
|
}
|
|
5009
5012
|
|
|
5010
5013
|
/**
|
|
5011
|
-
* Returns an array of the X and Y scales of the first SVG element found in the
|
|
5014
|
+
* Returns an array of the X and Y scales of the first SVG element found in the elements array.
|
|
5012
5015
|
* @returns {Array<number>} An array containing the X and Y scales of the SVG element.
|
|
5013
5016
|
*/
|
|
5014
5017
|
GetSVGScaler() {
|
|
@@ -5018,7 +5021,7 @@ class HeatMap {
|
|
|
5018
5021
|
|
|
5019
5022
|
// but first, are we even in an svg that can be scaled?
|
|
5020
5023
|
let isSvg = false;
|
|
5021
|
-
let element = this.
|
|
5024
|
+
let element = this.elements[0]; // a random start, may as well be the first
|
|
5022
5025
|
while (element) {
|
|
5023
5026
|
if (element.tagName.toLowerCase() == 'body') {
|
|
5024
5027
|
break;
|
|
@@ -5030,7 +5033,7 @@ class HeatMap {
|
|
|
5030
5033
|
}
|
|
5031
5034
|
|
|
5032
5035
|
if (isSvg) {
|
|
5033
|
-
let element = this.
|
|
5036
|
+
let element = this.elements[0]; // a random start, may as well be the first
|
|
5034
5037
|
while (element) {
|
|
5035
5038
|
if (element.tagName.toLowerCase() == 'body') {
|
|
5036
5039
|
break;
|
|
@@ -5062,7 +5065,7 @@ class HeatMap {
|
|
|
5062
5065
|
* @returns {number} The sum of squared values of the RGB color.
|
|
5063
5066
|
*/
|
|
5064
5067
|
getRGBNorm(i) {
|
|
5065
|
-
let rgb_string = this.
|
|
5068
|
+
let rgb_string = this.elements[i].getAttribute('fill');
|
|
5066
5069
|
let rgb_array = rgb_string.slice(4, -1).split(',');
|
|
5067
5070
|
// just get the sum of squared value of rgb, similar without sqrt, save computation
|
|
5068
5071
|
return rgb_array
|
|
@@ -5220,10 +5223,10 @@ class HeatMapRect {
|
|
|
5220
5223
|
this.x = plot.x_coord[position.x];
|
|
5221
5224
|
this.y = plot.y_coord[position.y];
|
|
5222
5225
|
// find which square we're on by searching for the x and y coordinates
|
|
5223
|
-
for (let i = 0; i < plot.
|
|
5226
|
+
for (let i = 0; i < plot.elements.length; i++) {
|
|
5224
5227
|
if (
|
|
5225
|
-
plot.
|
|
5226
|
-
plot.
|
|
5228
|
+
plot.elements[i].getAttribute('x') == this.x &&
|
|
5229
|
+
plot.elements[i].getAttribute('y') == this.y
|
|
5227
5230
|
) {
|
|
5228
5231
|
this.squareIndex = i;
|
|
5229
5232
|
break;
|
|
@@ -5251,7 +5254,7 @@ class HeatMapRect {
|
|
|
5251
5254
|
rect.setAttribute('stroke', constants.colorSelected);
|
|
5252
5255
|
rect.setAttribute('stroke-width', this.rectStrokeWidth);
|
|
5253
5256
|
rect.setAttribute('fill', 'none');
|
|
5254
|
-
plot.
|
|
5257
|
+
plot.elements[this.squareIndex].parentNode.appendChild(rect);
|
|
5255
5258
|
//constants.chart.appendChild(rect);
|
|
5256
5259
|
}
|
|
5257
5260
|
}
|
|
@@ -5267,87 +5270,12 @@ class ScatterPlot {
|
|
|
5267
5270
|
*/
|
|
5268
5271
|
constructor() {
|
|
5269
5272
|
this.prefix = this.GetPrefix();
|
|
5270
|
-
// this.SetVisualHighlight();
|
|
5271
5273
|
this.SetScatterLayer();
|
|
5272
5274
|
this.SetLineLayer();
|
|
5273
5275
|
this.SetAxes();
|
|
5274
5276
|
this.svgScaler = this.GetSVGScaler();
|
|
5275
5277
|
}
|
|
5276
5278
|
|
|
5277
|
-
// SetVisualHighlight() {
|
|
5278
|
-
// let point_index = this.GetElementIndex('point');
|
|
5279
|
-
// let smooth_index = this.GetElementIndex('smooth');
|
|
5280
|
-
// if (point_index && smooth_index && elements < 2) {
|
|
5281
|
-
// logError.LogAbsentElement('point or/and smooth line elements');
|
|
5282
|
-
// }
|
|
5283
|
-
// if (point_index != -1) {
|
|
5284
|
-
// this.CheckData(point_index);
|
|
5285
|
-
// }
|
|
5286
|
-
|
|
5287
|
-
// if (smooth_index != -1) {
|
|
5288
|
-
// this.CheckData(smooth_index);
|
|
5289
|
-
// }
|
|
5290
|
-
// }
|
|
5291
|
-
|
|
5292
|
-
// CheckData(i) {
|
|
5293
|
-
// let elements = 'elements' in singleMaidr ? singleMaidr.elements : null;
|
|
5294
|
-
|
|
5295
|
-
// // elements does not exist at all
|
|
5296
|
-
// if (elements == null) {
|
|
5297
|
-
// logError.LogAbsentElement('elements');
|
|
5298
|
-
// if (i == 0) constants.hasRect = 0;
|
|
5299
|
-
// if (i == 1) constants.hasSmooth = 0;
|
|
5300
|
-
// return;
|
|
5301
|
-
// }
|
|
5302
|
-
|
|
5303
|
-
// // elements exists but is empty
|
|
5304
|
-
// if (elements.length == 0) {
|
|
5305
|
-
// logError.LogAbsentElement('elements');
|
|
5306
|
-
// if (i == 0) constants.hasRect = 0;
|
|
5307
|
-
// if (i == 1) constants.hasSmooth = 0;
|
|
5308
|
-
// return;
|
|
5309
|
-
// }
|
|
5310
|
-
|
|
5311
|
-
// // elements exists but is not an array
|
|
5312
|
-
// if (!Array.isArray(elements)) {
|
|
5313
|
-
// logError.LogNotArray('elements');
|
|
5314
|
-
// if (i == 0) constants.hasRect = 0;
|
|
5315
|
-
// if (i == 1) constants.hasSmooth = 0;
|
|
5316
|
-
// return;
|
|
5317
|
-
// }
|
|
5318
|
-
|
|
5319
|
-
// // elements.length is more than 2
|
|
5320
|
-
// if (elements.length > 2) {
|
|
5321
|
-
// logError.LogTooManyElements('elements', 2);
|
|
5322
|
-
// }
|
|
5323
|
-
|
|
5324
|
-
// if ('data' in singleMaidr) {
|
|
5325
|
-
// if (i == 0) {
|
|
5326
|
-
// // check point elements
|
|
5327
|
-
// if (
|
|
5328
|
-
// singleMaidr.data[i] == null ||
|
|
5329
|
-
// singleMaidr.data[i].length != singleMaidr.elements[i].length
|
|
5330
|
-
// ) {
|
|
5331
|
-
// constants.hasRect = 0;
|
|
5332
|
-
// logError.LogDifferentLengths('point data', 'point elements');
|
|
5333
|
-
// }
|
|
5334
|
-
// } else if (i == 1) {
|
|
5335
|
-
// // check smooth line elements
|
|
5336
|
-
// if (
|
|
5337
|
-
// singleMaidr.data[i] == null ||
|
|
5338
|
-
// (!Array.isArray(singleMaidr.data[i]) &&
|
|
5339
|
-
// singleMaidr.data[i].length != this.chartLineX.length)
|
|
5340
|
-
// ) {
|
|
5341
|
-
// constants.hasSmooth = 0;
|
|
5342
|
-
// logError.LogDifferentLengths(
|
|
5343
|
-
// 'smooth line data',
|
|
5344
|
-
// 'smooth line elements'
|
|
5345
|
-
// );
|
|
5346
|
-
// }
|
|
5347
|
-
// }
|
|
5348
|
-
// }
|
|
5349
|
-
// }
|
|
5350
|
-
|
|
5351
5279
|
/**
|
|
5352
5280
|
* Sets the x and y group labels and title for the scatterplot based on the data in singleMaidr.
|
|
5353
5281
|
*/
|
|
@@ -5393,28 +5321,34 @@ class ScatterPlot {
|
|
|
5393
5321
|
*/
|
|
5394
5322
|
SetScatterLayer() {
|
|
5395
5323
|
// initially set as smooth layer (layer 2), if possible
|
|
5396
|
-
let elIndex = this.GetElementIndex('point');
|
|
5324
|
+
let elIndex = this.GetElementIndex('point'); // check if we have it
|
|
5397
5325
|
if (elIndex != -1) {
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5326
|
+
if ('selector' in singleMaidr) {
|
|
5327
|
+
this.plotPoints = document.querySelectorAll(
|
|
5328
|
+
singleMaidr.selector[elIndex]
|
|
5329
|
+
);
|
|
5330
|
+
} else if ('elements' in singleMaidr) {
|
|
5331
|
+
this.plotPoints = singleMaidr.elements[elIndex];
|
|
5332
|
+
}
|
|
5401
5333
|
} else if (singleMaidr.type == 'point') {
|
|
5402
|
-
|
|
5334
|
+
if ('selector' in singleMaidr) {
|
|
5335
|
+
this.plotPoints = document.querySelectorAll(singleMaidr.selector);
|
|
5336
|
+
} else if ('elements' in singleMaidr) {
|
|
5337
|
+
this.plotPoints = singleMaidr.elements;
|
|
5338
|
+
}
|
|
5403
5339
|
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
let pointValues = this.GetPointValues();
|
|
5340
|
+
let svgPointCoords = this.GetSvgPointCoords();
|
|
5341
|
+
let pointValues = this.GetPointValues();
|
|
5407
5342
|
|
|
5408
|
-
|
|
5409
|
-
|
|
5343
|
+
this.chartPointsX = svgPointCoords[0]; // x coordinates of points
|
|
5344
|
+
this.chartPointsY = svgPointCoords[1]; // y coordinates of points
|
|
5410
5345
|
|
|
5411
|
-
|
|
5412
|
-
|
|
5346
|
+
this.x = pointValues[0]; // actual values of x
|
|
5347
|
+
this.y = pointValues[1]; // actual values of y
|
|
5413
5348
|
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
}
|
|
5349
|
+
// for sound weight use
|
|
5350
|
+
this.points_count = pointValues[2]; // number of each points
|
|
5351
|
+
this.max_count = pointValues[3];
|
|
5418
5352
|
}
|
|
5419
5353
|
|
|
5420
5354
|
/**
|
|
@@ -5422,28 +5356,34 @@ class ScatterPlot {
|
|
|
5422
5356
|
*/
|
|
5423
5357
|
SetLineLayer() {
|
|
5424
5358
|
// layer = 2, smooth layer (from singleMaidr types)
|
|
5425
|
-
let elIndex = this.GetElementIndex('smooth');
|
|
5359
|
+
let elIndex = this.GetElementIndex('smooth'); // check if we have it
|
|
5426
5360
|
if (elIndex != -1) {
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5361
|
+
if ('selector' in singleMaidr) {
|
|
5362
|
+
this.plotLine = document.querySelectorAll(
|
|
5363
|
+
singleMaidr.selector[elIndex]
|
|
5364
|
+
)[0];
|
|
5365
|
+
} else if ('elements' in singleMaidr) {
|
|
5366
|
+
this.plotLine = singleMaidr.elements[elIndex][0];
|
|
5367
|
+
}
|
|
5430
5368
|
} else if (singleMaidr.type == 'smooth') {
|
|
5431
|
-
|
|
5369
|
+
if ('selector' in singleMaidr) {
|
|
5370
|
+
this.plotLine = document.querySelectorAll(singleMaidr.selector);
|
|
5371
|
+
} else if ('elements' in singleMaidr) {
|
|
5372
|
+
this.plotLine = singleMaidr.elements;
|
|
5373
|
+
}
|
|
5432
5374
|
}
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
let smoothCurvePoints = this.GetSmoothCurvePoints();
|
|
5375
|
+
let svgLineCoords = this.GetSvgLineCoords();
|
|
5376
|
+
let smoothCurvePoints = this.GetSmoothCurvePoints();
|
|
5436
5377
|
|
|
5437
|
-
|
|
5438
|
-
|
|
5378
|
+
this.chartLineX = svgLineCoords[0]; // x coordinates of curve
|
|
5379
|
+
this.chartLineY = svgLineCoords[1]; // y coordinates of curve
|
|
5439
5380
|
|
|
5440
|
-
|
|
5441
|
-
|
|
5381
|
+
this.curveX = smoothCurvePoints[0]; // actual values of x
|
|
5382
|
+
this.curvePoints = smoothCurvePoints[1]; // actual values of y
|
|
5442
5383
|
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
}
|
|
5384
|
+
this.curveMinY = Math.min(...this.curvePoints);
|
|
5385
|
+
this.curveMaxY = Math.max(...this.curvePoints);
|
|
5386
|
+
this.gradient = this.GetGradient();
|
|
5447
5387
|
}
|
|
5448
5388
|
|
|
5449
5389
|
/**
|
|
@@ -5453,13 +5393,33 @@ class ScatterPlot {
|
|
|
5453
5393
|
GetSvgPointCoords() {
|
|
5454
5394
|
let points = new Map();
|
|
5455
5395
|
|
|
5456
|
-
|
|
5457
|
-
let
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
points.
|
|
5461
|
-
|
|
5462
|
-
|
|
5396
|
+
if (this.plotPoints) {
|
|
5397
|
+
for (let i = 0; i < this.plotPoints.length; i++) {
|
|
5398
|
+
let x = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'x')); // .toFixed(1);
|
|
5399
|
+
let y = parseFloat(this.plotPoints[i].getAttribute(this.prefix + 'y'));
|
|
5400
|
+
if (!points.has(x)) {
|
|
5401
|
+
points.set(x, new Set([y]));
|
|
5402
|
+
} else {
|
|
5403
|
+
points.get(x).add(y);
|
|
5404
|
+
}
|
|
5405
|
+
}
|
|
5406
|
+
} else {
|
|
5407
|
+
// pull from data instead
|
|
5408
|
+
let elIndex = this.GetElementIndex('point');
|
|
5409
|
+
for (let i = 0; i < singleMaidr.data[elIndex].length; i++) {
|
|
5410
|
+
let x;
|
|
5411
|
+
let y;
|
|
5412
|
+
if ('x' in singleMaidr.data[elIndex][i]) {
|
|
5413
|
+
x = singleMaidr.data[elIndex][i]['x'];
|
|
5414
|
+
}
|
|
5415
|
+
if ('y' in singleMaidr.data[elIndex][i]) {
|
|
5416
|
+
y = singleMaidr.data[elIndex][i]['y'];
|
|
5417
|
+
}
|
|
5418
|
+
if (!points.has(x)) {
|
|
5419
|
+
points.set(x, new Set([y]));
|
|
5420
|
+
} else {
|
|
5421
|
+
points.get(x).add(y);
|
|
5422
|
+
}
|
|
5463
5423
|
}
|
|
5464
5424
|
}
|
|
5465
5425
|
|
|
@@ -5525,38 +5485,40 @@ class ScatterPlot {
|
|
|
5525
5485
|
|
|
5526
5486
|
// but first, are we even in an svg that can be scaled?
|
|
5527
5487
|
let isSvg = false;
|
|
5528
|
-
|
|
5529
|
-
while (element) {
|
|
5530
|
-
if (element.tagName.toLowerCase() == 'body') {
|
|
5531
|
-
break;
|
|
5532
|
-
}
|
|
5533
|
-
if (element.tagName && element.tagName.toLowerCase() === 'svg') {
|
|
5534
|
-
isSvg = true;
|
|
5535
|
-
}
|
|
5536
|
-
element = element.parentNode;
|
|
5537
|
-
}
|
|
5538
|
-
|
|
5539
|
-
if (isSvg) {
|
|
5488
|
+
if (this.plotPoints) {
|
|
5540
5489
|
let element = this.plotPoints[0]; // a random start, may as well be the first
|
|
5541
5490
|
while (element) {
|
|
5542
5491
|
if (element.tagName.toLowerCase() == 'body') {
|
|
5543
5492
|
break;
|
|
5544
5493
|
}
|
|
5545
|
-
if (element.
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5494
|
+
if (element.tagName && element.tagName.toLowerCase() === 'svg') {
|
|
5495
|
+
isSvg = true;
|
|
5496
|
+
}
|
|
5497
|
+
element = element.parentNode;
|
|
5498
|
+
}
|
|
5499
|
+
|
|
5500
|
+
if (isSvg) {
|
|
5501
|
+
let element = this.plotPoints[0]; // a random start, may as well be the first
|
|
5502
|
+
while (element) {
|
|
5503
|
+
if (element.tagName.toLowerCase() == 'body') {
|
|
5504
|
+
break;
|
|
5505
|
+
}
|
|
5506
|
+
if (element.getAttribute('transform')) {
|
|
5507
|
+
let transform = element.getAttribute('transform');
|
|
5508
|
+
let match = transform.match(
|
|
5509
|
+
/scale\((-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)\)/
|
|
5510
|
+
);
|
|
5511
|
+
if (match) {
|
|
5512
|
+
if (!isNaN(match[1])) {
|
|
5513
|
+
scaleX *= parseFloat(match[1]);
|
|
5514
|
+
}
|
|
5515
|
+
if (!isNaN(match[3])) {
|
|
5516
|
+
scaleY *= parseFloat(match[3]);
|
|
5517
|
+
}
|
|
5556
5518
|
}
|
|
5557
5519
|
}
|
|
5520
|
+
element = element.parentNode;
|
|
5558
5521
|
}
|
|
5559
|
-
element = element.parentNode;
|
|
5560
5522
|
}
|
|
5561
5523
|
}
|
|
5562
5524
|
|
|
@@ -5565,22 +5527,30 @@ class ScatterPlot {
|
|
|
5565
5527
|
|
|
5566
5528
|
/**
|
|
5567
5529
|
* Returns a prefix based on the element type.
|
|
5530
|
+
* This helps manipulate svg stuff, as the attribute info is slightly different depending on svg source
|
|
5568
5531
|
* @returns {string} The prefix.
|
|
5569
5532
|
*/
|
|
5570
5533
|
GetPrefix() {
|
|
5571
5534
|
let pointIndex = this.GetElementIndex('point');
|
|
5572
5535
|
|
|
5573
|
-
let element;
|
|
5536
|
+
let element = null;
|
|
5574
5537
|
if (pointIndex != -1) {
|
|
5575
|
-
|
|
5538
|
+
if ('selector' in singleMaidr) {
|
|
5539
|
+
element = document.querySelectorAll(
|
|
5540
|
+
singleMaidr.selector[pointIndex]
|
|
5541
|
+
)[0];
|
|
5542
|
+
} else if ('elements' in singleMaidr) {
|
|
5543
|
+
element = singleMaidr.elements[pointIndex][0];
|
|
5544
|
+
}
|
|
5576
5545
|
} else if (singleMaidr.type == 'point') {
|
|
5577
|
-
|
|
5546
|
+
if ('selector' in singleMaidr) {
|
|
5547
|
+
element = document.querySelectorAll(singleMaidr.selector)[0];
|
|
5548
|
+
} else if ('elements' in singleMaidr) {
|
|
5549
|
+
element = singleMaidr.elements[0];
|
|
5550
|
+
}
|
|
5578
5551
|
}
|
|
5579
5552
|
let prefix = '';
|
|
5580
|
-
if (
|
|
5581
|
-
'selector' in singleMaidr &&
|
|
5582
|
-
element.tagName.toLowerCase() === 'circle'
|
|
5583
|
-
) {
|
|
5553
|
+
if (element && element.tagName.toLowerCase() === 'circle') {
|
|
5584
5554
|
prefix = 'c';
|
|
5585
5555
|
}
|
|
5586
5556
|
return prefix;
|
|
@@ -5733,20 +5703,32 @@ class ScatterPlot {
|
|
|
5733
5703
|
* @returns {Array<Array<number>>} An array containing two arrays: the x-coordinates and y-coordinates.
|
|
5734
5704
|
*/
|
|
5735
5705
|
GetSvgLineCoords() {
|
|
5736
|
-
|
|
5737
|
-
let
|
|
5738
|
-
let coords = str.split(' ');
|
|
5739
|
-
|
|
5740
|
-
let X = [];
|
|
5741
|
-
let Y = [];
|
|
5706
|
+
let x_points = [];
|
|
5707
|
+
let y_points = [];
|
|
5742
5708
|
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5709
|
+
if (this.plotLine) {
|
|
5710
|
+
// extract all the y coordinates from the point attribute of polyline
|
|
5711
|
+
let str = this.plotLine.getAttribute('points');
|
|
5712
|
+
let coords = str.split(' ');
|
|
5713
|
+
for (let i = 0; i < coords.length; i++) {
|
|
5714
|
+
let coord = coords[i].split(',');
|
|
5715
|
+
x_points.push(parseFloat(coord[0]));
|
|
5716
|
+
y_points.push(parseFloat(coord[1]));
|
|
5717
|
+
}
|
|
5718
|
+
} else {
|
|
5719
|
+
// fetch from data instead
|
|
5720
|
+
let elIndex = this.GetElementIndex('point');
|
|
5721
|
+
for (let i = 0; i < singleMaidr.data[elIndex].length; i++) {
|
|
5722
|
+
if ('x' in singleMaidr.data[elIndex][i]) {
|
|
5723
|
+
x_points.push(singleMaidr.data[elIndex][i]['x']);
|
|
5724
|
+
}
|
|
5725
|
+
if ('y' in singleMaidr.data[elIndex][i]) {
|
|
5726
|
+
y_points.push(singleMaidr.data[elIndex][i]['y']);
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5747
5729
|
}
|
|
5748
5730
|
|
|
5749
|
-
return [
|
|
5731
|
+
return [x, y];
|
|
5750
5732
|
}
|
|
5751
5733
|
|
|
5752
5734
|
/**
|
|
@@ -5808,6 +5790,24 @@ class ScatterPlot {
|
|
|
5808
5790
|
|
|
5809
5791
|
return gradients;
|
|
5810
5792
|
}
|
|
5793
|
+
|
|
5794
|
+
/**
|
|
5795
|
+
* Returns whether or not we have elements / selectors for the given type.
|
|
5796
|
+
* @param {string} type - The type of element to check for. eg, 'point' or 'smooth'.
|
|
5797
|
+
* @returns {boolean} - True if we have elements / selectors for the given type, false otherwise.
|
|
5798
|
+
* @function
|
|
5799
|
+
* @memberof scatterplot
|
|
5800
|
+
*/
|
|
5801
|
+
GetRectStatus(type) {
|
|
5802
|
+
let elIndex = this.GetElementIndex(type);
|
|
5803
|
+
if ('selector' in singleMaidr) {
|
|
5804
|
+
return singleMaidr.selector[elIndex] ? true : false;
|
|
5805
|
+
} else if ('elements' in singleMaidr) {
|
|
5806
|
+
return singleMaidr.elements[elIndex] ? true : false;
|
|
5807
|
+
} else {
|
|
5808
|
+
return false;
|
|
5809
|
+
}
|
|
5810
|
+
}
|
|
5811
5811
|
}
|
|
5812
5812
|
|
|
5813
5813
|
/**
|
|
@@ -5826,6 +5826,7 @@ class Layer0Point {
|
|
|
5826
5826
|
this.y = plot.chartPointsY[0];
|
|
5827
5827
|
this.strokeWidth = 1.35;
|
|
5828
5828
|
this.circleIndex = [];
|
|
5829
|
+
this.hasRect = plot.GetRectStatus('point');
|
|
5829
5830
|
}
|
|
5830
5831
|
|
|
5831
5832
|
/**
|
|
@@ -5925,6 +5926,7 @@ class Layer1Point {
|
|
|
5925
5926
|
this.x = plot.chartLineX[0];
|
|
5926
5927
|
this.y = plot.chartLineY[0];
|
|
5927
5928
|
this.strokeWidth = 1.35;
|
|
5929
|
+
this.hasRect = plot.GetRectStatus('point');
|
|
5928
5930
|
}
|
|
5929
5931
|
|
|
5930
5932
|
/**
|
|
@@ -6015,6 +6017,8 @@ class Histogram {
|
|
|
6015
6017
|
this.bars = null;
|
|
6016
6018
|
if ('selector' in singleMaidr) {
|
|
6017
6019
|
this.bars = document.querySelectorAll(singleMaidr.selector);
|
|
6020
|
+
} else if ('elements' in singleMaidr) {
|
|
6021
|
+
this.bars = singleMaidr.elements;
|
|
6018
6022
|
}
|
|
6019
6023
|
|
|
6020
6024
|
// labels (optional)
|
|
@@ -6164,57 +6168,7 @@ class LinePlot {
|
|
|
6164
6168
|
constructor() {
|
|
6165
6169
|
this.SetLineLayer();
|
|
6166
6170
|
this.SetAxes();
|
|
6167
|
-
|
|
6168
|
-
let legendX = '';
|
|
6169
|
-
let legendY = '';
|
|
6170
|
-
if ('axes' in singleMaidr) {
|
|
6171
|
-
// legend labels
|
|
6172
|
-
if (singleMaidr.axes.x) {
|
|
6173
|
-
if (singleMaidr.axes.x.label) {
|
|
6174
|
-
if (legendX == '') {
|
|
6175
|
-
legendX = singleMaidr.axes.x.label;
|
|
6176
|
-
}
|
|
6177
|
-
}
|
|
6178
|
-
}
|
|
6179
|
-
if (singleMaidr.axes.y) {
|
|
6180
|
-
if (singleMaidr.axes.y.label) {
|
|
6181
|
-
if (legendY == '') {
|
|
6182
|
-
legendY = singleMaidr.axes.y.label;
|
|
6183
|
-
}
|
|
6184
|
-
}
|
|
6185
|
-
}
|
|
6186
|
-
}
|
|
6187
|
-
|
|
6188
|
-
this.plotLegend = {
|
|
6189
|
-
x: legendX,
|
|
6190
|
-
y: legendY,
|
|
6191
|
-
};
|
|
6192
|
-
|
|
6193
|
-
// title
|
|
6194
|
-
this.title = '';
|
|
6195
|
-
if ('labels' in singleMaidr) {
|
|
6196
|
-
if ('title' in singleMaidr.labels) {
|
|
6197
|
-
this.title = singleMaidr.labels.title;
|
|
6198
|
-
}
|
|
6199
|
-
}
|
|
6200
|
-
if (this.title == '') {
|
|
6201
|
-
if ('title' in singleMaidr) {
|
|
6202
|
-
this.title = singleMaidr.title;
|
|
6203
|
-
}
|
|
6204
|
-
}
|
|
6205
|
-
|
|
6206
|
-
// subtitle
|
|
6207
|
-
if ('labels' in singleMaidr) {
|
|
6208
|
-
if ('subtitle' in singleMaidr.labels) {
|
|
6209
|
-
this.subtitle = singleMaidr.labels.subtitle;
|
|
6210
|
-
}
|
|
6211
|
-
}
|
|
6212
|
-
// caption
|
|
6213
|
-
if ('labels' in singleMaidr) {
|
|
6214
|
-
if ('caption' in singleMaidr.labels) {
|
|
6215
|
-
this.caption = singleMaidr.labels.caption;
|
|
6216
|
-
}
|
|
6217
|
-
}
|
|
6171
|
+
this.UpdateConstants();
|
|
6218
6172
|
}
|
|
6219
6173
|
|
|
6220
6174
|
/**
|
|
@@ -6224,12 +6178,13 @@ class LinePlot {
|
|
|
6224
6178
|
let elements;
|
|
6225
6179
|
if ('selector' in singleMaidr) {
|
|
6226
6180
|
elements = document.querySelectorAll(singleMaidr.selector);
|
|
6181
|
+
} else if ('elements' in singleMaidr) {
|
|
6182
|
+
elements = singleMaidr.elements;
|
|
6227
6183
|
}
|
|
6228
6184
|
|
|
6229
|
-
|
|
6230
|
-
|
|
6185
|
+
if (elements) {
|
|
6186
|
+
this.plotLine = elements[elements.length - 1];
|
|
6231
6187
|
|
|
6232
|
-
if (typeof this.plotLine !== 'undefined') {
|
|
6233
6188
|
let pointCoords = this.GetPointCoords();
|
|
6234
6189
|
let pointValues = this.GetPoints();
|
|
6235
6190
|
|
|
@@ -6241,35 +6196,33 @@ class LinePlot {
|
|
|
6241
6196
|
|
|
6242
6197
|
this.curveMinY = Math.min(...this.pointValuesY);
|
|
6243
6198
|
this.curveMaxY = Math.max(...this.pointValuesY);
|
|
6244
|
-
constants.minX = 0;
|
|
6245
|
-
constants.maxX = this.pointValuesX.length - 1;
|
|
6246
|
-
constants.minY = this.curveMinY;
|
|
6247
|
-
constants.maxY = this.curveMaxY;
|
|
6248
|
-
|
|
6249
|
-
constants.autoPlayRate = Math.min(
|
|
6250
|
-
Math.ceil(constants.AUTOPLAY_DURATION / (constants.maxX + 1)),
|
|
6251
|
-
constants.MAX_SPEED
|
|
6252
|
-
);
|
|
6253
|
-
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
6254
|
-
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
6255
|
-
constants.MIN_SPEED = constants.autoPlayRate;
|
|
6256
|
-
}
|
|
6257
|
-
|
|
6258
|
-
// this.gradient = this.GetGradient();
|
|
6259
6199
|
}
|
|
6260
6200
|
}
|
|
6261
6201
|
|
|
6262
6202
|
/**
|
|
6263
|
-
*
|
|
6203
|
+
* Updates the constants for the line plot.
|
|
6204
|
+
* This includes the minimum and maximum x and y values, the autoplay rate, and the default speed.
|
|
6264
6205
|
*/
|
|
6265
|
-
|
|
6206
|
+
UpdateConstants() {
|
|
6266
6207
|
constants.minX = 0;
|
|
6267
|
-
constants.maxX =
|
|
6268
|
-
constants.minY =
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6208
|
+
constants.maxX = singleMaidr.data.length - 1;
|
|
6209
|
+
constants.minY = singleMaidr.data.reduce(
|
|
6210
|
+
(min, item) => (item.y < min ? item.y : min),
|
|
6211
|
+
singleMaidr.data[0].y
|
|
6212
|
+
);
|
|
6213
|
+
constants.maxY = singleMaidr.data.reduce(
|
|
6214
|
+
(max, item) => (item.y > max ? item.y : max),
|
|
6215
|
+
singleMaidr.data[0].y
|
|
6272
6216
|
);
|
|
6217
|
+
|
|
6218
|
+
constants.autoPlayRate = Math.min(
|
|
6219
|
+
Math.ceil(constants.AUTOPLAY_DURATION / (constants.maxX + 1)),
|
|
6220
|
+
constants.MAX_SPEED
|
|
6221
|
+
);
|
|
6222
|
+
constants.DEFAULT_SPEED = constants.autoPlayRate;
|
|
6223
|
+
if (constants.autoPlayRate < constants.MIN_SPEED) {
|
|
6224
|
+
constants.MIN_SPEED = constants.autoPlayRate;
|
|
6225
|
+
}
|
|
6273
6226
|
}
|
|
6274
6227
|
|
|
6275
6228
|
/**
|
|
@@ -6325,46 +6278,60 @@ class LinePlot {
|
|
|
6325
6278
|
}
|
|
6326
6279
|
}
|
|
6327
6280
|
|
|
6328
|
-
// GetGradient() {
|
|
6329
|
-
// let gradients = [];
|
|
6330
|
-
|
|
6331
|
-
// for (let i = 0; i < this.pointValuesY.length - 1; i++) {
|
|
6332
|
-
// let abs_grad = Math.abs(
|
|
6333
|
-
// (this.pointValuesY[i + 1] - this.pointValuesY[i]) /
|
|
6334
|
-
// (this.pointValuesX[i + 1] - this.pointValuesX[i])
|
|
6335
|
-
// ).toFixed(3);
|
|
6336
|
-
// gradients.push(abs_grad);
|
|
6337
|
-
// }
|
|
6338
|
-
|
|
6339
|
-
// gradients.push('end');
|
|
6340
|
-
|
|
6341
|
-
// return gradients;
|
|
6342
|
-
// }
|
|
6343
|
-
|
|
6344
6281
|
/**
|
|
6345
6282
|
* Sets the x and y group labels and title for the line plot based on the axes and title properties of the singleMaidr object.
|
|
6346
6283
|
*/
|
|
6347
6284
|
SetAxes() {
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
this.title = '';
|
|
6285
|
+
let legendX = '';
|
|
6286
|
+
let legendY = '';
|
|
6351
6287
|
if ('axes' in singleMaidr) {
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6288
|
+
// legend labels
|
|
6289
|
+
if (singleMaidr.axes.x) {
|
|
6290
|
+
if (singleMaidr.axes.x.label) {
|
|
6291
|
+
if (legendX == '') {
|
|
6292
|
+
legendX = singleMaidr.axes.x.label;
|
|
6293
|
+
}
|
|
6355
6294
|
}
|
|
6356
6295
|
}
|
|
6357
|
-
if (
|
|
6358
|
-
if (
|
|
6359
|
-
|
|
6296
|
+
if (singleMaidr.axes.y) {
|
|
6297
|
+
if (singleMaidr.axes.y.label) {
|
|
6298
|
+
if (legendY == '') {
|
|
6299
|
+
legendY = singleMaidr.axes.y.label;
|
|
6300
|
+
}
|
|
6360
6301
|
}
|
|
6361
6302
|
}
|
|
6362
6303
|
}
|
|
6363
|
-
|
|
6364
|
-
|
|
6304
|
+
|
|
6305
|
+
this.plotLegend = {
|
|
6306
|
+
x: legendX,
|
|
6307
|
+
y: legendY,
|
|
6308
|
+
};
|
|
6309
|
+
|
|
6310
|
+
// title
|
|
6311
|
+
this.title = '';
|
|
6312
|
+
if ('labels' in singleMaidr) {
|
|
6313
|
+
if ('title' in singleMaidr.labels) {
|
|
6314
|
+
this.title = singleMaidr.labels.title;
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
6317
|
+
if (this.title == '') {
|
|
6318
|
+
if ('title' in singleMaidr) {
|
|
6365
6319
|
this.title = singleMaidr.title;
|
|
6366
6320
|
}
|
|
6367
6321
|
}
|
|
6322
|
+
|
|
6323
|
+
// subtitle
|
|
6324
|
+
if ('labels' in singleMaidr) {
|
|
6325
|
+
if ('subtitle' in singleMaidr.labels) {
|
|
6326
|
+
this.subtitle = singleMaidr.labels.subtitle;
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
// caption
|
|
6330
|
+
if ('labels' in singleMaidr) {
|
|
6331
|
+
if ('caption' in singleMaidr.labels) {
|
|
6332
|
+
this.caption = singleMaidr.labels.caption;
|
|
6333
|
+
}
|
|
6334
|
+
}
|
|
6368
6335
|
}
|
|
6369
6336
|
|
|
6370
6337
|
/**
|
|
@@ -6455,7 +6422,6 @@ class Segmented {
|
|
|
6455
6422
|
*/
|
|
6456
6423
|
constructor() {
|
|
6457
6424
|
// initialize variables level, data, and elements
|
|
6458
|
-
let level = null;
|
|
6459
6425
|
let fill = null;
|
|
6460
6426
|
let data = null;
|
|
6461
6427
|
let elements = null;
|
|
@@ -6463,17 +6429,17 @@ class Segmented {
|
|
|
6463
6429
|
//axes.x.level
|
|
6464
6430
|
if ('x' in singleMaidr.axes) {
|
|
6465
6431
|
if ('level' in singleMaidr.axes.x) {
|
|
6466
|
-
level = singleMaidr.axes.x.level;
|
|
6432
|
+
this.level = singleMaidr.axes.x.level;
|
|
6467
6433
|
}
|
|
6468
6434
|
} else if ('y' in singleMaidr.axes) {
|
|
6469
6435
|
if ('level' in singleMaidr.axes.y) {
|
|
6470
|
-
level = singleMaidr.axes.y.level;
|
|
6436
|
+
this.level = singleMaidr.axes.y.level;
|
|
6471
6437
|
}
|
|
6472
6438
|
}
|
|
6473
6439
|
// axes.fill
|
|
6474
6440
|
if ('fill' in singleMaidr.axes) {
|
|
6475
6441
|
if ('level' in singleMaidr.axes.fill) {
|
|
6476
|
-
fill = singleMaidr.axes.fill.level;
|
|
6442
|
+
this.fill = singleMaidr.axes.fill.level;
|
|
6477
6443
|
}
|
|
6478
6444
|
}
|
|
6479
6445
|
}
|
|
@@ -6482,6 +6448,8 @@ class Segmented {
|
|
|
6482
6448
|
}
|
|
6483
6449
|
if ('selector' in singleMaidr) {
|
|
6484
6450
|
elements = document.querySelectorAll(singleMaidr.selector);
|
|
6451
|
+
} else if ('elements' in singleMaidr) {
|
|
6452
|
+
elements = singleMaidr.elements;
|
|
6485
6453
|
}
|
|
6486
6454
|
|
|
6487
6455
|
// gracefull failure: must have level + fill + data, elements optional
|
|
@@ -6489,9 +6457,10 @@ class Segmented {
|
|
|
6489
6457
|
logError.LogAbsentElement('elements');
|
|
6490
6458
|
constants.hasRect = 0;
|
|
6491
6459
|
}
|
|
6492
|
-
if (
|
|
6493
|
-
this.
|
|
6494
|
-
|
|
6460
|
+
if (data) {
|
|
6461
|
+
if (this.fill) {
|
|
6462
|
+
this.fill = this.fill.reverse(); // typically fill is in reverse order
|
|
6463
|
+
}
|
|
6495
6464
|
let dataAndELements = this.ParseData(data, elements);
|
|
6496
6465
|
this.plotData = dataAndELements[0];
|
|
6497
6466
|
this.elements = dataAndELements[1];
|
|
@@ -6579,7 +6548,12 @@ class Segmented {
|
|
|
6579
6548
|
let plotData = [];
|
|
6580
6549
|
let plotElements = [];
|
|
6581
6550
|
|
|
6582
|
-
if
|
|
6551
|
+
// override and kill elements if not same length as data
|
|
6552
|
+
if (elements) {
|
|
6553
|
+
if (elements.length != data.length) {
|
|
6554
|
+
plotElements = null;
|
|
6555
|
+
}
|
|
6556
|
+
} else {
|
|
6583
6557
|
plotElements = null;
|
|
6584
6558
|
}
|
|
6585
6559
|
|
|
@@ -6609,7 +6583,9 @@ class Segmented {
|
|
|
6609
6583
|
// set actual values
|
|
6610
6584
|
if (data[k].x == this.level[i] && data[k].fill == this.fill[j]) {
|
|
6611
6585
|
plotData[i][j] = data[k].y;
|
|
6612
|
-
|
|
6586
|
+
if (elements) {
|
|
6587
|
+
plotElements[i][j] = elements[k];
|
|
6588
|
+
}
|
|
6613
6589
|
break;
|
|
6614
6590
|
}
|
|
6615
6591
|
}
|
|
@@ -6660,6 +6636,7 @@ class Segmented {
|
|
|
6660
6636
|
if (constants.sonifMode == 'on') {
|
|
6661
6637
|
// we play a run of tones
|
|
6662
6638
|
position.z = 0;
|
|
6639
|
+
constants.KillSepPlay();
|
|
6663
6640
|
constants.sepPlayId = setInterval(
|
|
6664
6641
|
function () {
|
|
6665
6642
|
// play this tone
|
|
@@ -7304,10 +7281,8 @@ class Control {
|
|
|
7304
7281
|
window.position = new Position(-1, plot.plotData.length);
|
|
7305
7282
|
}
|
|
7306
7283
|
let rect;
|
|
7307
|
-
constants.hasRect
|
|
7308
|
-
if ('selector' in singleMaidr) {
|
|
7284
|
+
if (constants.hasRect) {
|
|
7309
7285
|
rect = new BoxplotRect();
|
|
7310
|
-
constants.hasRect = true;
|
|
7311
7286
|
}
|
|
7312
7287
|
let lastPlayed = '';
|
|
7313
7288
|
|
|
@@ -8256,7 +8231,7 @@ class Control {
|
|
|
8256
8231
|
return new Promise((resolve) => setTimeout(resolve, time));
|
|
8257
8232
|
}
|
|
8258
8233
|
|
|
8259
|
-
// helper functions
|
|
8234
|
+
// heat helper functions
|
|
8260
8235
|
function lockPosition() {
|
|
8261
8236
|
// lock to min / max postions
|
|
8262
8237
|
let didLockHappen = false;
|
|
@@ -8659,7 +8634,7 @@ class Control {
|
|
|
8659
8634
|
if (constants.showDisplay) {
|
|
8660
8635
|
display.displayValues();
|
|
8661
8636
|
}
|
|
8662
|
-
if (
|
|
8637
|
+
if (layer0Point.hasRect) {
|
|
8663
8638
|
layer0Point.UpdatePointDisplay();
|
|
8664
8639
|
}
|
|
8665
8640
|
if (constants.sonifMode != 'off') {
|
|
@@ -8672,9 +8647,9 @@ class Control {
|
|
|
8672
8647
|
display.displayValues();
|
|
8673
8648
|
}
|
|
8674
8649
|
if (constants.showRect) {
|
|
8675
|
-
if (constants.chartType == 'point') {
|
|
8650
|
+
if (constants.chartType == 'point' && layer0Point.hasRect) {
|
|
8676
8651
|
layer0Point.UpdatePointDisplay();
|
|
8677
|
-
} else {
|
|
8652
|
+
} else if (constants.chartType == 'smooth' && layer1Point.hasRect) {
|
|
8678
8653
|
layer1Point.UpdatePointDisplay();
|
|
8679
8654
|
}
|
|
8680
8655
|
}
|
|
@@ -8689,7 +8664,7 @@ class Control {
|
|
|
8689
8664
|
if (constants.showDisplayInBraille) {
|
|
8690
8665
|
display.displayValues();
|
|
8691
8666
|
}
|
|
8692
|
-
if (
|
|
8667
|
+
if (layer1Point.hasRect) {
|
|
8693
8668
|
layer1Point.UpdatePointDisplay();
|
|
8694
8669
|
}
|
|
8695
8670
|
if (constants.sonifMode != 'off') {
|