PageSourceSearch

https://www.drmartensplc.com/application/js/mega-menu.js?ccm_nocache=930369f3efb261594a394a9034d268e06ede3a65

js drmartensplc.com collected 2026-09-25 03:44:32 UTC 13,589 bytes, 481 lines download raw bytes

1if (typeof jQuery === 'undefined') {
2    throw new Error('MegaMenu requires jQuery');
3}
4
5/**
6 * Mega-menu JavaScript.
7 */
8+function ($) {
9    'use strict';
10
11    /* MegaMenu module
12    --------------------------------------------------------------------------*/
13
14    //// Class definition
15
16    let MegaMenu = function (element, options) {
17        this.options = options;
18        this.$body = $(document.body);
19        this.$element = $(element);
20        this.$menu = this.$element.find('ul').first();
21        this.$submenu = this.$element.find('.submenu').first();
22        this.submenuHeight = { maxItems: null, noItems: 256};
23        this.$column1 = this.$submenu.find('.col-1').first();
24        this.$column2 = this.$submenu.find('.col-2').first();
25        this.$column3 = this.$submenu.find('.col-3').first();
26        //
27        this.$backdrop = null;
28        this.originalBodyPad = null;
29        this.scrollbarWidth = 0;
30        this.ignoreBackdropClick = false;
31        this.fixedContent = '#ccm-toolbar, .ccm-page > header';
32        //
33        this.status = false;
34        //
35        this.transition = this.canTransition() && Boolean(this.options.animation);
36    };
37
38    MegaMenu.VERSION = '1.0.0';
39
40    MegaMenu.TRANSITION_TYPE = 'easeInOutCubic';
41    MegaMenu.TRANSITION_DURATION = 150;
42    MegaMenu.BACKDROP_TRANSITION_DURATION = 150;
43
44    MegaMenu.DEFAULTS = {
45        animation: 'fade',
46        backdrop: true
47    };
48
49    MegaMenu.prototype.init = function () {
50        const numbersArr = [];
51        const $menu = this.$menu.find('ul');
52        const padding = 140;
53        const itemHeight = 57;
54
55        if ($menu.length) {
56            $menu.each(function (i, el) {
57                numbersArr.push($(el).find('> li').length);
58            });
59        }
60
61        const biggest = Math.max.apply(null, numbersArr);
62
63        this.submenuHeight.maxItems = (biggest * itemHeight) + padding;
64    };
65
66    //// Event prototypes
67
68    /**
69     * Transition detection
70     *
71     * @type {boolean}
72     */
73    MegaMenu.prototype.canTransition = function () {
74        var thisBody = document.body || document.documentElement;
75        var thisStyle = thisBody.style;
76        return (thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined);
77    };
78
79    /**
80     * Gets an elements' CSS transform values.
81     *
82     * @param element
83     * @param plane
84     *
85     * @returns {int}
86     */
87    MegaMenu.prototype.getTransform = function getValues(element, plane) {
88        plane = (typeof plane !== 'undefined') ? plane : 'x';
89        let matrix = $(element).css('transform').replace(/[^0-9\-.,]/g, '').split(',');
90        if (plane === 'x') {
91            return parseInt(matrix[12]) || parseInt(matrix[4]);
92        } else { // 'y'
93            return parseInt(matrix[13]) || parseInt(matrix[5]);
94        }
95    };
96
97    //// Backdrop methods
98
99    /**
100     * Create backdrop.
101     */
102    MegaMenu.prototype.createBackdrop = function () {
103
104        // variables
105        let $header = this.$element.closest('header');
106        let headerHeight = parseInt($header.height()) + this.getTransform($header, 'y');
107
108        // backdrop
109        this.$backdrop = $('<div class="menu-backdrop">')
110            .css('top', headerHeight + 'px')
111            .appendTo(this.$body);
112        if (this.transition) {
113            this.$backdrop.addClass(this.options.animation);
114            this.$backdrop[0].offsetWidth; // force reflow
115        }
116        this.$backdrop.addClass('in');
117
118        // set interface
119        this.checkScrollbar();
120        this.setScrollbar();
121
122        // body class
123        this.$body.addClass('modal-open');
124    };
125
126    /**
127     * Destroy backdrop.
128     */
129    MegaMenu.prototype.destroyBackdrop = function (self) {
130
131        //
132        if (self === undefined) self = this;
133
134        // remove submenu
135        self.$submenu.hide();
136
137        // body class
138        self.$body.removeClass('modal-open');
139
140        // reset interface
141        self.resetAdjustments();
142        self.resetScrollbar();
143
144        // remove backdrop
145        self.$backdrop && self.$backdrop.remove();
146        self.$backdrop = null;
147    };
148
149
150    //// Visibility
151
152    /**
153     * Toggle.
154     */
155    MegaMenu.prototype.toggle = function () {
156        if (this.status === true) {
157            this.hide();
158        } else {
159            this.show();
160        }
161    };
162
163    /**
164     * Show.
165     */
166    MegaMenu.prototype.show = function (target) {
167
168        // trigger "show" event
169        this.$element.trigger($.Event('show.dp.megamenu'));
170
171        // status
172        this.status = true;
173
174        // create & show backdrop
175        if (!this.$backdrop) {
176            this.createBackdrop();
177        }
178
179        // show submenu
180        // this.$submenu.show().css('display', 'inline-flex');
181
182        this.$submenu.css('display', 'inline-flex');
183
184        var self = this;
185
186        setTimeout(function() {
187            self.$submenu.addClass('in');
188        }, this.transition ? MegaMenu.BACKDROP_TRANSITION_DURATION : 0, this);
189
190        if (this.transition) {
191            this.$submenu.addClass(this.options.animation);
192        }
193        // this.$submenu.addClass('in');
194
195        // variables
196        let $target = $(target);
197        let $submenu = $target.children('ul').first();
198
199        // add active state
200        $target.parent().find('.active').removeClass('active');
201        $target.addClass('active');
202
203        // load columns based on level
204        switch ($target.data('level')) {
205            case 0:
206                this.loadColumn1($submenu.clone(true));
207                break;
208            case 1:
209                this.loadColumn2($submenu.clone(true));
210                break;
211            default:
212                break;
213        }
214        this.loadColumn3($(target).children('.data').first().clone());
215
216        // load columns based on level
217        if (($target.attr('data-level') > 0) || ($submenu.length)) {
218            this.$submenu.css('height', this.submenuHeight.maxItems);
219        } else {
220            this.$submenu.css('height', this.submenuHeight.noItems);
221        }
222
223        // trigger "shown" event
224        this.$element.trigger($.Event('shown.dp.megamenu'));
225    };
226
227    /**
228     * Hide.
229     */
230    MegaMenu.prototype.hide = function () {
231
232        // trigger "hide" event
233        this.$element.trigger($.Event('hide.dp.megamenu'));
234
235        // hide submenu
236        this.$submenu.removeClass('in');
237
238        // reset active state
239        this.$menu.find('.active').removeClass('active');
240        // this.$menu.find('.section, .current').addClass('active');
241
242        // hide & then destroy the backdrop (after timeout if animated)
243        if (this.$backdrop) {
244            this.$backdrop.removeClass('in');
245            setTimeout(this.destroyBackdrop, this.transition ? MegaMenu.BACKDROP_TRANSITION_DURATION : 0, this);
246        }
247
248        // status
249        this.status = false;
250
251        //clear columns, remove event listeners
252        this.clearMenu();
253
254        // trigger "hidden" event
255        this.$element.trigger($.Event('hidden.dp.megamenu'));
256    };
257
258    //// Clear menu
259    MegaMenu.prototype.clearMenu = function () {
260        setTimeout(function () {
261            this.hideColumn1();
262            this.hideColumn2();
263            this.$column1.empty();
264            this.$column2.empty();
265            this.$column1.add(this.$column2).off('transitionend webkitTransitionEnd oTransitionEnd');
266        }.bind(this), MegaMenu.TRANSITION_DURATION);
267    };
268
269    //// Column 1
270
271    /**
272     * Load column 1.
273     *
274     * @param $submenu
275     */
276    MegaMenu.prototype.loadColumn1 = function ($submenu) {
277        if ($submenu.length) {
278            let active = $submenu.find('.section, .current');
279            if (active.length) {
280                this.loadColumn2(active.first().children('ul').first().clone(true));
281            } else {
282                this.hideColumn2();
283            }
284            this.showColumn1($submenu);
285        } else {
286            this.hideColumn1();
287            this.hideColumn2();
288        }
289    };
290
291    /**
292     * Show column 1.
293     *
294     * @param $submenu
295     */
296    MegaMenu.prototype.showColumn1 = function ($submenu) {
297        this.$column1
298            .html($submenu)
299            .addClass('column-show');
300    };
301
302    /**
303     * Hide column 1.
304     */
305    MegaMenu.prototype.hideColumn1 = function () {
306        let self = this;
307        this.$column1.removeClass('column-show');
308
309        this.$column1.on('transitionend webkitTransitionEnd oTransitionEnd', function () {
310            if (!self.$column1.hasClass('column-show')) {
311                self.$column1.empty();
312            }
313        });
314    };
315
316    //// Column 2
317
318    /**
319     * Load column 2.
320     *
321     * @param $submenu
322     */
323    MegaMenu.prototype.loadColumn2 = function ($submenu) {
324        if ($submenu.length) {
325            this.showColumn2($submenu);
326        } else {
327            this.hideColumn2();
328        }
329    };
330
331    /**
332     * Show column 2.
333     *
334     * @param $submenu
335     */
336    MegaMenu.prototype.showColumn2 = function ($submenu) {
337        this.$column2
338            .html($submenu)
339            .addClass('column-show');
340    };
341
342    /**
343     * Hide column 2.
344     */
345    MegaMenu.prototype.hideColumn2 = function () {
346        let self = this;
347        this.$column2.removeClass('column-show');
348
349        this.$column2.on('transitionend webkitTransitionEnd oTransitionEnd', function () {
350            if (!self.$column2.hasClass('column-show')) {
351                self.$column2.empty();
352            }
353        });
354    };
355
356    //// Column 3
357
358    /**
359     * Load column 3.
360     *
361     * @param $overview
362     */
363    MegaMenu.prototype.loadColumn3 = function ($overview) {
364        if ($overview) {
365            this.$column3.html($overview.html());
366            let $img = this.$column3.find('img').first();
367            $img.attr('src', $img.data('src'));
368        } else {
369            this.$column3.html('<p class="alert alert-danger">Overview is corrupted!</p>');
370        }
371    };
372
373    //// Display helpers
374    /**
375     * Reset adjustments.
376     */
377    MegaMenu.prototype.resetAdjustments = function () {
378        this.$element.css({
379            paddingLeft: '',
380            paddingRight: ''
381        });
382    };
383
384    /**
385     * Check scrollbar.
386     */
387    MegaMenu.prototype.checkScrollbar = function () {
388        let fullWindowWidth = window.innerWidth;
389        if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
390            let documentElementRect = document.documentElement.getBoundingClientRect();
391            fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left);
392        }
393        this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth;
394        this.scrollbarWidth = this.measureScrollbar();
395    };
396
397    /**
398     * Set scrollbar.
399     */
400    MegaMenu.prototype.setScrollbar = function () {
401        let bodyPad = parseInt((this.$body.css('padding-right') || 0), 10);
402        this.originalBodyPad = document.body.style.paddingRight || '';
403        let scrollbarWidth = this.scrollbarWidth;
404        if (this.bodyIsOverflowing) {
405            this.$body.css('padding-right', bodyPad + scrollbarWidth);
406            $(this.fixedContent).each(function (index, element) {
407                let actualPadding = element.style.paddingRight;
408                let calculatedPadding = $(element).css('padding-right');
409                $(element)
410                    .data('padding-right', actualPadding)
411                    .css('padding-right', parseFloat(calculatedPadding) + scrollbarWidth + 'px');
412            });
413        }
414    };
415
416    /**
417     * Reset scrollbar.
418     */
419    MegaMenu.prototype.resetScrollbar = function () {
420        this.$body.css('padding-right', this.originalBodyPad);
421        $(this.fixedContent).each(function (index, element) {
422            let padding = $(element).data('padding-right');
423            $(element).removeData('padding-right');
424            element.style.paddingRight = padding ? padding : '';
425        });
426    };
427
428    /**
429     * Measure scrollbar.
430     *
431     * @returns {number}
432     */
433    MegaMenu.prototype.measureScrollbar = function () {
434        let scrollDiv = document.createElement('div');
435        scrollDiv.className = 'modal-scrollbar-measure';
436        this.$body.append(scrollDiv);
437        let scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
438        this.$body[0].removeChild(scrollDiv);
439        return scrollbarWidth;
440    };
441
442
443    /* Plugin
444    --------------------------------------------------------------------------*/
445
446    /**
447     * Dunno..?
448     *
449     * @param option
450     * @param _relatedTarget
451     *
452     * @returns {*}
453     *
454     * @constructor
455     */
456    function Plugin(option, _relatedTarget) {
457        return this.each(function () {
458
459            let $this = $(this);
460            let data = $this.data('dp.megamenu');
461            let options = $.extend({}, MegaMenu.DEFAULTS, $this.data(), typeof option == 'object' && option);
462
463            if (!data) {
464                $this.data('dp.megamenu', (data = new MegaMenu(this, options)));
465            }
466            if (typeof option == 'string') {
467                data[option](_relatedTarget);
468            } else if (options.show) {
469                data.show(_relatedTarget);
470            } else if (options.init) {
471                data.init();
472            }
473        });
474    }
475
476    //// Register plugin
477
478    $.fn.megaMenu = Plugin;
479    $.fn.megaMenu.Constructor = MegaMenu;
480
481}(jQuery);

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.