Tree of Life

Click a sephirah or path

Overview Detail
function startPan(clientX, clientY) { isPanning = true; didPan = false; panStart = { x: clientX, y: clientY, px: panX, py: panY }; document.getElementById("svg-container").classList.add("dragging"); } function movePan(clientX, clientY) { if (!isPanning) return; const svgEl = document.querySelector("#svg-container svg"); if (!svgEl) return; const ctm = svgEl.getScreenCTM(); if (!ctm) return; // ctm.a = screen pixels per SVG unit; invert to get SVG units per pixel const scale = ctm.a; const dx = (clientX - panStart.x) / scale; const dy = (clientY - panStart.y) / scale; if (Math.abs(clientX - panStart.x) > 4 || Math.abs(clientY - panStart.y) > 4) didPan = true; panX = panStart.px - dx; panY = panStart.py - dy; svgEl.setAttribute("viewBox", viewBoxForZoom(cvFullVbW, cvFullVbH)); } function endPan() { if (!isPanning) return; isPanning = false; document.getElementById("svg-container").classList.remove("dragging"); } const svgContainer = document.getElementById("svg-container"); svgContainer.addEventListener("mousedown", e => { if (!cardView) return; e.preventDefault(); startPan(e.clientX, e.clientY); }); document.addEventListener("mousemove", e => { movePan(e.clientX, e.clientY); }); document.addEventListener("mouseup", endPan); svgContainer.addEventListener("touchstart", e => { if (!cardView || e.touches.length !== 1) return; startPan(e.touches[0].clientX, e.touches[0].clientY); }, { passive: true }); svgContainer.addEventListener("touchmove", e => { if (e.touches.length !== 1) return; movePan(e.touches[0].clientX, e.touches[0].clientY); }, { passive: true }); svgContainer.addEventListener("touchend", endPan, { passive: true }); // Capture-phase click handler: suppress lightbox if the mousedown was a drag svgContainer.addEventListener("click", e => { if (didPan) { e.stopImmediatePropagation(); didPan = false; } }, true);