1// load namespace 2export const SV = window.SV || {}; 3 4SV.HoverIntent = (function () { 5 // constructor 6 return function (elements, userConfig) { 7 // private members 8 9 const defaultOptions = { 10 exitDelay: 400, 11 exitDropdownDelay: 200, 12 interval: 100, 13 sensitivity: 7, 14 dropdownElement: ".mega-dropdown-content", 15 }; 16 let config = {}; 17 18 let currX, currY, prevX, prevY; 19 let allElems, pollTimer, exitTimer; 20 21 // private methods 22 23 // override default options with user config 24 const extend = function (defaults, userArgs) { 25 for (let i in userArgs) { 26 defaults[i] = userArgs[i]; 27 } 28 29 return defaults; 30 }; 31 32 // update mouse position 33 const mouseTrack = function (ev) { 34 currX = ev.pageX; 35 currY = ev.pageY; 36 }; 37 38 // check if mouse movement has slowed enough to trigger active state 39 const mouseCompare = function (targetElem) { 40 const distX = prevX - currX, 41 distY = prevY - currY; 42 const distance = Math.sqrt(distX * distX + distY * distY); 43 44 if (distance < config.sensitivity) { 45 // if we re-entered an element, cancel delayed exit and clear any active elements immediately 46 clearTimeout(exitTimer); 47 for (let elem of allElems) { 48 if (elem.isActive) { 49 config.onExit(elem); 50 elem.isActive = false; 51 } 52 } 53 54 // trigger hover 55 config.onEnter(targetElem); 56 targetElem.isActive = true; 57 } else { 58 // update previous coordinates and try again later 59 prevX = currX; 60 prevY = currY; 61 pollTimer = setTimeout(function () { 62 mouseCompare(targetElem); 63 }, config.interval); 64 } 65 }; 66 67 const init = function (elements, userConfig) { 68 if (!userConfig || !userConfig.onEnter || !userConfig.onExit) { 69 throw "onEnter and onExit callbacks must be provided"; 70 } 71 config = extend(defaultOptions, userConfig); 72 allElems = elements; 73 74 for (let elem of allElems) { 75 // holds current element state 76 elem.isActive = false; 77 // keeps track of mouse position 78 elem.addEventListener("mousemove", mouseTrack); 79 80 elem.addEventListener("mouseenter", function (ev) { 81 // set initial entry position 82 prevX = ev.pageX; 83 prevY = ev.pageY; 84 // if this element is already active, cancel exit 85 if (elem.isActive) { 86 clearTimeout(exitTimer); 87 return; 88 } 89 90 // while mouse is over this element, check distance every 100ms 91 pollTimer = setTimeout(function () { 92 mouseCompare(elem); 93 }, config.interval); 94 }); 95 elem.addEventListener("mouseleave", function (ev) { 96 clearTimeout(pollTimer); 97 if (!elem.isActive) return; 98 99 exitTimer = setTimeout(function () { 100 config.onExit(elem); 101 elem.isActive = false; 102 }, config.exitDelay); 103 }); 104 105 const dropdownContent = elem.querySelector(config.dropdownElement); 106 if (dropdownContent) { 107 dropdownContent.addEventListener("mouseleave", (e) => { 108 if (e.clientY > dropdownContent.getBoundingClientRect().bottom) { 109 exitTimer = setTimeout(() => { 110 config.onExit(elem); 111 elem.isActive = false; 112 }, config.exitDropdownDelay); 113 } 114 }); 115 } 116 } 117 }; 118 119 init(elements, userConfig); 120 }; 121})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.