API Documentation: sync_script.py
SYNC_SCRIPT = "\n <script>\n document.addEventListener('DOMContentLoaded', function() {\n var plotDiv = document.querySelector('.plotly-graph-div');\n if (plotDiv) {\n var isUpdating = false;\n\n // Store original ranges and mapping information\n var originalRanges = {\n heatmapX: null,\n heatmapY: null,\n colDendX: null,\n rowDendY: null\n };\n\n // Store the number of data points for proper scaling\n var dataInfo = {\n numRows: 50, // Number of rows in heatmap\n numCols: 4 // Number of columns in heatmap\n };\n\n // Initialize original ranges\n setTimeout(function() {\n var layout = plotDiv.layout;\n originalRanges.heatmapX = [0, dataInfo.numCols - 1];\n originalRanges.heatmapY = [0, dataInfo.numRows - 1];\n originalRanges.colDendX = layout.xaxis2.range || [0, 45];\n originalRanges.rowDendY = layout.yaxis3.range || [0, 500];\n }, 100);\n\n plotDiv.on('plotly_relayout', function(eventData) {\n if (isUpdating) return;\n isUpdating = true;\n\n var updates = {};\n var hasUpdates = false;\n\n // Handle heatmap x-axis changes\n if (eventData['xaxis4.range[0]'] !== undefined) {\n var heatmapXRange = [eventData['xaxis4.range[0]'], eventData['xaxis4.range[1]']];\n\n // Calculate proportional range for column dendrogram\n var heatmapWidth = originalRanges.heatmapX[1] - originalRanges.heatmapX[0];\n var dendXWidth = originalRanges.colDendX[1] - originalRanges.colDendX[0];\n\n var startProp = (heatmapXRange[0] - originalRanges.heatmapX[0]) / heatmapWidth;\n var endProp = (heatmapXRange[1] - originalRanges.heatmapX[0]) / heatmapWidth;\n\n var dendXStart = originalRanges.colDendX[0] + (startProp * dendXWidth);\n var dendXEnd = originalRanges.colDendX[0] + (endProp * dendXWidth);\n\n updates['xaxis2.range'] = [dendXStart, dendXEnd];\n hasUpdates = true;\n }\n\n // Handle heatmap y-axis changes\n if (eventData['yaxis4.range[0]'] !== undefined) {\n var heatmapYRange = [eventData['yaxis4.range[0]'], eventData['yaxis4.range[1]']];\n\n // Calculate proportional range for row dendrogram\n var heatmapHeight = originalRanges.heatmapY[1] - originalRanges.heatmapY[0];\n var dendYHeight = originalRanges.rowDendY[1] - originalRanges.rowDendY[0];\n\n var startProp = (heatmapYRange[0] - originalRanges.heatmapY[0]) / heatmapHeight;\n var endProp = (heatmapYRange[1] - originalRanges.heatmapY[0]) / heatmapHeight;\n\n var dendYStart = originalRanges.rowDendY[0] + (startProp * dendYHeight);\n var dendYEnd = originalRanges.rowDendY[0] + (endProp * dendYHeight);\n\n updates['yaxis3.range'] = [dendYStart, dendYEnd];\n hasUpdates = true;\n }\n\n // Handle column dendrogram changes\n if (eventData['xaxis2.range[0]'] !== undefined) {\n var dendXRange = [eventData['xaxis2.range[0]'], eventData['xaxis2.range[1]']];\n\n var dendXWidth = originalRanges.colDendX[1] - originalRanges.colDendX[0];\n var heatmapWidth = originalRanges.heatmapX[1] - originalRanges.heatmapX[0];\n\n var startProp = (dendXRange[0] - originalRanges.colDendX[0]) / dendXWidth;\n var endProp = (dendXRange[1] - originalRanges.colDendX[0]) / dendXWidth;\n\n var heatmapXStart = originalRanges.heatmapX[0] + (startProp * heatmapWidth);\n var heatmapXEnd = originalRanges.heatmapX[0] + (endProp * heatmapWidth);\n\n updates['xaxis4.range'] = [heatmapXStart, heatmapXEnd];\n hasUpdates = true;\n }\n\n // Handle row dendrogram changes - FIXED VERSION\n if (eventData['yaxis3.range[0]'] !== undefined) {\n var dendYRange = [eventData['yaxis3.range[0]'], eventData['yaxis3.range[1]']];\n\n var dendYHeight = originalRanges.rowDendY[1] - originalRanges.rowDendY[0];\n var heatmapHeight = originalRanges.heatmapY[1] - originalRanges.heatmapY[0];\n\n // Calculate proportion based on the dendrogram's coordinate system\n var startProp = (dendYRange[0] - originalRanges.rowDendY[0]) / dendYHeight;\n var endProp = (dendYRange[1] - originalRanges.rowDendY[0]) / dendYHeight;\n\n // Map to heatmap coordinates, but consider the reversed y-axis\n // The dendrogram y-axis is NOT reversed, but heatmap y-axis IS reversed\n var heatmapYStart = originalRanges.heatmapY[0] + ((1 - endProp) * heatmapHeight);\n var heatmapYEnd = originalRanges.heatmapY[0] + ((1 - startProp) * heatmapHeight);\n\n // Ensure we don't go outside bounds\n heatmapYStart = Math.max(originalRanges.heatmapY[0], Math.min(originalRanges.heatmapY[1], heatmapYStart));\n heatmapYEnd = Math.max(originalRanges.heatmapY[0], Math.min(originalRanges.heatmapY[1], heatmapYEnd));\n\n updates['yaxis4.range'] = [heatmapYStart, heatmapYEnd];\n hasUpdates = true;\n }\n\n if (hasUpdates) {\n Plotly.relayout(plotDiv, updates).then(function() {\n isUpdating = false;\n });\n } else {\n isUpdating = false;\n }\n });\n\n // Handle selection events specifically\n plotDiv.on('plotly_selected', function(eventData) {\n if (isUpdating || !eventData || !eventData.range) return;\n isUpdating = true;\n\n var updates = {};\n var range = eventData.range;\n\n // Handle row dendrogram selection (yaxis3)\n if (range.y && eventData.points && eventData.points[0] && eventData.points[0].yaxis === 'y3') {\n var dendYRange = [range.y[0], range.y[1]];\n\n var dendYHeight = originalRanges.rowDendY[1] - originalRanges.rowDendY[0];\n var heatmapHeight = originalRanges.heatmapY[1] - originalRanges.heatmapY[0];\n\n var startProp = (dendYRange[0] - originalRanges.rowDendY[0]) / dendYHeight;\n var endProp = (dendYRange[1] - originalRanges.rowDendY[0]) / dendYHeight;\n\n // Handle reversed y-axis mapping\n var heatmapYStart = originalRanges.heatmapY[0] + ((1 - endProp) * heatmapHeight);\n var heatmapYEnd = originalRanges.heatmapY[0] + ((1 - startProp) * heatmapHeight);\n\n updates['yaxis3.range'] = dendYRange;\n updates['yaxis4.range'] = [heatmapYStart, heatmapYEnd];\n\n Plotly.relayout(plotDiv, updates).then(function() {\n isUpdating = false;\n });\n } else {\n isUpdating = false;\n }\n });\n }\n });\n </script>\n"
module-attribute