PageSourceSearch

https://verifymy.io/wp-content/themes/hello-theme-child-master/js/developer-documentation.js

js verifymy.io collected 2026-10-02 17:49:17 UTC 19,210 bytes, 528 lines download raw bytes

1function toggleDeveloperMenu() {
2    const menu = document.getElementById('dev-doc-menu');
3    document.body.classList.toggle('menu-developer-documentation-is-collapsed');
4    menu.classList.toggle('collapsed');
5}
6
7function toggleDeveloperMenuMobile() {
8    document.body.classList.toggle('developer-documentation-menu-opened');
9}
10
11function expandDeveloperMenu(event) {
12    event.preventDefault();
13
14    const link = event.target.closest('a');
15    const section = event.target.closest('.menu-developer-documentation-section');
16    const list = section.querySelector('.menu-developer-documentation-subitems');
17    
18    const menu = document.getElementById('dev-doc-menu');
19    if (menu.classList.contains('collapsed')) {
20        toggleDeveloperMenu();
21        
22        link.classList.add('expanded');
23        section.classList.add('expanded');
24        list.classList.add('expanded');
25        
26        return;
27    }
28
29    link.classList.toggle('expanded');
30    section.classList.toggle('expanded');
31    list.classList.toggle('expanded');
32}
33
34/* TABS */
35const developerDocumentationTabsScrollContainer = document.querySelector('.developer-documentation-tabs__container');
36const developerDocumentationTabs = document.querySelector('.developer-documentation-tabs');
37
38if (developerDocumentationTabsScrollContainer) {
39    developerDocumentationTabsScrollContainer.addEventListener('scroll', () => {
40        adjustDeveloperDocumentationTabsScroll();
41    });
42    
43    window.addEventListener('resize', () => {
44        adjustDeveloperDocumentationTabsScroll();
45        adjustDeveloperDocumentationTabsActiveScroll();
46    });
47    
48    window.addEventListener('load', function() {
49        adjustDeveloperDocumentationTabsScroll();
50        adjustDeveloperDocumentationTabsActiveScroll();
51    });
52    
53    // Add drag scrolling functionality
54    let isDown = false;
55    let startX;
56    let scrollLeft;
57    let moved = false;
58    // Minimum horizontal distance (px) the pointer must travel before a
59    // mousedown+move is treated as a drag instead of a click. Increase to make
60    // clicks more forgiving, decrease to make dragging more sensitive.
61    const DRAG_THRESHOLD = 10;
62    
63    // Prevent default drag behavior on all links
64    const tabLinks = developerDocumentationTabsScrollContainer.querySelectorAll('a');
65    tabLinks.forEach(link => {
66        link.addEventListener('dragstart', (e) => {
67            e.preventDefault();
68        });
69    });
70    
71    developerDocumentationTabsScrollContainer.addEventListener('mousedown', (e) => {
72        if (!isDeveloperDocumentationTabsScrollable()) return;
73        isDown = true;
74        moved = false;
75        developerDocumentationTabsScrollContainer.classList.add('active-drag');
76        startX = e.pageX - developerDocumentationTabsScrollContainer.offsetLeft;
77        scrollLeft = developerDocumentationTabsScrollContainer.scrollLeft;
78    });
79    
80    developerDocumentationTabsScrollContainer.addEventListener('mouseleave', () => {
81        isDown = false;
82        developerDocumentationTabsScrollContainer.classList.remove('active-drag');
83    });
84    
85    developerDocumentationTabsScrollContainer.addEventListener('mouseup', () => {
86        isDown = false;
87        developerDocumentationTabsScrollContainer.classList.remove('active-drag');
88        // Reset moved flag after a short delay to allow click event to check it
89        setTimeout(() => {
90            moved = false;
91        }, 10);
92    });
93    
94    developerDocumentationTabsScrollContainer.addEventListener('mousemove', (e) => {
95        if (!isDown) return;
96        const x = e.pageX - developerDocumentationTabsScrollContainer.offsetLeft;
97        const walk = (x - startX) * 2;
98        // Only treat as a drag once the pointer has moved past the threshold,
99        // so small jitters between mousedown/mouseup still count as a click.
100        if (!moved && Math.abs(x - startX) < DRAG_THRESHOLD) return;
101        e.preventDefault();
102        moved = true;
103        developerDocumentationTabsScrollContainer.scrollLeft = scrollLeft - walk;
104    });
105    
106    // Prevent clicks on links when dragging - use capture phase
107    tabLinks.forEach(link => {
108        link.addEventListener('click', (e) => {
109            if (moved) {
110                e.preventDefault();
111                e.stopPropagation();
112                e.stopImmediatePropagation();
113            }
114        }, { capture: true });
115    });
116    
117    // Touch scrolling works natively with overflow-x: auto
118}
119
120function isDeveloperDocumentationTabsScrollable() {
121    return developerDocumentationTabsScrollContainer.scrollWidth > developerDocumentationTabsScrollContainer.clientWidth + 1;
122}
123
124function adjustDeveloperDocumentationTabsScroll() {
125    // Toggle scrollable state so the grab cursor/drag only apply when scrolling
126    // is actually possible (independent of current scroll position).
127    developerDocumentationTabs.classList.toggle('is-scrollable', isDeveloperDocumentationTabsScrollable());
128
129    // Check if the user has scrolled to the very end (also true when not scrollable)
130    // This calculation might need slight adjustments depending on padding/borders
131    if (developerDocumentationTabsScrollContainer.scrollLeft + developerDocumentationTabsScrollContainer.clientWidth >= developerDocumentationTabsScrollContainer.scrollWidth - 1) {
132        developerDocumentationTabs.classList.add('no-overflow');
133    } else {
134        developerDocumentationTabs.classList.remove('no-overflow');
135    } 
136}
137
138function adjustDeveloperDocumentationTabsActiveScroll() {
139    const activeTab = developerDocumentationTabsScrollContainer.querySelector('.developer-documentation-tabs__item.active');
140    if (activeTab) {
141        // Calculate the position to scroll to center the active tab
142        const scrollLeft = activeTab.offsetLeft - (developerDocumentationTabsScrollContainer.offsetWidth / 2) + (activeTab.offsetWidth / 2);
143        
144        // Scroll only the container, not the entire page
145        developerDocumentationTabsScrollContainer.scrollTo({
146            left: scrollLeft,
147            behavior: 'smooth'
148        });
149    }
150}
151
152/* TOC */
153class ElementorTOCActiveHandler {
154    constructor(options = {}) {
155        this.tocSelector = options.tocSelector || '.elementor-toc__list-item';
156        this.linkSelector = options.linkSelector || '.elementor-toc__list-item-text';
157        this.activeClass = options.activeClass || 'active';
158        this.rootMargin = options.rootMargin || '0px 0px -10% 0px';
159        this.threshold = options.threshold || [0, 0.25, 0.5, 0.75, 1];
160        
161        this.tocItems = [];
162        this.targetElements = new Map();
163        this.visibilityRatios = new Map();
164        this.observer = null;
165        
166        this.init();
167    }
168    
169    init() {
170        // Wait for DOM to be ready, then wait for TOC to be rendered
171        if (document.readyState === 'loading') {
172            document.addEventListener('DOMContentLoaded', () => this.waitForTOC());
173        } else {
174            this.waitForTOC();
175        }
176    }
177    
178    waitForTOC() {
179        // First check if the developer documentation TOC exists
180        const checkDevDocTOC = () => {
181            return document.querySelector('.developer-documentation-toc');
182        };
183        
184        // Check if TOC elements exist within the dev doc TOC
185        const checkTOC = () => {
186            const devDocTOC = checkDevDocTOC();
187            if (!devDocTOC) {
188                return false;
189            }
190            
191            const tocElements = devDocTOC.querySelectorAll(this.tocSelector);
192            const tocLinks = devDocTOC.querySelectorAll(this.linkSelector);
193            
194            if (tocElements.length > 0 && tocLinks.length > 0) {
195                this.setup();
196                return true;
197            }
198            return false;
199        };
200        
201        // Try immediately first
202        if (checkTOC()) {
203            return;
204        }
205        
206        // If not found, use multiple strategies to wait for TOC
207        let attempts = 0;
208        const maxAttempts = 50; // 10 seconds maximum
209        
210        const pollForTOC = () => {
211            attempts++;
212            
213            if (checkTOC()) {
214                return; // Found it!
215            }
216            
217            if (attempts < maxAttempts) {
218                setTimeout(pollForTOC, 200);
219            }
220        };
221        
222        // Start polling
223        setTimeout(pollForTOC, 200);
224        
225        // Also listen for various events that might indicate TOC is ready
226        const eventListeners = [
227            'load',
228            'elementor/frontend/init',
229            'elementor/frontend/ready'
230        ];
231        
232        eventListeners.forEach(eventName => {
233            window.addEventListener(eventName, () => {
234                setTimeout(() => {
235                    if (!this.tocItems.length) {
236                        checkTOC();
237                    }
238                }, 100);
239            });
240        });
241        
242        // Watch for DOM mutations (when Elementor adds the TOC)
243        const observer = new MutationObserver((mutations) => {
244            mutations.forEach((mutation) => {
245                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
246                    mutation.addedNodes.forEach((node) => {
247                        if (node.nodeType === 1) { // Element node
248                            // Check if the added node is or contains our dev doc TOC
249                            const isDevDocTOC = node.classList.contains('developer-documentation-toc') ||
250                                              node.querySelector && node.querySelector('.developer-documentation-toc');
251                            
252                            if (isDevDocTOC && !this.tocItems.length) {
253                                setTimeout(() => {
254                                    checkTOC();
255                                }, 100);
256                            }
257                        }
258                    });
259                }
260            });
261        });
262        
263        observer.observe(document.body, {
264            childList: true,
265            subtree: true
266        });
267        
268        // Clean up observer after TOC is found
269        const originalSetup = this.setup.bind(this);
270        this.setup = function() {
271            observer.disconnect();
272            originalSetup();
273        };
274    }
275    
276    setup() {
277        this.collectTOCItems();
278        this.setupIntersectionObserver();
279        this.observeTargetElements();
280        
281        // Set initial active state
282        setTimeout(() => this.updateActiveStates(), 100);
283    }
284    
285    collectTOCItems() {
286        // Make sure we're only looking within the developer documentation TOC
287        const devDocTOC = document.querySelector('.developer-documentation-toc');
288        if (!devDocTOC) {
289            return;
290        }
291        
292        const tocElements = devDocTOC.querySelectorAll(this.tocSelector);
293        
294        tocElements.forEach(tocItem => {
295            const link = tocItem.querySelector(this.linkSelector);
296            if (!link) return;
297            
298            const href = link.getAttribute('href');
299            if (!href || !href.startsWith('#')) return;
300            
301            const targetId = href.substring(1);
302            const directTarget = document.getElementById(targetId);
303            
304            if (directTarget) {
305                // Find the parent .elementor-element with data-element_type="container"
306                let targetElement = directTarget.closest('.elementor-element[data-element_type="container"]');
307                
308                // Fallback: if no container parent, try any .elementor-element
309                if (!targetElement) {
310                    targetElement = directTarget.closest('.elementor-element');
311                }
312                
313                // Final fallback: use the direct target
314                if (!targetElement) {
315                    targetElement = directTarget;
316                }
317                
318                this.tocItems.push({
319                    tocItem,
320                    link,
321                    targetElement,
322                    targetId,
323                    directTarget // Keep reference to original target
324                });
325                
326                this.targetElements.set(targetElement, {
327                    tocItem,
328                    targetId,
329                    visibilityRatio: 0,
330                    directTarget
331                });
332                
333                this.visibilityRatios.set(targetId, 0);
334            }
335        });
336    }
337    
338    setupIntersectionObserver() {
339        const options = {
340            root: null,
341            rootMargin: this.rootMargin,
342            threshold: this.threshold
343        };
344        
345        this.observer = new IntersectionObserver((entries) => {
346            entries.forEach(entry => {
347                const targetData = this.targetElements.get(entry.target);
348                if (targetData) {
349                    targetData.visibilityRatio = entry.intersectionRatio;
350                    this.visibilityRatios.set(targetData.targetId, entry.intersectionRatio);
351                }
352            });
353            
354            this.updateActiveStates();
355        }, options);
356    }
357    
358    observeTargetElements() {
359        this.targetElements.forEach((data, element) => {
360            this.observer.observe(element);
361        });
362    }
363    
364    updateActiveStates() {
365        // Remove all active classes first
366        this.tocItems.forEach(({ tocItem }) => {
367            tocItem.classList.remove(this.activeClass);
368        });
369        
370        // Find the element with highest visibility ratio
371        let maxVisibilityRatio = 0;
372        let mostVisibleElement = null;
373        
374        // Check for fully visible elements first
375        for (const [targetId, ratio] of this.visibilityRatios.entries()) {
376            if (ratio >= 0.99) { // Consider 99%+ as fully visible
377                const targetData = Array.from(this.targetElements.values())
378                    .find(data => data.targetId === targetId);
379                
380                if (targetData) {
381                    mostVisibleElement = targetData;
382                    break;
383                }
384            }
385        }
386        
387        // If no fully visible element, find the one with highest visibility
388        if (!mostVisibleElement) {
389            for (const [targetId, ratio] of this.visibilityRatios.entries()) {
390                if (ratio > maxVisibilityRatio) {
391                    maxVisibilityRatio = ratio;
392                    const targetData = Array.from(this.targetElements.values())
393                        .find(data => data.targetId === targetId);
394                    
395                    if (targetData) {
396                        mostVisibleElement = targetData;
397                    }
398                }
399            }
400        }
401        
402        // Apply active class to the most visible element
403        if (mostVisibleElement && (mostVisibleElement.visibilityRatio > 0 || maxVisibilityRatio > 0)) {
404            mostVisibleElement.tocItem.classList.add(this.activeClass);
405            
406            // Optional: scroll the TOC item into view if it's outside viewport
407            this.scrollTOCItemIntoView(mostVisibleElement.tocItem);
408        }
409        
410        // Fallback: if no elements are visible, activate the first one
411        if (maxVisibilityRatio === 0 && this.tocItems.length > 0) {
412            // Check if we're at the top of the page
413            if (window.scrollY < 100) {
414                this.tocItems[0].tocItem.classList.add(this.activeClass);
415            }
416        }
417    }
418    
419    scrollTOCItemIntoView(tocItem) {
420        const tocContainer = tocItem.closest('.elementor-toc__body, .elementor-widget-table-of-contents');
421        if (tocContainer) {
422            const tocRect = tocContainer.getBoundingClientRect();
423            const itemRect = tocItem.getBoundingClientRect();
424            
425            // Check if item is outside the visible TOC area
426            if (itemRect.top < tocRect.top || itemRect.bottom > tocRect.bottom) {
427                tocItem.scrollIntoView({
428                    behavior: 'smooth',
429                    block: 'nearest',
430                    inline: 'nearest'
431                });
432            }
433        }
434    }
435    
436    // Method to manually refresh the active states
437    refresh() {
438        this.updateActiveStates();
439    }
440    
441    // Method to destroy the observer
442    destroy() {
443        if (this.observer) {
444            this.observer.disconnect();
445        }
446        
447        // Remove all active classes
448        this.tocItems.forEach(({ tocItem }) => {
449            tocItem.classList.remove(this.activeClass);
450        });
451    }
452}
453
454// Auto-initialize when script loads
455(function() {
456    let tocHandler = null;
457    
458    const initializeTOC = () => {
459        if (tocHandler) {
460            tocHandler.destroy();
461        }
462        tocHandler = new ElementorTOCActiveHandler();
463        window.tocActiveHandler = tocHandler;
464    };
465    
466    // Initialize immediately
467    initializeTOC();
468    
469    // Re-initialize on various Elementor events
470    const elementorEvents = [
471        'elementor/frontend/init',
472        'elementor/frontend/ready',
473        'elementor/loaded'
474    ];
475    
476    elementorEvents.forEach(eventName => {
477        document.addEventListener(eventName, () => {
478            setTimeout(initializeTOC, 500);
479        });
480    });
481    
482    // Listen for Elementor frontend hooks if available
483    if (typeof elementorFrontend !== 'undefined') {
484        elementorFrontend.hooks.addAction('frontend/element_ready/widget', function(scope) {
485            const tocWidget = scope.find('.elementor-widget-table-of-contents');
486            if (tocWidget.length > 0) {
487                setTimeout(initializeTOC, 300);
488            }
489        });
490    } else {
491        // If elementorFrontend isn't available yet, wait for it
492        const waitForElementor = () => {
493            if (typeof elementorFrontend !== 'undefined') {
494                elementorFrontend.hooks.addAction('frontend/element_ready/widget', function(scope) {
495                    const tocWidget = scope.find('.elementor-widget-table-of-contents');
496                    if (tocWidget.length > 0) {
497                        setTimeout(initializeTOC, 300);
498                    }
499                });
500            } else {
501                setTimeout(waitForElementor, 100);
502            }
503        };
504        setTimeout(waitForElementor, 100);
505    }
506    
507    // Fallback: Watch for specific Elementor classes being added
508    const observer = new MutationObserver((mutations) => {
509        mutations.forEach((mutation) => {
510            if (mutation.type === 'attributes' && 
511                mutation.target.classList && 
512                mutation.target.classList.contains('elementor-widget-table-of-contents')) {
513                setTimeout(initializeTOC, 200);
514            }
515        });
516    });
517    
518    observer.observe(document.body, {
519        attributes: true,
520        attributeFilter: ['class'],
521        subtree: true
522    });
523})();
524
525// Export for module usage
526if (typeof module !== 'undefined' && module.exports) {
527    module.exports = ElementorTOCActiveHandler;
528}

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.