Improve performance for load menu

Forum rules
Please note that response time for technical support is within 3-5 business days.
rider
Posts: 497
Joined: 05 Mar 2010, 13:17

10 Dec 2020, 21:41

Unfortunatelly not. Then the state is not stored and the menu will be everytime not open.

Here my code:

Code: Select all

/** 
 * PrimeFaces Morpheus Layout
 */
PrimeFaces.widget.Morpheus = PrimeFaces.widget.BaseWidget.extend({
    
    init: function(cfg) {
        this._super(cfg);
        this.wrapper = $(document.body).children('.layout-wrapper');
        this.sidebar = this.wrapper.children('.layout-sidebar');
        this.tabMenu = this.jq;
        this.tabMenuNav = this.tabMenu.children('.layout-tabmenu-nav');
        this.tabMenuNavItems = this.tabMenuNav.children('li');
        this.tabMenuNavLinks = this.tabMenuNav.find('a');
        this.topbar = this.wrapper.children('.topbar');
        this.topbarMenu = this.topbar.children('.topbar-menu');
        this.topbarItems = this.topbarMenu.children('li');
        this.topbarLinks = this.topbarItems.children('a');
        this.menuButton = $('#menu-button');
        this.topbarMenuButton = $('#topbar-menu-button');        
        this.menuActive = false;
        this.sidebarClick = false;
        this.topbarLinkClick = false;
        this.topbarMenuClick = false;

        this._bindEvents();
    },
    
    _bindEvents: function() {
        var $this = this;
        
        this.menuButton.off('click.menu').on('click.menu', function(e) {
            if($this.wrapper.hasClass('layout-wrapper-menu-active')) {
                $this.hideMenu();
            }
            else {
                var activeItem = $this.tabMenuNavItems.filter('.active-item');
                
                activeItem = activeItem.length > 0 ? activeItem : $this.tabMenuNavItems.eq(0);
                $this.openMenu(activeItem.children('a'));
            }
            
            $this.sidebarClick = true;

            e.preventDefault();
        });
        
        this.tabMenuNavLinks.off('click.menu').on('click.menu', function(e) {
            var link = $(this);
            link.parent().siblings('.active-item').removeClass('active-item');
            
            $this.openMenu(link);

            $(this).children('.layout-tabmenu-tooltip').hide();
            
            e.preventDefault();
        });
        
        $(document.body).off('click.layoutBody').on('click.layoutBody', function() {
            if(!$this.topbarMenuClick && !$this.topbarLinkClick) {
                $this.topbarItems.filter('.active-topmenuitem').removeClass('active-topmenuitem');
                $this.topbarMenu.removeClass('topbar-menu-visible');
            }
            
            if(!$this.sidebarClick && ($this.isOverlayMenu() || !$this.isDesktop())) {
                $this.wrapper.removeClass('layout-wrapper-menu-active');
            }
            
            $this.topbarLinkClick = false;
            $this.topbarMenuClick = false;
            $this.sidebarClick = false;
        });
    },
    
    openMenu: function(link, restore) {
        this.sidebar.css('overflow','hidden');
        var parent = link.parent();
        parent.addClass('active-item');
        
        this.wrapper.addClass('layout-wrapper-menu-active');
        if(restore) {
            this.wrapper.addClass('layout-wrapper-menu-active-restore');
        }
        
        this.tabMenu.children('.layout-tabmenu-contents').children('.layout-tabmenu-content').removeClass('layout-tabmenu-content-active').
                eq(parent.index()).addClass('layout-tabmenu-content-active');
    },
    
    hideMenu: function() {
        this.sidebar.css('overflow','visible');
        this.wrapper.removeClass('layout-wrapper-menu-active layout-wrapper-menu-active-restore');
    },

    fireTabChangeEvent: function(tab) {
        if (this.cfg.behaviors && this.cfg.behaviors['tabChange']) {
            var ext = {
                params: [
                    {name: this.id + '_newTab', value: tab.attr('id')},
                    {name: this.id + '_tabindex', value: tab.index()}
                ]
            };

            this.cfg.behaviors['tabChange'].call(this, ext);
        }
    },
    
    isTablet: function() {
        var width = window.innerWidth;
        return width <= 1024 && width > 640;
    },

    isDesktop: function() {
        return window.innerWidth > 1024;
    },

    isMobile: function() {
        return window.innerWidth <= 640;
    },
    
    isOverlayMenu: function() {
        return this.wrapper.hasClass('layout-overlay-menu');
    },
    
    isIOS: function(e) {
        return ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) || (navigator.userAgent.match(/iPad/i)));
    },
    
    saveTabState: function(id) {
        $.cookie('morpheus_expandedtab', id, {path: '/'});
    },
    
    clearTabState: function() {
        $.removeCookie('morpheus_expandedtab', {path: '/'});
    },
        
    restoreTabState: function() {
        this.expandedTab = $.cookie('morpheus_expandedtab');
        if (this.expandedTab) {
            var tab = $("#" + this.expandedTab.replace(/:/g, "\\:"));
            this.openMenu(tab.children('a'), true);
        }
        else if(this.cfg.activeIndex) {
            var active = this.tabMenuNavItems.eq(parseInt(this.cfg.activeIndex));
            this.openMenu(active.children('a'), true);
        }
    }
    
});

/** 
 * PrimeFaces MorpheusMenu Component
 */
PrimeFaces.widget.MorpheusMenu = PrimeFaces.widget.BaseWidget.extend({
    
    init: function(cfg) {
        this._super(cfg);
        this.menulinks = this.jq.find('a');
        this.expandedMenuitems = this.expandedMenuitems || [];     
        this.menuActive = false;
        this.topbarLinkClick = false;
        this.topbarMenuClick = false;
        this.nano = this.jq.closest('.nano');

        this._bindEvents();
        this.restoreMenuState();
    },
    
    _bindEvents: function() {
        var $this = this;
        
        this.menulinks.off('click').on('click', function(e) {
            var link = $(this),
            item = link.parent(),
            submenu = item.children('ul');
                                     
            if(item.hasClass('active-menuitem')) {
                if(submenu.length) {
                    $this.removeMenuitem(item.attr('id'));
                    item.removeClass('active-menuitem');
                    submenu.slideUp();
                }
            }
            else {
                $this.addMenuitem(item.attr('id'));
                $this.deactivateItems(item.siblings(), true);
                $this.activate(item);
            }
            
            setTimeout(function() {
                $this.nano.nanoScroller();
            }, 500);
                                    
            if(submenu.length) {
                e.preventDefault();
            }
        });
    },
         
    activate: function(item) {
        var submenu = item.children('ul');
        item.addClass('active-menuitem');

        if(submenu.length) {
            submenu.slideDown();
        }
    },
    
    deactivate: function(item) {
        var submenu = item.children('ul');
        item.removeClass('active-menuitem');
        
        if(submenu.length) {
            submenu.hide();
        }
    },
        
    deactivateItems: function(items, animate) {
        var $this = this;
        
        for(var i = 0; i < items.length; i++) {
            var item = items.eq(i),
            submenu = item.children('ul');
            
            if(submenu.length) {
                if(item.hasClass('active-menuitem')) {
                    var activeSubItems = item.find('.active-menuitem');
                    item.removeClass('active-menuitem');
                    item.find('.ink').remove();
                    
                    if(animate) {
                        submenu.slideUp('normal', function() {
                            $(this).parent().find('.active-menuitem').each(function() {
                                $this.deactivate($(this));
                            });
                        });
                    }
                    else {
                        submenu.hide();
                        item.find('.active-menuitem').each(function() {
                            $this.deactivate($(this));
                        });
                    }
                    
                    $this.removeMenuitem(item.attr('id'));
                    activeSubItems.each(function() {
                        $this.removeMenuitem($(this).attr('id'));
                    });
                }
                else {
                    item.find('.active-menuitem').each(function() {
                        var subItem = $(this);
                        $this.deactivate(subItem);
                        $this.removeMenuitem(subItem.attr('id'));
                    });
                }
            }
            else if(item.hasClass('active-menuitem')) {
                $this.deactivate(item);
                $this.removeMenuitem(item.attr('id'));
            }
        }
    },
    
    clearActiveItems: function() {
        var activeItems = this.jq.find('li.active-menuitem'),
        subContainers = activeItems.children('ul');

        activeItems.removeClass('active-menuitem');
        subContainers.hide();
    },
            
    removeMenuitem: function (id) {
        this.expandedMenuitems = $.grep(this.expandedMenuitems, function (value) {
            return value !== id;
        });
        this.saveMenuState();
    },
    
    addMenuitem: function (id) {
        if ($.inArray(id, this.expandedMenuitems) === -1) {
            this.expandedMenuitems.push(id);
        }
        this.saveMenuState();
    },
    
    saveMenuState: function() {
        $.cookie('morpheus_expandeditems', this.expandedMenuitems.join(','), {path: '/'});
    },
    
    clearMenuState: function() {
        $.removeCookie('morpheus_expandeditems', {path: '/'});
    },
        
    restoreMenuState: function() {
        var menucookie = $.cookie('morpheus_expandeditems');
        if (menucookie) {
            this.expandedMenuitems = menucookie.split(',');
            for (var i = 0; i < this.expandedMenuitems.length; i++) {
                var id = this.expandedMenuitems[i];
                if (id) {
                    var menuitem = $("#" + this.expandedMenuitems[i].replace(/:/g, "\\:"));
                    menuitem.addClass('active-menuitem');
                    
                    var submenu = menuitem.children('ul');
                    if(submenu.length) {
                        submenu.show();
                    }
                }
            }
        }
        
        var inlineProfileCookie = $.cookie('morpheus_inlineprofile_expanded');
        if (inlineProfileCookie) {
            this.profileMenu.show().prev('.profile').addClass('profile-expanded');
        }
    }
    
});

$(document).ready(function() {
    var nano = $('.nano'),
    wrapper = $(document.body).find('.layout-wrapper');
    
    if(nano.length && wrapper.length) {
        nano.nanoScroller({flash:true, isRTL: wrapper.hasClass('layout-rtl')});
    }
});

/*!
 * jQuery Cookie Plugin v1.4.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2006, 2014 Klaus Hartl
 * Released under the MIT license
 */
(function (factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD (Register as an anonymous module)
		define(['jquery'], factory);
	} else if (typeof exports === 'object') {
		// Node/CommonJS
		module.exports = factory(require('jquery'));
	} else {
		// Browser globals
		factory(jQuery);
	}
}(function ($) {

	var pluses = /\+/g;

	function encode(s) {
		return config.raw ? s : encodeURIComponent(s);
	}

	function decode(s) {
		return config.raw ? s : decodeURIComponent(s);
	}

	function stringifyCookieValue(value) {
		return encode(config.json ? JSON.stringify(value) : String(value));
	}

	function parseCookieValue(s) {
		if (s.indexOf('"') === 0) {
			// This is a quoted cookie as according to RFC2068, unescape...
			s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
		}

		try {
			// Replace server-side written pluses with spaces.
			// If we can't decode the cookie, ignore it, it's unusable.
			// If we can't parse the cookie, ignore it, it's unusable.
			s = decodeURIComponent(s.replace(pluses, ' '));
			return config.json ? JSON.parse(s) : s;
		} catch(e) {}
	}

	function read(s, converter) {
		var value = config.raw ? s : parseCookieValue(s);
		return $.isFunction(converter) ? converter(value) : value;
	}

	var config = $.cookie = function (key, value, options) {

		// Write

		if (arguments.length > 1 && !$.isFunction(value)) {
			options = $.extend({}, config.defaults, options);

			if (typeof options.expires === 'number') {
				var days = options.expires, t = options.expires = LocalDateTime.now();
				t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
			}

			return (document.cookie = [
				encode(key), '=', stringifyCookieValue(value),
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
				options.path    ? '; path=' + options.path : '',
				options.domain  ? '; domain=' + options.domain : '',
				options.secure  ? '; secure' : ''
			].join(''));
		}

		// Read

		var result = key ? undefined : {},
			// To prevent the for loop in the first place assign an empty array
			// in case there are no cookies at all. Also prevents odd result when
			// calling $.cookie().
			cookies = document.cookie ? document.cookie.split('; ') : [],
			i = 0,
			l = cookies.length;

		for (; i < l; i++) {
			var parts = cookies[i].split('='),
				name = decode(parts.shift()),
				cookie = parts.join('=');

			if (key === name) {
				// If second argument (value) is a function it's a converter...
				result = read(cookie, value);
				break;
			}

			// Prevent storing a cookie that we couldn't decode.
			if (!key && (cookie = read(cookie)) !== undefined) {
				result[name] = cookie;
			}
		}

		return result;
	};

	config.defaults = {};

	$.removeCookie = function (key, options) {
		// Must not alter options, thus extending a fresh object...
		$.cookie(key, '', $.extend({}, options, { expires: -1 }));
		return !$.cookie(key);
	};

}));

/* Issue #924 is fixed for 5.3+ and 6.0. (compatibility with 5.3) */
if(window['PrimeFaces'] && window['PrimeFaces'].widget.Dialog) {
    PrimeFaces.widget.Dialog = PrimeFaces.widget.Dialog.extend({
        
        enableModality: function() {
            this._super();
            $(document.body).children(this.jqId + '_modal').addClass('ui-dialog-mask');
        },
        
        syncWindowResize: function() {}
    });
}
Primefaces 12.0, WildFly 21

mert.sincan
Posts: 5281
Joined: 29 Jun 2013, 12:38

10 Dec 2020, 21:57

Thanks a lot for the update. This was just to test whether its performance has improved or not. Is there an improvement on your side only in terms of performance? If it improves performance, I can add other deficiencies such as menu state etc. to this code. I wanted to test this with such code blocks, as I cannot reproduce the problem.

rider
Posts: 497
Joined: 05 Mar 2010, 13:17

10 Dec 2020, 22:05

hm, to be honest... I cannot say 100% if it improves or not.... The main issue is the menu which take a few milliseconds / seconds to open (if state is still open). This is the main issue.
It can reproduce also showcase - I have here the same issue...
Here it´s maybe a little bit faster, due to the fact the xhtml has not so many components
Primefaces 12.0, WildFly 21

mert.sincan
Posts: 5281
Joined: 29 Jun 2013, 12:38

12 Dec 2020, 01:59

Thanks a lot for the update! Could you please check restoreTabState method with debugger; in the original layout.js? Also, is this issue the same in all browsers?

Best Regards,

vished
Posts: 479
Joined: 02 Feb 2014, 17:38

14 Dec 2020, 08:53

For me the same. I can replicate this also in the showcase / template preview.
With more components in the view it´s much more available instead of less components in the view.

E.g:
https://www.primefaces.org/morpheus/charts.xhtml

I´m using Google Chrome Version 87.0.

@mert.sincan:
I guess you can replicate this also on your side?

I´m not sure how I can do this with the dubugger.
PF 8.0

rider
Posts: 497
Joined: 05 Mar 2010, 13:17

21 Dec 2020, 11:18

@mert.sincan:
Do you have an update here?
Primefaces 12.0, WildFly 21

mert.sincan
Posts: 5281
Joined: 29 Jun 2013, 12:38

21 Dec 2020, 17:41

So sorry :// I still couldn't replicate it. Could you please attach a video link related to it? I'll ask other team members to do tests on this.

Best Regards,

rider
Posts: 497
Joined: 05 Mar 2010, 13:17

21 Dec 2020, 17:57

Sure...
Please see below. As you can see the menu is not open directly., there is a delay.
https://streamable.com/t7vpu9

If you have more components on the page, then the menu is slower opening.
It´s really annoying, because sometime I click on a link and it this time, the menu opened and it will click a menu item from the menu and I will redirect to another page.
Primefaces 12.0, WildFly 21

mert.sincan
Posts: 5281
Joined: 29 Jun 2013, 12:38

23 Dec 2020, 01:14

Hi,
Thanks a lot for the video. If a tab is active, I think we can send it to the server with an ajax request. Unfortunately, doing this state storage on the client can cause such flickers. What do you think about it?

Thanks a lot for the feedbacks,
Best Regards,

rider
Posts: 497
Joined: 05 Mar 2010, 13:17

23 Dec 2020, 07:50

If this is the only way to solve this issue, I´m fine.... but this will also cost performance because you need a request to the server, right?
Primefaces 12.0, WildFly 21

Post Reply

Return to “Morpheus - PrimeFaces”

  • Information
  • Who is online

    Users browsing this forum: No registered users and 1 guest