PageSourceSearch

https://tonzon.nl/assets/2026/js/megamenu.js

js tonzon.nl collected 2026-09-26 07:56:21 UTC 18,148 bytes, 559 lines download raw bytes

1console.log("Megamenu script loaded");
2
3// megamenu renderer
4function renderSimpleMenu($placeholder, menuJson) {
5    if (!$placeholder || !menuJson || !menuJson.length) return;
6
7    const $ul = $('<ul>', {
8        class: 'list-unstyled fs-14 fw-500 text-decoration-underline column-2'
9    });
10
11    $.each(menuJson, function(_, item) {
12        if (item.noLink) return;
13
14        let title = decodeURIComponent(item.title || '')
15            .replace(/\s*\(.*?\)\s*/g, '')
16            .trim();
17
18        const $li = $('<li>', { class: 'mt-24 mt-lg-32' });
19
20        const $a = $('<a>', {
21            class: 'header-nav-link',
22            href: item.href || '#',
23            text: title
24        });
25
26        $li.append($a);
27        $ul.append($li);
28    });
29
30    $placeholder.empty().append($ul);
31} 
32
33// megamenu two column renderer
34function safeDecodeMenuTitle(str) {
35    str = String(str || '');
36
37    try {
38        return decodeURIComponent(str);
39    } catch (e) {
40        // fallback for broken latin1/cp1252-style percent encoding like %EB
41        return str.replace(/%([0-9A-Fa-f]{2})/g, function(match, hex) {
42            return String.fromCharCode(parseInt(hex, 16));
43        });
44    }
45}
46
47function cleanMenuTitle(title) {
48    return safeDecodeMenuTitle(title)
49        .replace(/\s*\(.*?\)\s*/g, '')
50        .trim();
51}
52
53function splitIntoColumns(items, columnCount) {
54    const result = [];
55    const total = items.length;
56
57    const baseSize = Math.floor(total / columnCount);
58    const remainder = total % columnCount;
59
60    let start = 0;
61
62    for (let i = 0; i < columnCount; i++) {
63        const extra = i < remainder ? 1 : 0;
64        const end = start + baseSize + extra;
65
66        result.push(items.slice(start, end));
67        start = end;
68    }
69
70    return result;
71}
72
73function renderMenuColumns($placeholder, menuJson, options) {
74    if (!$placeholder?.length || !menuJson?.length) return;
75
76    const settings = $.extend({
77        title: '',
78        columns: 2,
79        colClasses: [
80            'col-6 col-md-4 col-lg-3',
81            'col-6 col-md-3 col-lg-2 ps-lg-5'
82        ],
83        headingClass: 'fs-14 fw-500 mb-4 d-none d-md-block d-lg-none d-xl-block',
84        ulClass: 'list-unstyled fs-14 fw-500 text-decoration-underline',
85        firstLiClass: 'mt-8 mt-md-16 mt-lg-24',
86        liClass: 'mt-16 mt-md-24',
87        linkClass: 'header-nav-link'
88    }, options || {});
89
90    const items = $.grep(menuJson, item => !item.noLink);
91
92    const columns = splitIntoColumns(items, settings.columns);
93
94    $placeholder.empty();
95
96    $.each(columns, function(colIndex, columnItems) {
97        const colClass = settings.colClasses[colIndex] || 'col';
98        const headingText = colIndex === 0 ? settings.title : '&nbsp;';
99
100        const $col = $('<div>', { class: colClass });
101
102        const $heading = $('<h2>', {
103            class: settings.headingClass,
104            html: headingText
105        });
106
107        const $ul = $('<ul>', { class: settings.ulClass });
108
109        $.each(columnItems, function(itemIndex, item) {
110            const $li = $('<li>', {
111                class: itemIndex === 0 ? settings.firstLiClass : settings.liClass
112            });
113
114            const $a = $('<a>', {
115                class: settings.linkClass,
116                href: item.href || '#',
117                text: cleanMenuTitle(item.title)
118            });
119
120            $li.append($a);
121            $ul.append($li);
122        });
123
124        $col.append($heading).append($ul);
125        $placeholder.append($col);
126    });
127}      
128
129//GSAP és multi menu
130//Mobilon áthelyezzük a menüt:
131if ($(window).width() < 1200) {
132    $("#main-nav-BS-helye").append($("#main-nav-BS"));
133}
134
135//MULTI MENUS (submenük)
136$(document).ready(function() {
137    const $nav = $("#main-nav-BS");
138    const $mobileTarget = $("#main-nav-BS-helye");
139    const $desktopTarget = $("#main-nav-BS-origin");
140
141    function checkWidth() {
142        const windowWidth = $(window).width();
143
144        if (windowWidth < 1200) {
145            // Ha mobil nézet és még nincs a mobil helyén, áttesszük
146            if ($nav.parent().attr('id') !== 'main-nav-BS-helye') {
147                $mobileTarget.append($nav);
148            }
149        } else {
150            // Ha 1200 felett vagyunk, és még a mobil helyén van, visszatesszük az eredeti helyére
151            if ($nav.parent().attr('id') === 'main-nav-BS-helye') {
152                $desktopTarget.append($nav);
153                
154                // Opcionális: Ha asztalin nem kell a GSAP transform, töröljük le róla
155                gsap.set($nav, { clearProps: "all" });
156            }
157        }
158    }
159
160    // Futtatás betöltéskor (DOM ready után)
161    checkWidth();
162
163    // Futtatás minden átméretezéskor (resize)
164    $(window).on('resize', function() {
165        checkWidth();
166    });
167
168    function openMenu(menu, menuClass = '') {
169        const $targetMenu = $(menu);
170        const $otherMenus = $('.multi-menu').not($targetMenu);
171        const $overlay = $('#menu-overlay');
172
173        // 1. Többi menü bezárása (ha nyitva lenne bármelyik)
174        if ($otherMenus.is(':visible')) {
175            gsap.to($otherMenus, {
176                duration: 0.3,
177                height: 0,
178                opacity: 0,
179                display: 'none',
180                ease: "power2.inOut"
181            });
182        }
183
184        // 2. Logika: Nyitás vagy Csukás?
185        if ($targetMenu.is(':visible')) {
186            // CSUKÁS
187            gsap.to($targetMenu, {
188                duration: 0.4,
189                height: 0,
190                opacity: 0,
191                ease: "power2.inIn",
192                onComplete: () => $targetMenu.hide()
193            });
194            
195            gsap.to($overlay, {
196                duration: 0.4,
197                opacity: 0,
198                display: 'none'
199            });
200
201            // if ($(window).width() < 1200) {
202            //     gsap.set($('#offerte-mobile-fixed'), { x: "-100%" });
203            // }
204        } else {
205            // NYITÁS
206            const isMobile = $(window).width() < 1200;
207
208            // Reset layer 3 if re-opening the alle menu
209            if (menu === '#multi-menu-alle') {
210                gsap.set($('#mega-layer3'), { display: 'none' });
211                $targetMenu.find('.row.gx-lg-5').show();
212            }
213
214            // Alapbeállítások (közös)
215            gsap.set($targetMenu, { 
216                display: 'block', 
217                height: 0, 
218                opacity: 0,
219                overflowY: 'hidden',
220            });
221
222            // Dinamikus célértékek meghatározása
223            const offerteH = isMobile ? (200 || 120) : 0;
224            const targetH = isMobile ? $(window).height() - 100 : 462;
225
226            gsap.to($targetMenu, {
227                duration: 0.6,
228                height: targetH, 
229                opacity: 1,
230                ease: "expo.out",
231                onComplete: function() {
232                    gsap.set($targetMenu, { 
233                        overflowY: 'scroll',
234                        webkitOverflowScrolling: 'touch',
235                        maxHeight: targetH,
236                        paddingBottom: isMobile ? offerteH : 0
237                    });
238                }
239            });
240
241            gsap.to($overlay, {
242                duration: 0.5,
243                display: 'block',
244                opacity: 1, // Feltételezve, hogy a CSS-ben az alap opacity pl. 0.5 vagy 1
245                ease: "power2.out"
246            });
247
248            // if (isMobile) {
249            //     gsap.set($('#offerte-mobile-fixed'), { x: "0%" });
250            // }
251            if (isMobile) {
252                if (menu === '#multi-menu-search') {
253                    gsap.set($('#offerte-mobile-fixed'), { x: '-100%' });
254                } else {
255                    setOfferteLayer(2);
256                }
257            }
258
259            // adjust menu height on mobile
260            if ($(window).width() < 800 && menuClass != '' && !$(menuClass).hasClass('mega-calculated-height')) {                
261                // console.log("Adjusting mega menu height for mobile " + menuClass + ", targetH " + targetH);
262                // console.log("offerteH: " + offerteH);
263                // console.log("Current mega menu height: " + $(menuClass).height());
264                // console.log("Calculated new height: " + ($(menuClass).height() - (targetH - $(menuClass).height()) - $(menuClass).height()));
265
266                var myOffset = 25;
267                if (menuClass === '.mega-informatie') myOffset = -25;
268
269                $(menuClass).height($(menuClass).height() + (targetH - $(menuClass).height()) - $(menuClass).height() - myOffset);
270                $(menuClass).css('overflow-y', 'scroll');
271                $(menuClass).addClass('mega-calculated-height');
272            }
273        }
274    }
275
276    // Close helpers (desktop only)
277    let menuCloseTimer = null;
278
279    function closeAllMenus() {
280        const $visibleMenus = $('.multi-menu:visible');
281        if (!$visibleMenus.length) return;
282        gsap.to($visibleMenus, {
283            duration: 0.3,
284            height: 0,
285            opacity: 0,
286            ease: "power2.inOut",
287            onComplete: () => $visibleMenus.hide()
288        });
289        gsap.to($('#menu-overlay'), {
290            duration: 0.3,
291            opacity: 0,
292            display: 'none'
293        });
294        setOfferteLayer(1);
295    }
296
297    function scheduleMenuClose() {
298        if ($(window).width() < 1200) return;
299        clearTimeout(menuCloseTimer);
300        menuCloseTimer = setTimeout(closeAllMenus, 150);
301    }
302
303    function cancelMenuClose() {
304        clearTimeout(menuCloseTimer);
305    }
306
307    // MEGAMENU triggers
308    $('#custom-menu-trigger').on('click mouseenter', function (e) {
309        if (e.type === 'mouseenter' && $(window).width() < 1200) return;
310        cancelMenuClose();
311        if (e.type === 'mouseenter' && $('#multi-menu-alle').is(':visible')) return;
312        e.preventDefault();
313        e.stopPropagation();
314        openMenu('#multi-menu-alle');
315    });
316    $('#custom-menu-trigger').closest('li').on('mouseleave', scheduleMenuClose);
317
318    $('#custom-menu-trigger-info').on('click mouseenter', function (e) {
319        if (e.type === 'mouseenter' && $(window).width() < 1200) return;
320        cancelMenuClose();
321        if (e.type === 'mouseenter' && $('#multi-menu-informatie').is(':visible')) return;
322        e.preventDefault();
323        e.stopPropagation();
324        openMenu('#multi-menu-informatie', '.mega-informatie');
325    });
326    $('#custom-menu-trigger-info').closest('li').on('mouseleave', scheduleMenuClose);
327
328    $('#custom-menu-trigger-tonzon').on('click mouseenter', function (e) {
329        if (e.type === 'mouseenter' && $(window).width() < 1200) return;
330        cancelMenuClose();
331        if (e.type === 'mouseenter' && $('#multi-menu-tonzon').is(':visible')) return;
332        e.preventDefault();
333        e.stopPropagation();
334        openMenu('#multi-menu-tonzon', '.mega-tonzon');
335    });
336    $('#custom-menu-trigger-tonzon').closest('li').on('mouseleave', scheduleMenuClose);
337
338    $('.custom-menu-trigger-search').on('click', function (e) {
339        // if (e.type === 'mouseenter' && $(window).width() < 1200) return;
340        cancelMenuClose();
341        // if (e.type === 'mouseenter' && $('#multi-menu-search').is(':visible')) return;
342        e.preventDefault();
343        e.stopPropagation();
344        openMenu('#multi-menu-search');
345    });
346    $('.custom-menu-trigger-search').closest('li').on('mouseleave', scheduleMenuClose);
347
348    // Keep menus open when hovering over the panel itself
349    $('#multi-menu-alle, #multi-menu-informatie, #multi-menu-tonzon, #multi-menu-search')
350        .on('mouseenter', cancelMenuClose)
351        .on('mouseleave', scheduleMenuClose);
352
353    $('.link-back').on('click', function (e) {
354        e.preventDefault();
355        e.stopPropagation();
356        $('.multi-menu').slideUp(200);
357        $('#menu-overlay').slideUp(300);
358        setOfferteLayer(1);
359    });   
360
361    $(document).on('click', function (e) {
362        if (!$(e.target).closest('.multi-menu').length) {
363            $('.multi-menu').slideUp(200);
364            $('#menu-overlay').slideUp(300);
365            setOfferteLayer(1);
366        }
367    });
368
369    //MAIN NAV (fõmenü)
370    // const $nav = $('#main-nav-BS');
371    const $body = $('body');
372    const $offerte = $('#offerte-mobile-fixed');
373
374    $nav.on('show.bs.collapse', function(e) {
375        //e.preventDefault();
376        
377        $body.addClass('menu-open');
378        $nav.addClass('show');
379
380        // Elõször láthatóvá tesszük, de még kint van
381        gsap.set($nav, { visibility: 'visible' });
382
383        // Beúsztatás a 0-ás pontra (a képernyõ bal széle)
384        gsap.to($nav, {
385            duration: 0.6,
386            x: "0%", // Bejön a helyére
387            ease: "expo.out",
388            overwrite: true
389        });
390        gsap.to($offerte, {
391            duration: 0.6,
392            x: "0%", // Bejön a helyére
393            ease: "expo.out",
394            overwrite: true
395        });
396        
397    });
398
399    $nav.on('hide.bs.collapse', function(e) {
400        //e.preventDefault();
401
402        gsap.to($nav, {
403            duration: 0.5,
404            x: "-100%", // Teljesen kimegy balra
405            ease: "power2.in",
406            onComplete: function() {
407                $nav.removeClass('show');
408                $nav.css('visibility', 'hidden');
409                $body.removeClass('menu-open');
410            }
411        });
412        gsap.to($offerte, {
413            duration: 0.5,
414            x: "-100%", // Teljesen kimegy balra
415            ease: "power2.in"
416        });
417    });
418});
419
420// render menu's START  
421// renderSimpleMenu($('.mega-vloerisolatie'), params_1165.menuJson);
422renderMenuColumns($('.mega-vloerisolatie'), mega_vloerisolatie, {
423    title: '',
424    columns: 2,
425    colClasses: [
426        'col-6',
427        'col-6'
428    ],
429    headingClass: 'd-none',
430    linkClass: 'header-nav-link lh-140'
431});
432renderSimpleMenu($('.mega-muurisolatie'), mega_muurisolatie);
433renderSimpleMenu($('.mega-dakisolatie'), mega_dakisolatie);
434renderSimpleMenu($('.mega-cv-isolatie'), mega_cvisolatie);     
435
436renderMenuColumns($('.mega-informatie'), mega_informatie, {
437    title: 'Meer informatie',
438    columns: 2,
439    colClasses: [
440        'col-12 col-lg-6',
441        'col-12 col-lg-6 mt-8 mt-lg-0'
442    ],
443});      
444
445renderMenuColumns($('.mega-tonzon'), mega_tonzon, {
446    title: 'Over Tonzon',
447    columns: 2,
448    colClasses: [
449        'col-12 col-lg-6',
450        'col-12 col-lg-6 mt-8 mt-lg-0'
451    ],
452});      
453
454// render menu's END
455
456// Mobile layer 3 ? opens category detail within #multi-menu-alle
457function setOfferteLayer(n) {
458    if ($(window).width() >= 1200) return;
459    const $o = $('#offerte-mobile-fixed');
460    $o.find('.review-block').toggleClass('d-none', n !== 1);
461    $o.find('.shop-card').toggleClass('d-none', n !== 2);
462    $o.find('.faq-card').toggleClass('d-none', n !== 3);
463
464    // show shop and faq above 800px on layer 2 and 3
465    if ($(window).width() > 800 && n > 1) {
466        $o.find('.shop-card').removeClass('d-none');
467        $o.find('.faq-card').removeClass('d-none');
468    }
469}
470
471function showMegaLayer3(target, title) {
472    let $layer3 = $('#mega-layer3');
473    if (!$layer3.length) {
474        $layer3 = $('<div>', { id: 'mega-layer3', class: 'px-10 pt-0' });
475        $layer3.append(
476            $('<div>', { class: 'mb-16' }).append(
477                $('<a>', {
478                    href: '#',
479                    class: 'f-14 color-2 link-reverse link-back-layer2',
480                    html: '<i class="ico ico-arrow-start"></i>Ga terug'
481                })
482            ),
483            $('<h2>', { id: 'mega-layer3-title', class: 'fs-22 fw-500 mt-10 mb-0' }),
484            $('<p>', { class: 'fs-14 fw-500 mt-24 mb-0', text: 'Meer over' }),
485            $('<div>', { id: 'mega-layer3-links' })
486        );
487        $('#multi-menu-alle .container-lg').append($layer3);
488        gsap.set($layer3, { display: 'none' });
489    }
490
491    // Build a flat link list from the target mega-item
492    const $megaItem = $('.mega-item.mega-' + target);
493    const $ul = $('<ul>', { class: 'list-unstyled fs-14 fw-500 text-decoration-underline' });
494    $megaItem.find('a').each(function () {
495        $('<li>', { class: 'mt-22' }).append(
496            $('<a>', { class: 'header-nav-link', href: $(this).attr('href'), text: $(this).text() })
497        ).appendTo($ul);
498    });
499
500    $('#mega-layer3-title').text(title);
501    $('#mega-layer3-links').html($ul);
502
503    $('#multi-menu-alle .row.gx-lg-5').hide();
504    gsap.set($layer3, { display: 'block', opacity: 0, x: 30 });
505    gsap.to($layer3, { opacity: 1, x: 0, duration: 0.3, ease: 'power2.out' });
506    setOfferteLayer(3);
507}
508
509function hideMegaLayer3() {
510    const $layer3 = $('#mega-layer3');
511    if (!$layer3.length || $layer3.css('display') === 'none') return;
512    gsap.to($layer3, {
513        opacity: 0, x: -30, duration: 0.25, ease: 'power2.in',
514        onComplete: function () {
515            gsap.set($layer3, { display: 'none', x: 0 });
516            $('#multi-menu-alle .row.gx-lg-5').show();
517            setOfferteLayer(2);
518        }
519    });
520}
521
522$(document).on('click', '.link-back-layer2', function (e) {
523    e.preventDefault();
524    e.stopPropagation();
525    hideMegaLayer3();
526});
527
528// mega selector on click and mouseenter
529$(document).on("mouseenter", ".mega-selector", function (e) {
530    e.preventDefault();
531
532    $(".mega-selector").removeClass("active");
533    $(this).addClass("active");
534
535    var target = $(this).attr("href").replace(/^\/|\/$/g, '');
536    console.log("Mega selector clicked: " + target);
537
538    // Mobile tap: open layer 3
539    if (e.type === 'click' && $(window).width() < 1200) {
540        var title = $(this).find('span').first().text().trim();
541        showMegaLayer3(target, title);
542        return;
543    }
544
545    // Ignore mouseenter on mobile
546    if (e.type === 'mouseenter' && $(window).width() < 1200) return;
547
548    // Desktop: switch the visible mega-item panel
549    if ($(".mega-item.mega-" + target).not(".d-none").length > 0) {
550        return;
551    }
552
553    $(".mega-item").stop(true, true);
554
555    $(".mega-item:not(.d-none)").slideUp(150, function() {
556        $(this).addClass("d-none").removeAttr("style");
557        $(".mega-item.mega-" + target).removeClass("d-none").hide().slideDown(300);
558    });
559});

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.