@@ -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 } ) ;
0 commit comments