Skip to content

Commit bc57d8f

Browse files
committed
better decoupling between dropdown boxes
1 parent 6445eb4 commit bc57d8f

2 files changed

Lines changed: 134 additions & 42 deletions

File tree

scripts/main.js

Lines changed: 126 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -678,8 +678,27 @@ document.addEventListener("DOMContentLoaded", () => {
678678

679679
const viewers = viewerConfigs.map((config) => initViewer(config, datasets));
680680

681+
// Track last processed URLs for each viewer to prevent duplicate updates
682+
const lastProcessedUrls = new Map();
683+
684+
// Debug flag - set to true to enable detailed logging
685+
const DEBUG_DROPDOWN_SYNC = false;
686+
681687
// Helper function to update dropdowns based on iframe URL changes
682688
function updateDropdownsFromIframeUrl(viewerIndex, newUrl) {
689+
// Validate viewer index
690+
if (viewerIndex < 0 || viewerIndex >= viewers.length) {
691+
console.error("Invalid viewer index:", viewerIndex);
692+
return;
693+
}
694+
695+
// Prevent duplicate processing of the same URL for the same viewer
696+
const lastUrl = lastProcessedUrls.get(viewerIndex);
697+
if (lastUrl === newUrl) {
698+
return;
699+
}
700+
lastProcessedUrls.set(viewerIndex, newUrl);
701+
683702
const viewer = viewers[viewerIndex];
684703
if (
685704
!viewer ||
@@ -690,6 +709,39 @@ document.addEventListener("DOMContentLoaded", () => {
690709
return;
691710
}
692711

712+
// Log which viewer is being updated (for debugging)
713+
if (DEBUG_DROPDOWN_SYNC) {
714+
console.log(
715+
`Updating dropdowns for ${viewer.config.prefix} with URL:`,
716+
newUrl,
717+
);
718+
}
719+
720+
// Ensure we're only working with this specific viewer's DOM elements
721+
const viewerPrefix = viewer.config.prefix;
722+
const viewerDatasetSelect = document.getElementById(
723+
`${viewerPrefix}-dataset-select`,
724+
);
725+
const viewerNeuronSelect = document.getElementById(
726+
`${viewerPrefix}-neuron-select`,
727+
);
728+
const viewerHemisphereSelect = document.getElementById(
729+
`${viewerPrefix}-hemisphere-select`,
730+
);
731+
732+
// Verify these match the viewer's stored references
733+
if (
734+
viewer.datasetSelect !== viewerDatasetSelect ||
735+
viewer.neuronSelect !== viewerNeuronSelect ||
736+
viewer.hemisphereSelect !== viewerHemisphereSelect
737+
) {
738+
console.error(
739+
"Viewer element mismatch detected for viewer index",
740+
viewerIndex,
741+
);
742+
return;
743+
}
744+
693745
// Match the URL against known URLs in neurons.json
694746
// Don't parse - instead search through all neuron records
695747
let matchedRecord = null;
@@ -727,33 +779,31 @@ document.addEventListener("DOMContentLoaded", () => {
727779

728780
if (!matchedRecord) {
729781
// Clear cell type and hemisphere dropdowns when navigating to non-cell-type pages
730-
const neuronSelect = viewer.neuronSelect;
731-
const hemisphereSelect = viewer.hemisphereSelect;
732-
733-
if (neuronSelect && neuronSelect.value) {
782+
// Use the verified viewer-specific elements
783+
if (viewerNeuronSelect && viewerNeuronSelect.value) {
734784
// Temporarily disable change events
735-
const originalNeuronOnChange = neuronSelect.onchange;
736-
const originalHemisphereOnChange = hemisphereSelect
737-
? hemisphereSelect.onchange
785+
const originalNeuronOnChange = viewerNeuronSelect.onchange;
786+
const originalHemisphereOnChange = viewerHemisphereSelect
787+
? viewerHemisphereSelect.onchange
738788
: null;
739789

740-
neuronSelect.onchange = null;
741-
if (hemisphereSelect) hemisphereSelect.onchange = null;
790+
viewerNeuronSelect.onchange = null;
791+
if (viewerHemisphereSelect) viewerHemisphereSelect.onchange = null;
742792

743793
// Clear selections
744-
neuronSelect.value = "";
745-
if (hemisphereSelect) {
746-
hemisphereSelect.value = "";
747-
hemisphereSelect.disabled = true;
794+
viewerNeuronSelect.value = "";
795+
if (viewerHemisphereSelect) {
796+
viewerHemisphereSelect.value = "";
797+
viewerHemisphereSelect.disabled = true;
748798
}
749799

750800
// Clear current record
751801
viewer.state.currentNeuronRecord = null;
752802

753803
// Restore change handlers
754-
neuronSelect.onchange = originalNeuronOnChange;
755-
if (hemisphereSelect)
756-
hemisphereSelect.onchange = originalHemisphereOnChange;
804+
viewerNeuronSelect.onchange = originalNeuronOnChange;
805+
if (viewerHemisphereSelect)
806+
viewerHemisphereSelect.onchange = originalHemisphereOnChange;
757807

758808
// Update URL to remove cell type and hemisphere parameters
759809
updateURL();
@@ -767,68 +817,72 @@ document.addEventListener("DOMContentLoaded", () => {
767817
const normalizedCellType = normalizeKey(record.name);
768818

769819
// Update the neuron select dropdown if it's different
770-
const neuronSelect = viewer.neuronSelect;
771-
const hemisphereSelect = viewer.hemisphereSelect;
772-
773-
if (neuronSelect && neuronSelect.value !== normalizedCellType) {
820+
// Use the verified viewer-specific elements
821+
if (
822+
viewerNeuronSelect &&
823+
viewerNeuronSelect.value !== normalizedCellType
824+
) {
774825
// Temporarily disable change event to prevent loops
775-
const originalNeuronOnChange = neuronSelect.onchange;
776-
neuronSelect.onchange = null;
826+
const originalNeuronOnChange = viewerNeuronSelect.onchange;
827+
viewerNeuronSelect.onchange = null;
777828

778-
neuronSelect.value = normalizedCellType;
829+
viewerNeuronSelect.value = normalizedCellType;
779830
viewer.state.currentNeuronRecord = record;
780831

781832
// Restore the change handler
782-
neuronSelect.onchange = originalNeuronOnChange;
833+
viewerNeuronSelect.onchange = originalNeuronOnChange;
783834

784835
// Update hemisphere options based on the new neuron
785-
if (hemisphereSelect) {
836+
if (viewerHemisphereSelect) {
786837
// Get available hemispheres for this cell type
787838
const availableHemispheres = getAvailableHemispheres(record);
788839

789840
// Temporarily disable change event
790-
const originalHemisphereOnChange = hemisphereSelect.onchange;
791-
hemisphereSelect.onchange = null;
841+
const originalHemisphereOnChange = viewerHemisphereSelect.onchange;
842+
viewerHemisphereSelect.onchange = null;
792843

793844
// Clear and repopulate hemisphere select
794-
hemisphereSelect.innerHTML = "";
845+
viewerHemisphereSelect.innerHTML = "";
795846
const defaultOption = document.createElement("option");
796847
defaultOption.value = "";
797848
defaultOption.textContent = "Choose hemisphere";
798-
hemisphereSelect.appendChild(defaultOption);
849+
viewerHemisphereSelect.appendChild(defaultOption);
799850

800851
availableHemispheres.forEach((hem) => {
801852
const option = document.createElement("option");
802853
option.value = hem;
803854
option.textContent = hem.charAt(0).toUpperCase() + hem.slice(1);
804-
hemisphereSelect.appendChild(option);
855+
viewerHemisphereSelect.appendChild(option);
805856
});
806857

807-
hemisphereSelect.disabled = availableHemispheres.length === 0;
858+
viewerHemisphereSelect.disabled = availableHemispheres.length === 0;
808859

809860
// Set the hemisphere value after options are populated
810861
if (availableHemispheres.includes(hemisphere)) {
811-
hemisphereSelect.value = hemisphere;
862+
viewerHemisphereSelect.value = hemisphere;
812863
} else if (availableHemispheres.includes("combined")) {
813-
hemisphereSelect.value = "combined";
864+
viewerHemisphereSelect.value = "combined";
814865
}
815866

816867
// Restore the change handler
817-
hemisphereSelect.onchange = originalHemisphereOnChange;
868+
viewerHemisphereSelect.onchange = originalHemisphereOnChange;
818869
}
819-
} else if (hemisphereSelect && hemisphereSelect.value !== hemisphere) {
870+
} else if (
871+
viewerHemisphereSelect &&
872+
viewerHemisphereSelect.value !== hemisphere
873+
) {
820874
// Only update hemisphere if neuron didn't change
821875
const availableHemispheres = getAvailableHemispheres(record);
822876

823877
if (availableHemispheres.includes(hemisphere)) {
824878
// Temporarily disable change event to prevent loops
825-
const originalOnChange = hemisphereSelect.onchange;
826-
hemisphereSelect.onchange = null;
879+
const originalOnChange = viewerHemisphereSelect.onchange;
880+
viewerHemisphereSelect.onchange = null;
827881

828-
hemisphereSelect.value = hemisphere;
882+
viewerHemisphereSelect.value = hemisphere;
829883

830884
// Restore the change handler
831-
hemisphereSelect.onchange = originalOnChange;
885+
viewerHemisphereSelect.onchange = originalOnChange;
832886
}
833887
}
834888

@@ -841,6 +895,10 @@ document.addEventListener("DOMContentLoaded", () => {
841895
const frame = document.getElementById(`${config.prefix}-frame`);
842896
if (!frame) return null;
843897

898+
// Capture index in closure to ensure correct viewer is updated
899+
const viewerIndex = index;
900+
const viewerPrefix = config.prefix;
901+
844902
return new NeuviewIframeController(frame, {
845903
onUrlChanged: function (data) {
846904
// Update dropdowns when iframe URL changes
@@ -849,7 +907,13 @@ document.addEventListener("DOMContentLoaded", () => {
849907
data.currentUrl !== "about:blank" &&
850908
!data.currentUrl.startsWith("about:")
851909
) {
852-
updateDropdownsFromIframeUrl(index, data.currentUrl);
910+
if (DEBUG_DROPDOWN_SYNC) {
911+
console.log(
912+
`${viewerPrefix} iframe URL changed to:`,
913+
data.currentUrl,
914+
);
915+
}
916+
updateDropdownsFromIframeUrl(viewerIndex, data.currentUrl);
853917
}
854918
},
855919
onNavigationRequest: function (data) {
@@ -859,7 +923,10 @@ document.addEventListener("DOMContentLoaded", () => {
859923
data.url !== "about:blank" &&
860924
!data.url.startsWith("about:")
861925
) {
862-
updateDropdownsFromIframeUrl(index, data.url);
926+
if (DEBUG_DROPDOWN_SYNC) {
927+
console.log(`${viewerPrefix} navigation request to:`, data.url);
928+
}
929+
updateDropdownsFromIframeUrl(viewerIndex, data.url);
863930
}
864931
},
865932
debug: false,
@@ -872,7 +939,11 @@ document.addEventListener("DOMContentLoaded", () => {
872939
const frame = document.getElementById(`${config.prefix}-frame`);
873940
if (!frame) return;
874941

942+
// Capture index and prefix in closure to ensure correct viewer is updated
943+
const viewerIndex = index;
944+
const viewerPrefix = config.prefix;
875945
let lastSrc = frame.src;
946+
let debounceTimer = null;
876947

877948
// Monitor src attribute changes with MutationObserver for instant updates
878949
const observer = new MutationObserver((mutations) => {
@@ -888,7 +959,20 @@ document.addEventListener("DOMContentLoaded", () => {
888959
!newSrc.startsWith("about:")
889960
) {
890961
lastSrc = newSrc;
891-
updateDropdownsFromIframeUrl(index, newSrc);
962+
if (DEBUG_DROPDOWN_SYNC) {
963+
console.log(
964+
`${viewerPrefix} src attribute changed to:`,
965+
newSrc,
966+
);
967+
}
968+
969+
// Debounce to prevent multiple rapid calls
970+
if (debounceTimer) {
971+
clearTimeout(debounceTimer);
972+
}
973+
debounceTimer = setTimeout(() => {
974+
updateDropdownsFromIframeUrl(viewerIndex, newSrc);
975+
}, 50);
892976
}
893977
}
894978
});

scripts/neuview-iframe-controller.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@
8181
return;
8282
}
8383

84+
// CRITICAL: Verify the message came from THIS iframe, not another iframe
85+
if (
86+
!this.iframe.contentWindow ||
87+
event.source !== this.iframe.contentWindow
88+
) {
89+
return;
90+
}
91+
8492
this._log("Received message:", event.data);
8593

8694
const { type, data } = event.data;

0 commit comments

Comments
 (0)