Ben-at-work Posted April 7, 2016 Posted April 7, 2016 Hi, really hope someone here can help or offer some guidance. I'm using dropotron to power the drop down menus (code will be on post below) on our new site which works fine. The problem I have is that's not very accessible for keyboard only users. Curriculum Art GCSE Media Studies Humanities & Social Sciences Information Information Home Admissions Behaviour For Learning For example, if a user hovers the mouse over Curriculum the submenu appears. On a mobile device touching Curriculum displays the sub menu. Tab-Enter will display the menu, but tabbing again will move to the next option at the top level, it will move to Information rather than Art on the submenu. Looking at Information which has a specified URL we get the following. Mouse hover displays the sub menu, touch and tab-enter take the user to the URL specified. What I'm desperate to achieve is the following behaviour: When a menu is hovered by a mouse, show the sub-menu. When a menu is clicked with a mouse, open the link (or do nothing, or close the menu - not too bothered!). The first time a menu is clicked by touch, show the sub menu. The second time a menu is clicked by touch, open the link (or do nothing, or close the menu - not too bothered!). Hitting Enter opens the link I'm in no way an expert (barely a beginner!) with JS so anything would be a help!
Ben-at-work Posted April 7, 2016 Author Posted April 7, 2016 /* jquery.dropotron.js v1.4.3 | (c) n33 | n33.co | MIT licensed */ (function($) { // Disables selection. $.fn.disableSelection_dropotron = function() { return $(this).css('user-select', 'none').css('-khtml-user-select', 'none').css('-moz-user-select', 'none').css('-o-user-select', 'none').css('-webkit-user-select', 'none'); } // Dropotron prototype method. $.fn.dropotron = function(options) { if (this.length == 0) return $(this); if (this.length > 1) for (var i=0; i < this.length; i++) $(this[i]).dropotron(options); return $.dropotron($.extend({ selectorParent: $(this) }, options)); } // Dropotron method. $.dropotron = function(options) { // Settings. var settings = $.extend({ // Parent jQuery object. selectorParent: null, // Base Z-Index. baseZIndex: 1000, // Menu class (assigned to every </pre><ul>). menuClass: 'dropotron', // Expansion mode ("hover" or "click"). expandMode: 'hover', // Hover delay (in ms). hoverDelay: 150, // Hide delay (in ms; 0 disables). hideDelay: 250, // Opener class. openerClass: 'opener', // Active opener class. openerActiveClass: 'active', // Submenu class prefix. submenuClassPrefix: 'level-', // Menu mode ("instant", "fade", "slide", "zoom"). mode: 'fade', // Menu speed ("fast", "slow", or ms). speed: 'fast', // Easing mode ("swing", "linear"). easing: 'swing', // Alignment ("left", "center", "right"). alignment: 'left', // Submenu offset X. offsetX: 0, // Submenu offset Y. offsetY: 0, // Global offset Y. globalOffsetY: 0, // IE Offset X. IEOffsetX: 0, // IE Offset Y. IEOffsetY: 0, // If true and mode = "fade", prevents top-level opener fade. noOpenerFade: true, // Detach second level menus (prevents parent style bleed). detach: true, // If true and detach = true, leave original menu intact. cloneOnDetach: true }, options); // Vars. var $top = settings.selectorParent, $menus = $top.find('ul'), $body = $('body'), $bodyhtml = $('body,html'), $window = $(window); var isLocked = false, hoverTimeoutId = null, hideTimeoutId = null; // Main. // Top. $top .on('doCollapseAll', function() { $menus.trigger('doCollapse'); }); // Top level menus. $menus.each(function() { var $menu = $(this), $opener = $menu.parent(); // If a hideDelay is set, set up the event. if (settings.hideDelay > 0) $menu.add($opener) .on('mouseleave', function(e) { window.clearTimeout(hideTimeoutId); hideTimeoutId = window.setTimeout(function() { $menu.trigger('doCollapse'); }, settings.hideDelay); }); // Menu. $menu .disableSelection_dropotron() .hide() .addClass(settings.menuClass) .css('position', 'absolute') .on('mouseenter', function(e) { window.clearTimeout(hideTimeoutId); }) .on('doExpand', function() { // Already visible? Bail out. if ($menu.is(':visible')) return false; // Reset our "hide" timeout. window.clearTimeout(hideTimeoutId); // Collapse everything but this menu. $menus.each(function() { var $this = $(this); if (!$.contains($this.get(0), $opener.get(0))) $this.trigger('doCollapse'); }); // Vars. var oo = $opener.offset(), op = $opener.position(), opp = $opener.parent().position(), ow = $opener.outerWidth(), mw = $menu.outerWidth(), isTL = ($menu.css('z-index') == settings.baseZIndex); var x, c, left, top; // If this is a top level menu ... if (isTL) { // If detach is enabled, use .position() if (!settings.detach) x = op; // Otherwise, use .offset() else x = oo; top = x.top + $opener.outerHeight() + settings.globalOffsetY; c = settings.alignment; // Remove alignment classes. $menu .removeClass('left') .removeClass('right') .removeClass('center'); // Figure out alignment and position. switch (settings.alignment) { case 'right': left = x.left - mw + ow; if (left < 0) { left = x.left; c = 'left'; } break; case 'center': left = x.left - Math.floor((mw - ow) / 2); if (left < 0) { left = x.left; c = 'left'; } else if (left + mw > $window.width()) { left = x.left - mw + ow; c = 'right'; } break; case 'left': default: left = x.left; if (left + mw > $window.width()) { left = x.left - mw + ow; c = 'right'; } break; } // Add alignment class. $menu.addClass(c); } // Otherwise, we're dealing with a submenu. else { // If the opener position isn't static ... if ($opener.css('position') == 'relative' || $opener.css('position') == 'absolute') { top = settings.offsetY; left = (-1 * op.left); } else { top = op.top + settings.offsetY; left = 0; } // Figure out position (based on alignment). switch (settings.alignment) { case 'right': left += (-1 * $opener.parent().outerWidth()) + settings.offsetX; break; case 'center': case 'left': default: left += $opener.parent().outerWidth() + settings.offsetX; break; } } // Legacy IE? Apply IE-specific offsets. if (navigator.userAgent.match(/MSIE ([0-9]+)\./) && RegExp.$1 < 8) { left += settings.IEOffsetX; top += settings.IEOffsetY; } // Position and show the menu. $menu .css('left', left + 'px') .css('top', top + 'px') .css('opacity', '0.01') .show(); // Kludge! var tmp = false; // Non-static position fix. if ($opener.css('position') == 'relative' || $opener.css('position') == 'absolute') left = (-1 * op.left); else left = 0; if ($menu.offset().left < 0) { left += $opener.parent().outerWidth() - settings.offsetX; tmp = true; } else if ($menu.offset().left + mw > $window.width()) { left += (-1 * $opener.parent().outerWidth()) - settings.offsetX; tmp = true; } if (tmp) $menu .css('left', left + 'px'); $menu .hide() .css('opacity', '1'); // Figure out animation mode. switch (settings.mode) { case 'zoom': isLocked = true; $opener.addClass(settings.openerActiveClass); $menu.animate({ width: 'toggle', height: 'toggle' }, settings.speed, settings.easing, function() { isLocked = false; }); break; case 'slide': isLocked = true; $opener.addClass(settings.openerActiveClass); $menu.animate({ height: 'toggle' }, settings.speed, settings.easing, function() { isLocked = false; }); break; case 'fade': isLocked = true; if (isTL && !settings.noOpenerFade) { var tmp; if (settings.speed == 'slow') tmp = 80; else if (settings.speed == 'fast') tmp = 40; else tmp = Math.floor(settings.speed / 2); $opener.fadeTo(tmp, 0.01, function() { $opener.addClass(settings.openerActiveClass); $opener.fadeTo(settings.speed, 1); $menu.fadeIn(settings.speed, function() { isLocked = false; }); }); } else { $opener.addClass(settings.openerActiveClass); $opener.fadeTo(settings.speed, 1); $menu.fadeIn(settings.speed, function() { isLocked = false; }); } break; case 'instant': default: $opener.addClass(settings.openerActiveClass); $menu.show(); break; } return false; }) .on('doCollapse', function() { // Not visible? Bail out. if (!$menu.is(':visible')) return false; // Collapse the menu. $menu.hide(); $opener.removeClass(settings.openerActiveClass); $menu.find('.' + settings.openerActiveClass).removeClass(settings.openerActiveClass); $menu.find('ul').hide(); return false; }) .on('doToggle', function(e) { if ($menu.is(':visible')) $menu.trigger('doCollapse'); else $menu.trigger('doExpand'); return false; }); // Menu's opener. $opener .disableSelection_dropotron() .addClass('opener') .css('cursor', 'pointer') .on('click touchend', function(e) { // Locked? Bail. if (isLocked) return; // Toggle menu. e.preventDefault(); e.stopPropagation(); $menu.trigger('doToggle'); }); // If expandMode is "hover", set up the event. if (settings.expandMode == 'hover') $opener.hover(function(e) { if (isLocked) return; hoverTimeoutId = window.setTimeout(function() { $menu.trigger('doExpand'); }, settings.hoverDelay); }, function (e) { window.clearTimeout(hoverTimeoutId); }); }); // All links. $menus.find('a') .css('display', 'block') .on('click touchend', function(e) { // Locked? Bail. if (isLocked) return; // No href? Prevent default. if ($(this).attr('href').length < 1) e.preventDefault(); }); // All list items. $top.find('li') .css('white-space', 'nowrap') .each(function() { var $this = $(this), $a = $this.children('a'), $ul = $this.children('ul'), href = $a.attr('href'); // If href is blank ("") or a hash (#), prevent the link from doing anything. $a.on('click touchend', function(e) { if (href.length == 0 || href == '#') e.preventDefault(); else e.stopPropagation(); }); // If there is a link but no unordered list ... if ($a.length > 0 && $ul.length == 0) $this.on('click touchend', function(e) { if (isLocked) return; $top.trigger('doCollapseAll'); e.stopPropagation(); }); }); // Top level list items. $top.children('li').each(function() { var $opener = $(this), $menu = $opener.children('ul'), c; if ($menu.length > 0) { // If we're using detach ... if (settings.detach) { // If we're cloning on detach ... if (settings.cloneOnDetach) { // Make a copy of the menu. c = $menu.clone(); // Leave it behind c .attr('class', '') .hide() .appendTo($menu.parent()); } // Detach the menu and move it to the end of the element. $menu .detach() .appendTo($body); } // Step through menus, assigning z-indexes and submenu class prefixes as necessary. for(var z = settings.baseZIndex, i = 1, y = $menu; y.length > 0; i++) { y.css('z-index', z++); if (settings.submenuClassPrefix) y.addClass(settings.submenuClassPrefix + (z - 1 - settings.baseZIndex)); y = y.find('> li > ul'); } } }); // Window. $window .on('', function() { $top.trigger('doCollapseAll'); }) .on('keypress', function(e) { // Collapse all menus if the user hits escape. if (!isLocked && e.keyCode == 27) { e.preventDefault(); $top.trigger('doCollapseAll'); } }); // Body. $bodyhtml .on('click touchend', function() { if (!isLocked) $top.trigger('doCollapseAll'); }); }; })(jQuery)
SimonHooker Posted April 8, 2016 Posted April 8, 2016 Just looking at the html I can see a slight issue in that you have gone nav -> list item -> unordered list -> list item etc - but this might be due to copying out the html. Your example seems a little incomplete though. Taking your code, and the example at https://github.com/n33/jquery.dropotron, can come up with this though. The interesting event here is 'doExpand' because your main issue seems to be the fact that opening a menu with tab+enter does not then put the focus on the child list. So changing the list HTML to the following ( sorting out the structure + adding in tabindex to submenu ) Curriculum Art GCSE Media Studies Humanities & Social Sciences Information Information Home Admissions Behaviour For Learning And changing the line [color=#333333]$menu.addClass(c);[/color] to $opener.find( 'a' ).blur(); [color=#333333]// Take focus off the opening link [/color] $menu .addClass(c) .find( 'li:first-of-type a' ) .focus(); // After adding classes, give focus to the first anchor tag in the submenu This makes it so that after opening a submenu with tabs, further tabs happen first of all in that submenu. I've tried the above in Chrome and IE11. Full gist available here : https://gist.github.com/SimonHooker/c797a95d965cec891e4df72028b1da2b 1
Ben-at-work Posted April 8, 2016 Author Posted April 8, 2016 Hi Simon, Thank you so much for this, I really appreciate the time you took - you've got me out of a hole! Oddly it doesn't work in Firefox on Windows (only), but I'll dig into that later. If someone has JS disabled, the menu won't appear is there anything simple I could to counteract this? Thanks again.
SimonHooker Posted April 8, 2016 Posted April 8, 2016 Hi there, It's odd that it doesn't work on Firefox as that should use the same rendering engine as Chrome. It may be the li:first-of-type selector that it's not agreeing with. You could also try $menu .addClass(c) .find( 'li' ) .first() .find( 'a' ) .focus(); // After adding classes, give focus to the first anchor tag in the submenu Regarding handling users without Javascript enabled, that is more difficult. The only real fallback you can do in for that would involve tags to render static sub-menus out, which is not a graceful solution. Nowadays I make no concessions to users that don't have javascript enabled to be honest, as the fraction that fit that description is so negligible. Worth checking any accessibility requirements that you have though. 1
Ben-at-work Posted April 11, 2016 Author Posted April 11, 2016 Still doing on Windows Firefox, fine everywhere else. Odd, but shouldn't be a deal breaker! Thanks for all you help, it really is appreciated.
Ben-at-work Posted April 21, 2016 Author Posted April 21, 2016 Hi Simon, Sorry to drag this back up, it appears this doesn't work on the latest version of Chrome (Windows or OS X) or Firefox (both OSs). Nearly had a heart attack when we started to deploy the updated versions this morning. Is there anything you can think of that I could do? Thanks, Ben
SimonHooker Posted April 21, 2016 Posted April 21, 2016 Hi Ben Considering that I primarily use Chrome + so of course tested the above in Chrome originally, this is a bit concerning. If you could supply updated source code, what you actually have in your source that is, then I could give it another quick look in case there's something that's been missed somewhere along the line.
Ben-at-work Posted April 21, 2016 Author Posted April 21, 2016 Yeah, it was working absolutely perfectly until this morning until we noticed it stopped working on machines running version 50.0.2661.86. Nothing has changed in the code (I haven't touched it anyway) but have pasted below anyway. I can't thank you enough for looking at this. The JS will be in a separate post due to the length. Curriculum Art GCSE Media Studies Humanities & Social Sciences Information Information Home Admissions Behaviour For Learning
SimonHooker Posted April 21, 2016 Posted April 21, 2016 Looks like I'm still on 49, will update and get back to you once I get a gap to look at this in, hopefully not too long. Sure hope Chrome 50 doesn't break the JS I've just been working on!
Ben-at-work Posted April 21, 2016 Author Posted April 21, 2016 (function($) { // Disables selection. $.fn.disableSelection_dropotron = function() { return $(this).css('user-select', 'none').css('-khtml-user-select', 'none').css('-moz-user-select', 'none').css('-o-user-select', 'none').css('-webkit-user-select', 'none'); } // Dropotron prototype method. $.fn.dropotron = function(options) { if (this.length == 0) return $(this); if (this.length > 1) for (var i=0; i < this.length; i++) $(this[i]).dropotron(options); return $.dropotron($.extend({ selectorParent: $(this) }, options)); } // Dropotron method. $.dropotron = function(options) { // Settings. var settings = $.extend({ // Parent jQuery object. selectorParent: null, // Base Z-Index. baseZIndex: 1000, // Menu class (assigned to every </pre><ul>). menuClass: 'dropotron', // Expansion mode ("hover" or "click"). expandMode: 'hover', // Hover delay (in ms). hoverDelay: 150, // Hide delay (in ms; 0 disables). hideDelay: 250, // Opener class. openerClass: 'opener', // Active opener class. openerActiveClass: 'active', // Submenu class prefix. submenuClassPrefix: 'level-', // Menu mode ("instant", "fade", "slide", "zoom"). mode: 'fade', // Menu speed ("fast", "slow", or ms). speed: 'fast', // Easing mode ("swing", "linear"). easing: 'swing', // Alignment ("left", "center", "right"). alignment: 'left', // Submenu offset X. offsetX: 0, // Submenu offset Y. offsetY: 0, // Global offset Y. globalOffsetY: 0, // IE Offset X. IEOffsetX: 0, // IE Offset Y. IEOffsetY: 0, // If true and mode = "fade", prevents top-level opener fade. noOpenerFade: true, // Detach second level menus (prevents parent style bleed). detach: true, // If true and detach = true, leave original menu intact. cloneOnDetach: true }, options); // Vars. var $top = settings.selectorParent, $menus = $top.find('ul'), $body = $('body'), $bodyhtml = $('body,html'), $window = $(window); var isLocked = false, hoverTimeoutId = null, hideTimeoutId = null; // Main. // Top. $top .on('doCollapseAll', function() { $menus.trigger('doCollapse'); }); // Top level menus. $menus.each(function() { var $menu = $(this), $opener = $menu.parent(); // If a hideDelay is set, set up the event. if (settings.hideDelay > 0) $menu.add($opener) .on('mouseleave', function(e) { window.clearTimeout(hideTimeoutId); hideTimeoutId = window.setTimeout(function() { $menu.trigger('doCollapse'); }, settings.hideDelay); }); // Menu. $menu .disableSelection_dropotron() .hide() .addClass(settings.menuClass) .css('position', 'absolute') .on('mouseenter', function(e) { window.clearTimeout(hideTimeoutId); }) .on('doExpand', function() { // Already visible? Bail out. if ($menu.is(':visible')) return false; // Reset our "hide" timeout. window.clearTimeout(hideTimeoutId); // Collapse everything but this menu. $menus.each(function() { var $this = $(this); if (!$.contains($this.get(0), $opener.get(0))) $this.trigger('doCollapse'); }); // Vars. var oo = $opener.offset(), op = $opener.position(), opp = $opener.parent().position(), ow = $opener.outerWidth(), mw = $menu.outerWidth(), isTL = ($menu.css('z-index') == settings.baseZIndex); var x, c, left, top; // If this is a top level menu ... if (isTL) { // If detach is enabled, use .position() if (!settings.detach) x = op; // Otherwise, use .offset() else x = oo; top = x.top + $opener.outerHeight() + settings.globalOffsetY; c = settings.alignment; // Remove alignment classes. $menu .removeClass('left') .removeClass('right') .removeClass('center'); // Figure out alignment and position. switch (settings.alignment) { case 'right': left = x.left - mw + ow; if (left < 0) { left = x.left; c = 'left'; } break; case 'center': left = x.left - Math.floor((mw - ow) / 2); if (left < 0) { left = x.left; c = 'left'; } else if (left + mw > $window.width()) { left = x.left - mw + ow; c = 'right'; } break; case 'left': default: left = x.left; if (left + mw > $window.width()) { left = x.left - mw + ow; c = 'right'; } break; } // Add alignment class. $opener.find( 'a' ).blur(); // Take focus off the opening link $menu .addClass(c) .find( 'li' ) .first() .find( 'a' ) .focus(); // After adding classes, give focus to the first anchor tag in the submenu } // Otherwise, we're dealing with a submenu. else { // If the opener position isn't static ... if ($opener.css('position') == 'relative' || $opener.css('position') == 'absolute') { top = settings.offsetY; left = (-1 * op.left); } else { top = op.top + settings.offsetY; left = 0; } // Figure out position (based on alignment). switch (settings.alignment) { case 'right': left += (-1 * $opener.parent().outerWidth()) + settings.offsetX; break; case 'center': case 'left': default: left += $opener.parent().outerWidth() + settings.offsetX; break; } } // Legacy IE? Apply IE-specific offsets. if (navigator.userAgent.match(/MSIE ([0-9]+)\./) && RegExp.$1 < 8) { left += settings.IEOffsetX; top += settings.IEOffsetY; } // Position and show the menu. $menu .css('left', left + 'px') .css('top', top + 'px') .css('opacity', '0.01') .show(); // Kludge! var tmp = false; // Non-static position fix. if ($opener.css('position') == 'relative' || $opener.css('position') == 'absolute') left = (-1 * op.left); else left = 0; if ($menu.offset().left < 0) { left += $opener.parent().outerWidth() - settings.offsetX; tmp = true; } else if ($menu.offset().left + mw > $window.width()) { left += (-1 * $opener.parent().outerWidth()) - settings.offsetX; tmp = true; } if (tmp) $menu .css('left', left + 'px'); $menu .hide() .css('opacity', '1'); // Figure out animation mode. switch (settings.mode) { case 'zoom': isLocked = true; $opener.addClass(settings.openerActiveClass); $menu.animate({ width: 'toggle', height: 'toggle' }, settings.speed, settings.easing, function() { isLocked = false; }); break; case 'slide': isLocked = true; $opener.addClass(settings.openerActiveClass); $menu.animate({ height: 'toggle' }, settings.speed, settings.easing, function() { isLocked = false; }); break; case 'fade': isLocked = true; if (isTL && !settings.noOpenerFade) { var tmp; if (settings.speed == 'slow') tmp = 80; else if (settings.speed == 'fast') tmp = 40; else tmp = Math.floor(settings.speed / 2); $opener.fadeTo(tmp, 0.01, function() { $opener.addClass(settings.openerActiveClass); $opener.fadeTo(settings.speed, 1); $menu.fadeIn(settings.speed, function() { isLocked = false; }); }); } else { $opener.addClass(settings.openerActiveClass); $opener.fadeTo(settings.speed, 1); $menu.fadeIn(settings.speed, function() { isLocked = false; }); } break; case 'instant': default: $opener.addClass(settings.openerActiveClass); $menu.show(); break; } return false; }) .on('doCollapse', function() { // Not visible? Bail out. if (!$menu.is(':visible')) return false; // Collapse the menu. $menu.hide(); $opener.removeClass(settings.openerActiveClass); $menu.find('.' + settings.openerActiveClass).removeClass(settings.openerActiveClass); $menu.find('ul').hide(); return false; }) .on('doToggle', function(e) { if ($menu.is(':visible')) $menu.trigger('doCollapse'); else $menu.trigger('doExpand'); return false; }); // Menu's opener. $opener .disableSelection_dropotron() .addClass('opener') .css('cursor', 'pointer') .on('click touchend', function(e) { // Locked? Bail. if (isLocked) return; // Toggle menu. e.preventDefault(); e.stopPropagation(); $menu.trigger('doToggle'); }); // If expandMode is "hover", set up the event. if (settings.expandMode == 'hover') $opener.hover(function(e) { if (isLocked) return; hoverTimeoutId = window.setTimeout(function() { $menu.trigger('doExpand'); }, settings.hoverDelay); }, function (e) { window.clearTimeout(hoverTimeoutId); }); }); // All links. $menus.find('a') .css('display', 'block') .on('click touchend', function(e) { // Locked? Bail. if (isLocked) return; // No href? Prevent default. if ($(this).attr('href').length < 1) e.preventDefault(); }); // All list items. $top.find('li') .css('white-space', 'nowrap') .each(function() { var $this = $(this), $a = $this.children('a'), $ul = $this.children('ul'), href = $a.attr('href'); // If href is blank ("") or a hash (#), prevent the link from doing anything. $a.on('click touchend', function(e) { if (href.length == 0 || href == '#') e.preventDefault(); else e.stopPropagation(); }); // If there is a link but no unordered list ... if ($a.length > 0 && $ul.length == 0) $this.on('click touchend', function(e) { if (isLocked) return; $top.trigger('doCollapseAll'); e.stopPropagation(); }); }); // Top level list items. $top.children('li').each(function() { var $opener = $(this), $menu = $opener.children('ul'), c; if ($menu.length > 0) { // If we're using detach ... if (settings.detach) { // If we're cloning on detach ... if (settings.cloneOnDetach) { // Make a copy of the menu. c = $menu.clone(); // Leave it behind c .attr('class', '') .hide() .appendTo($menu.parent()); } // Detach the menu and move it to the end of the element. $menu .detach() .appendTo($body); } // Step through menus, assigning z-indexes and submenu class prefixes as necessary. for(var z = settings.baseZIndex, i = 1, y = $menu; y.length > 0; i++) { y.css('z-index', z++); if (settings.submenuClassPrefix) y.addClass(settings.submenuClassPrefix + (z - 1 - settings.baseZIndex)); y = y.find('> li > ul'); } } }); // Window. $window .on('', function() { $top.trigger('doCollapseAll'); }) .on('keypress', function(e) { // Collapse all menus if the user hits escape. if (!isLocked && e.keyCode == 27) { e.preventDefault(); $top.trigger('doCollapseAll'); } }); // Body. $bodyhtml .on('click touchend', function() { if (!isLocked) $top.trigger('doCollapseAll'); }); }; })(jQuery)
SimonHooker Posted May 6, 2016 Posted May 6, 2016 Hi Ben, Very sorry but have been pretty busy with releases lately so haven't had a chance to look into this issue!
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now