c83b730a18186bcddc4fe008a48317ac577d9bde
kate
  Sat Nov 12 20:17:41 2016 -0800
Fix twitchy highlights. refs #17369

diff --git src/hg/js/hgGtexTrackSettings.js src/hg/js/hgGtexTrackSettings.js
index 33ab0ce..f03064e 100644
--- src/hg/js/hgGtexTrackSettings.js
+++ src/hg/js/hgGtexTrackSettings.js
@@ -18,30 +18,31 @@
 //      3. highlight surround   <path id=$tissue_Aura_Hi>
 //      4. label                <text id=$tissue_Text_Hi>
 //      5. leader line          <line | polyline id=$tissue_Lead_Hi>
 //
 // Implementation note: jQuery is used below where effective.  Some SVG manipulation 
 //  (e.g. add/remove/toggle classes) don't work from jQuery, so code for that is raw javascript
 
 var gtexTrackSettings = (function() {
 
     // Globals
     
     var _svgDoc;        // SVG has its own DOM
     var _svgRoot;
 
     var _topTissue;     // Highlighted tissue is drawn last so it is on top
+    var _topTissueName = null;
     var _topAura;
 
     var TEXT_HI = '_Text_Hi';
     var LEAD_HI = '_Lead_Hi';
     var AURA_HI = '_Aura_Hi';
     var PIC_HI = '_Pic_Hi';
     var PIC_LO = '_Pic_Lo';
 
     var COLOR_BLACK = 'black';
     var COLOR_SELECTED = COLOR_BLACK;
     var COLOR_BLUE = 'blue';
     var COLOR_HIGHLIGHT = COLOR_BLUE;
     var COLOR_GRAY = '#737373';
     var COLOR_UNSELECTED = COLOR_GRAY;
     var COLOR_PINK = '#F69296';
@@ -144,134 +145,170 @@
             clearMapTissueElColor(el);
         }
     }
 
     function toggleTissue(tis) {
         // Select/unselect tissue in list and body map
         var $tis = $('#' + tis);
         $tis.toggleClass(CLASS_TISSUE_SELECTED);
         if ($tis.hasClass(CLASS_TISSUE_SELECTED)) {
             setTissue(tis);
         } else {
             clearTissue(tis);
         }
     }
 
-    function toggleHighlightTissue(tis) {
-        // Highlight tissue in body map and tissue table
-        // TODO: simplify me
+    function highlightTissue(tis) {
+        // Highlight tissue label and color patch in tissue table
+        toggleHighlightTissue(tis, true);
+    }
+
+    function unHighlightTissue(tis) {
+        toggleHighlightTissue(tis, false);
+    }
+
+    function toggleHighlightTissue(tis, isHovered) {
+        // Highlight/unhighlight tissue in body map and tissue table
 
         // Highlight tissue label and color patch in tissue table
         var $tis = $('#' + tis);
-        if ($tis !== null) {
-            $tis.toggleClass(CLASS_TISSUE_HOVERED);
-            var $colorPatch = $tis.prev('.tissueColor');
-            $colorPatch.toggleClass('tissueHoveredColor');
+        if (tis === null) {
+            return;
         }
-        var isHovered = $tis.hasClass(CLASS_TISSUE_HOVERED) ? true : false;
+        if (isHovered && _topTissueName !== null) {
+            return;
+        }
+        $tis.toggleClass(CLASS_TISSUE_HOVERED, isHovered);
+        var $colorPatch = $tis.prev('.tissueColor');
+        $colorPatch.toggleClass('tissueHoveredColor', isHovered);
 
         // Highlight tissue in body map by changing text appearance, visually moving organ to top
         // and adding a white (or sometimes blue if white background) surround ('aura')
 
         // Highlight tissue label in body map
         // TODO:  Try jQuery here
         textEl = _svgDoc.getElementById(tis + TEXT_HI);
         if (textEl === null) {
             return;
         }
         // TODO: unify with text styling below.  Perhaps just add class to children will do it.
-        textEl.classList.toggle(CLASS_TISSUE_HOVERED);
+        textEl.classList.toggle(CLASS_TISSUE_HOVERED, isHovered);
 
         var lineEl = _svgDoc.getElementById(tis + LEAD_HI);
         var pic = $('#' + tis + PIC_HI, _svgRoot);
         var picEl = _svgDoc.getElementById(tis + PIC_HI);
         var aura = $('#' + tis + AURA_HI, _svgRoot);
         var auraEl = _svgDoc.getElementById(tis + AURA_HI);
         var textLineCount = textEl.childElementCount;
         var i;
 
         if (isHovered) {
             textEl.style.fill = COLOR_HIGHLIGHT;
             for (i = 0; i < textLineCount; i++) {
                 textEl.children[i].style.fill = COLOR_HIGHLIGHT;
             }
             if (lineEl !== null) {     // cell types lack leader lines
             lineEl.style.stroke = COLOR_HIGHLIGHT;
             }
-            $(pic).show();
-            $(aura).show();
-
             var topAura = auraEl.cloneNode(true);
             topAura.id = 'topAura';
             _topAura = _svgRoot.appendChild(topAura);
+            $(_topAura).show();
         
             var topTissue = picEl.cloneNode(true);
+            topTissue.addEventListener('mouseleave', onMapLeaveTissue);
             topTissue.id = 'topTissue';
-            topTissue.classList.add(tis);
+            _topTissueName = tis;
             _topTissue = _svgRoot.appendChild(topTissue);
+            $(_topTissue).show();
         } else {
             var color = textEl.classList.contains(CLASS_TISSUE_SELECTED) ? 
                                 COLOR_SELECTED : COLOR_UNSELECTED;
             textEl.style.fill = color;
             for (i = 0; i < textLineCount; i++) {
                 textEl.children[i].style.fill = color;
             }
-            $(aura).hide();
-            $(pic).hide();
             if (lineEl !== null) {     // cell types lack leader lines
                 lineEl.style.stroke = COLOR_LEADER;      // pink
             }
             _svgRoot.removeChild(_topTissue);
+            _topTissueName = null;
             _svgRoot.removeChild(_topAura);
         }
     }
 
     // Event handlers
 
     function onClickSetAll() {
         // Set all on body map and tissue list
         tissues.forEach(setTissue);
     }
 
     function onClickClearAll() {
         // Clear all on body map and tissue list
         tissues.forEach(clearTissue);
     }
 
     function onClickToggleTissue(tis) {
         // Select/unselect from tissue list
         tis = this.id;  // arg bad
         toggleTissue(tis);
     }
 
     function onMapClickToggleTissue(ev) {
         // Select/unselect from illustration
         var svgId = ev.currentTarget.id;
         var tis = tissueFromSvgId(svgId);
         toggleTissue(tis);
     }
 
-    function onHoverTissue() {
+    function onEnterTissue() {
         // Mouseover on label in tissue list
-        toggleHighlightTissue(this.id);
+        highlightTissue(this.id);
     }
 
-    function onMapHoverTissue(ev) {
+    function onLeaveTissue() {
+        // Mouseover on label in tissue list
+        unHighlightTissue(this.id);
+    }
+
+    function onMapEnterTissue(ev) {
         // Mouseover on tissue shape or label in illustration
         var svgId = ev.currentTarget.id;
-        var tis = tissueFromSvgId(svgId);
-        toggleHighlightTissue(tis);
+        var tis = (svgId === 'topTissue' ? _topTissueName :  tissueFromSvgId(svgId));
+        highlightTissue(tis);
+    }
+
+    function onMapLeaveTissue(ev) {
+        // Mouseover on tissue shape or label in illustration
+        var toTarget = ev.relatedTarget;
+        var toParent;
+
+        //  Handle case where lower and upper shapes are not the same.  If leaving lower to enter upper, we are not really leaving
+        if (toTarget) {
+            if (toTarget.id === 'topTissue') {
+                return;
+            }
+            //  Handle case where there are multiple paths for the tissue, and topTissue will be a parent
+            toParent = toTarget.parentElement;
+            if (toParent && toParent.id === 'topTissue') {
+                return;
+            }
+        }
+        var svgId = ev.currentTarget.id;
+        var tis = (svgId === 'topTissue' ? _topTissueName :  tissueFromSvgId(svgId));
+        unHighlightTissue(tis);
     }
 
     function submitForm() {
     // Submit the form (from GO button -- as in hgGateway.js)
     // Show a spinner -- sometimes it takes a while for hgTracks to start displaying.
     $('.gbGoIcon').removeClass('fa-play').addClass('fa-spinner fa-spin');
     $form = $('form');
     $form.submit();
     }
 
     // Initialization
 
     function initTissue(tis) {
         // Set tissue to unhighlighted state
         $('#' + tis + PIC_HI, _svgRoot).hide();
@@ -286,44 +323,44 @@
                 setMapTissueElColor(textEl);
             }
         }
     }
 
     function initBodyMap() {
         // Set organs to unhighlighted state
         tissues.forEach(initTissue);
     }
 
     function animateTissue(tis) {
         // Add event handlers to body map and tissue list
 
         // Add click and mouseover handler to tissue label in tissue list
         $('#' + tis).click(tis, onClickToggleTissue);
-        $('#' + tis).hover(onHoverTissue, onHoverTissue);
+        $('#' + tis).hover(onEnterTissue, onLeaveTissue);
 
         // Add mouseover and click handlers to tissue label in body map
         var textEl = _svgDoc.getElementById(tis + TEXT_HI);
         if (textEl !== null) {
             textEl.addEventListener('click', onMapClickToggleTissue);
-            textEl.addEventListener('mouseenter', onMapHoverTissue);
-            textEl.addEventListener('mouseleave', onMapHoverTissue);
+            textEl.addEventListener('mouseenter', onMapEnterTissue);
+            textEl.addEventListener('mouseleave', onMapLeaveTissue);
         }
         // add mouseover and click handlers to tissue shape
         var picEl = _svgDoc.getElementById(tis + PIC_LO);
         if (picEl !== null) {
-            picEl.addEventListener('mouseenter', onMapHoverTissue);
-            picEl.addEventListener('mouseleave', onMapHoverTissue);
+            picEl.addEventListener('mouseenter', onMapEnterTissue);
+            picEl.addEventListener('mouseleave', onMapLeaveTissue);
             picEl.addEventListener('mouseup', onMapClickToggleTissue);
         }
     }
 
     function animateTissues() {
         // Add event handlers to tissue table and body map SVG
         $('#setAll').click(onClickSetAll);
         $('#clearAll').click(onClickClearAll);
         tissues.forEach(animateTissue);
     }
 
     function init() {
         // cart.setCgi('gtexTrackSettings');
 
         $(function() {