PageSourceSearch

https://www.wunderkarten.de/common/components-js/hover-intent.js

js wunderkarten.de collected 2026-09-28 11:54:45 UTC 4,296 bytes, 121 lines download raw bytes

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.