1(function($) { 2 3 /* globals jQuery */ 4 5 "use strict"; 6 7 /** 8 * Accesibility | Keyboard Support 9 PBL 10 */ 11 12 var keyboard = { 13 14 keysTriggered: { // multitasking alike 15 Tab: false, Enter: false, ShiftLeft: false, ShiftRight: false, Escape: false, 16 ArrowLeft: false, ArrowRight: false, ArrowUp: false, ArrowDown: false 17 }, 18 19 utils: { 20 isSimpleHamburgerStack: !($('body').hasClass('header-simple') && !$('body').hasClass('mobile-side-slide')), 21 switchExpanded: (el, state) => $(el).attr('aria-expanded', state), 22 }, 23 24 menuLinks: $('#menu .menu-item a').not('.menu-toggle').toArray(), 25 subheaderLinks: $('#Subheader a').toArray(), 26 wooPopup: $('.mfn-header-login').find('a, input').toArray(), 27 contentLinks: [...$('#Content a').toArray(), ...$('#Footer a').toArray()], 28 29 clickListener() { 30 31 jQuery(document).on('keydown', 'a, *[role="link"], button', ( e ) => { 32 let { originalEvent } = e; 33 let { code : keyClicked } = originalEvent; 34 35 if ( keyClicked in this.keysTriggered ) { 36 this.keysTriggered[keyClicked] = true; 37 } 38 39 setTimeout( _ => this.recognizeGesture(e), 1); 40 }); 41 42 jQuery(document).on('keyup', 'a, *[role="link"], button', ( e ) => { 43 let { originalEvent } = e; 44 let { code : keyClicked } = originalEvent; 45 46 if ( keyClicked in this.keysTriggered ) { 47 this.keysTriggered[keyClicked] = false; 48 } 49 50 setTimeout( _ => this.recognizeGesture(e), 1); 51 }); 52 53 jQuery('#skip-links-menu').one('focus', 'a', function(){ 54 $('#skip-links-menu').css('top', '0px'); 55 }); 56 57 }, 58 59 skipLinks() { 60 if( $(':focus').closest('nav').is('#skip-links-menu') ) { 61 $('#skip-links-menu').css('top', '0px'); 62 } else { 63 $('#skip-links-menu').css('top', '-200px'); 64 } 65 }, 66 67 recognizeGesture(e) { 68 let { Tab, ShiftLeft, ShiftRight, ArrowDown, ArrowUp, ArrowLeft, ArrowRight, Enter, Escape } = this.keysTriggered; 69 const focusedElement = $(':focus'); 70 let modalOpened = $('*[aria-expanded=true]:not(#menu):not(.sub-menu)'); 71 const menuOpened = $('nav#menu').find('*[aria-expanded=true]'); 72 const domPrefix = $('body').hasClass('side-slide-is-open') ? '.extras-wrapper ' : '.top_bar_right '; 73 const shouldChangeDirection = () => { 74 let shouldChange = false; 75 76 if ($('body').hasClass('rtl')) shouldChange = true; // RTL 77 if ($('body').hasClass('header-rtl')) shouldChange = true; // Header Right | Ex. Creative Right 78 79 return shouldChange; 80 } 81 82 /* CLOSE MODALS WHEN CURSOR IS OUTSIDE OF IT --- FORCE !!! */ 83 if( $(modalOpened).length && !focusedElement.closest(modalOpened).length) { 84 // If Side_slide is opened, then modals should be closed first ALWAYS 85 // classList object| it will recog if item is from DOM, not just a trash code from jquery 86 const elementToClose = Object.values(modalOpened).filter( 87 (modal) => (modal.classList && $(modal).attr('id') !== 'Side_slide') 88 ) 89 modalOpened = !elementToClose.length ? modalOpened : $(elementToClose[0]); 90 91 // All other modals are using mfn-close-icon class, Side_slide is exception 92 if( modalOpened.attr('id') !== 'Side_slide' && modalOpened.attr('aria-expanded') == 'true') { 93 94 if( $(modalOpened).siblings('.mfn-close-icon').length ){ 95 $(modalOpened).siblings('.mfn-close-icon').trigger('click'); 96 } else { 97 $(modalOpened).find('.mfn-close-icon').trigger('click'); 98 } 99 } 100 101 this.utils.switchExpanded(modalOpened, false); 102 103 switch(true){ 104 case modalOpened.hasClass('woocommerce-MyAccount-navigation'): 105 $(`${domPrefix} .myaccount_button`).trigger('focus'); 106 break; 107 case modalOpened.hasClass('mfn-cart-holder'): 108 //modalOpened.find('.mfn-close-icon').trigger('click'); 109 $(`${domPrefix} #header_cart`).trigger('focus'); 110 break; 111 case modalOpened.attr('id') === 'Side_slide': 112 modalOpened.find('.close').trigger('click'); 113 $(`.responsive-menu-toggle`).trigger('focus'); 114 break; 115 } 116 } 117 118 /* CLOSE MENU DROPDOWNS WHEN OUTSIDE OF IT --- AVOID HAMBURGER MENU WITH SIMPLE STYLE OF HEADER !!! */ 119 if( $(menuOpened).length && this.utils.isSimpleHamburgerStack ) { 120 if ( !focusedElement.closest(menuOpened).length && !menuOpened.siblings().is(focusedElement) ) { 121 $(menuOpened).attr('aria-expanded','false').slideUp(); 122 } 123 } 124 125 /* DOUBLE CHECK OF SUBMENUS ARIA-EXPANDED! */ 126 $('.sub-menu[aria-expanded=true]').each( (index, item) => { 127 if ( $(item).css('display') == 'none') { 128 this.utils.switchExpanded(item, false); 129 } 130 }) 131 132 if(Enter){ 133 switch( true ){ 134 //Side slide close by button 135 case focusedElement.hasClass('close'): 136 this.utils.switchExpanded(modalOpened, false); 137 $('.responsive-menu-toggle').trigger('focus'); 138 break; 139 140 //Submenu side-slide 141 case focusedElement.hasClass('menu-toggle') && $('body').hasClass('side-slide-is-open'): 142 const submenuSide = focusedElement.siblings('.sub-menu'); 143 144 if (submenuSide.css('display') == 'block') { 145 submenuSide.find('a').first().trigger('focus'); 146 } 147 148 break; 149 150 //Submenu dropdown 151 case focusedElement.hasClass('menu-toggle') && !$('body').hasClass('side-slide-is-open'): 152 const submenu = focusedElement.siblings('.sub-menu'); 153 154 //When hamburger menu with header simple, then break the loop 155 if ( !this.utils.isSimpleHamburgerStack ){ 156 157 if ( submenu.attr('aria-expanded') == 'true' ) { 158 this.utils.switchExpanded(submenu, false); 159 } else { 160 this.utils.switchExpanded(submenu, true); 161 162 let submenuItem = submenu.find('a').first(); 163 setTimeout(_ => submenuItem.trigger('focus'), 100); 164 } 165 166 break; 167 } 168 169 if (submenu.css('display') != 'none') { 170 this.utils.switchExpanded(submenu, false); 171 submenu.slideUp(0); 172 173 //close deeper arias expanded 174 const subarias = submenu.find('*[aria-expanded=true]'); 175 if (subarias.length) { 176 this.utils.switchExpanded(subarias, false);
177 subarias.slideUp(); 178 } 179 180 } else { 181 this.utils.switchExpanded(submenu, true); 182 183 let submenuItem = submenu.slideDown(0).find('a').first(); 184 setTimeout(_ => submenuItem.trigger('focus'), 100); 185 } 186 187 break; 188 189 // WooCommerce Header Login 190 case focusedElement.hasClass('toggle-login-modal'): 191 $('.woocommerce-MyAccount-navigation').find('a').first().trigger('focus'); 192 $('.woocommerce-MyAccount-navigation').attr('aria-expanded', 'true'); 193 break; 194 195 // Elements which imitate links 196 case focusedElement.attr('role') === 'link': 197 if( focusedElement.find('.image_links').length ){ //image 198 window.location = focusedElement.find('a').attr('href'); 199 } else if( focusedElement.find('.title').length ) { //accordion 200 focusedElement.find('.title').trigger('click'); 201 } else if( focusedElement.closest('.mfn-woo-list').length ) { 202 focusedElement.trigger('click'); 203 } 204 break; 205 206 //WPML dropdown 207 case focusedElement.attr('data-title') === 'wpml': 208 const langDropdown = focusedElement.siblings('.wpml-lang-dropdown'); 209 210 if ( langDropdown.attr('aria-expanded') == 'false' ) { 211 langDropdown.attr('aria-expanded', 'true'); 212 langDropdown.find('a').first().trigger('focus'); 213 } else { 214 langDropdown.attr('aria-expanded', 'false'); 215 } 216 break; 217 218 //WooCommerce Cart Sidebar 219 case focusedElement.hasClass('single_add_to_cart_button'): 220 $('.mfn-cart-holder').find('a').first().trigger('focus'); 221 break; 222 223 //Turn on dropdown or sidecart 224 case focusedElement.hasClass('responsive-menu-toggle'): 225 //Sideslide --- socials && extras, order of array is important! 226 if ( $('body').hasClass('mobile-side-slide') ) { 227 this.menuLinks = [ 228 ...$('#Side_slide').find('a.close').toArray(), 229 ...$('.extras-wrapper').find('a').toArray(), 230 ...this.menuLinks, 231 ...$('#Side_slide .social').find('a').toArray() 232 ]; 233 234 $(this.menuLinks[0]).trigger('focus'); 235 focusedElement.trigger('click'); 236 this.utils.switchExpanded( $('#Side_slide'), true ); 237 } 238 239 break; 240 241 case focusedElement.hasClass('overlay-menu-toggle'): 242 if( $('#overlay-menu ul').attr('aria-expanded') == 'false' || $('#overlay-menu ul').attr('aria-expanded') === undefined ){ 243 this.utils.switchExpanded( $('#overlay-menu ul'), true ); 244 245 $('#overlay-menu').find('.menu-item a').first().trigger('focus'); 246 }else{ 247 this.utils.switchExpanded($('#overlay-menu ul'), false); 248 249 if( focusedElement.hasClass('close') ) { 250 $('.overlay-menu-toggle').trigger('focus'); 251 } 252 } 253 break; 254 } 255 256 } else if(Tab && (ShiftLeft || ShiftRight) ) { 257 258 //Tabs fix, make noticable for tab, overwrite tabindex from -1 to 0 259 $('a.ui-tabs-anchor').attr('tabindex', 0); 260 261 //Skip links, are they triggered? 262 this.skipLinks(); 263 264 }else if(Tab) { 265 266 //Tabs fix, make noticable for tab, overwrite tabindex from -1 to 0 267 $('a.ui-tabs-anchor').attr('tabindex', 0); 268 269 //Skip links, are they triggered? 270 this.skipLinks(); 271 272 switch( true ) { 273 case focusedElement.closest('li').hasClass('wc-block-product-categories-list-item'): // woocommerce widget -- product category dropdowns 274 if( !focusedElement.closest('.li-expanded').length ){ 275 focusedElement.siblings('.cat-expander').trigger('click'); 276 focusedElement.siblings('ul').find('a').first().trigger('focus'); 277 } 278 279 focusedElement.closest('li').siblings('.li-expanded').each((index, object) => $(object).find('.cat-expander').trigger('click') ); 280 break;
281 case focusedElement.is('.overlay-menu-toggle', '.focus') && $('#Overlay').css('display') === 'block': //Overlay 282 $('.overlay-menu-toggle').trigger('focus'); 283 break; 284 case focusedElement.is( $(this.contentLinks[this.contentLinks.length-1]) ) && !$('body').hasClass('footer-menu-sticky'): //when reached end of page 285 //if fixed nav, then first, reach that 286 if( $('.fixed-nav').length ) { 287 $('.fixed-nav').first().trigger('focus'); 288 } else { 289 $('body a').first().trigger('focus'); 290 } 291 292 break; 293 } 294 295 } else if ( Escape ) { 296 var openedSubmenus = Array.from( $('.sub-menu[aria-expanded=true]') ); 297 var modals = $('.woocommerce').find('nav[aria-expanded=true]'); 298 299 300 if ( $('body').hasClass('side-slide-is-open') && focusedElement.closest('#Side_slide').length) { 301 //side slide 302 this.utils.switchExpanded(modalOpened, false); 303 modalOpened.find('.close').trigger('click'); 304 305 $('.responsive-menu-toggle').trigger('focus'); 306 } else if( openedSubmenus.length ) { 307 //menus, dropdown 308 var menuItemOpened = $('nav').find('.sub-menu[aria-expanded=true]').siblings('a.menu-toggle'); 309 310 openedSubmenus.forEach(submenu => { 311 this.utils.switchExpanded(submenu, false); 312 $(submenu).slideUp(); 313 }) 314 315 menuItemOpened.trigger('focus'); 316 } else if( $('.mfn-header-login').find('nav[aria-expanded=true]').length ) { 317 //side login 318 $('.close-login-modal').trigger('click'); 319 this.utils.switchExpanded(modals, false); 320 321 $('.myaccount_button').trigger('focus'); 322 } else if( $('.mfn-cart-holder').attr('aria-expanded') == 'true' ) { 323 //side cart 324 $('.mfn-cart-holder').find('.close-mfn-cart').trigger('click'); 325 this.utils.switchExpanded($('.mfn-cart-holder'), false); 326 327 $('#header_cart').trigger('focus'); 328 return; 329 } else if ( $('.responsive-menu-toggle').hasClass('active') ) { 330 // responsive menu toggle 331 $('.responsive-menu-toggle').trigger('click'); 332 $('.responsive-menu-toggle').trigger('focus'); 333 } 334 335 336 } else if ( !shouldChangeDirection() ? ArrowRight : ArrowLeft ) { 337 338 if(focusedElement.closest('li').find('.menu-toggle').length) { 339 const submenu = focusedElement.siblings('.sub-menu'); 340 341 if (submenu.css('display') == 'none') { 342 this.utils.switchExpanded(submenu, true); 343 344 //open and focus next submenu 345 let submenuItem = submenu.slideDown(0).find('a').first(); 346 setTimeout(_ => submenuItem.trigger('focus'), 100); 347 } 348 } 349 350 } else if ( !shouldChangeDirection() ? ArrowLeft : ArrowRight ) { 351 352 if(focusedElement.closest('ul[aria-expanded=true]').length) { 353 const submenu = focusedElement.closest('.sub-menu'); 354 355 //close deeper arias expanded 356 const subarias = submenu.find('*[aria-expanded=true]'); 357 if (subarias.length) { 358 this.utils.switchExpanded(subarias, false); 359 subarias.slideUp(); 360 } 361 } 362 363 } 364 365 }, 366 367 init() { 368 369 if( $('body').hasClass('keyboard-support') ) { 370 371 this.clickListener(); 372 373 /* Instead of using WP Walkers, we can do that using JS, quicker, simpler (KISS PRINCIPLE) */ 374 $('.sub-menu').attr('aria-expanded', 'false'); 375 $('.menu-toggle, .menu-item a').attr('tabindex', '0'); 376 377 /* These containers are changing for multiple templates, change it by JS (DRY PRINCIPLE) */ 378 $('#Content').attr('role', 'main'); 379 $('#Header_wrapper').attr('role', 'banner').attr('aria-label', mfn.accessibility.translation.headerContainer); 380 381 // Woo Menu 382 $('.woocommerce-MyAccount-navigation').attr('role', 'navigation').attr('aria-expanded', 'false'); 383 384 /* Remove aria-expanded for headers which does not open on menu click (responsive-menu-button) */ 385 if ( !$('body').is('.header-creative, .header-simple, .header-overlay') ) { 386 $('#menu').removeAttr('aria-expanded'); 387 } 388 } 389 390 } 391 392 } 393 394 /** 395 * Accesibility | New tab or _blank target links | PBL 396 */ 397 398 var warningLinks = { 399 400 onLinkAlert(){ 401 $('a').click(function( e ) { 402 403 const target = $(e.currentTarget); 404 if( (target.attr('target') === '_blank' || target.attr('target') === '0' ) ) { 405 //stop action 406 var answer = confirm ("The link will open in a new tab. Do you want to continue? "); 407 if (!answer) { 408 e.preventDefault(); 409 } 410 411 } 412 413 }) 414 }, 415 416 init(){ 417 if ( $('body').hasClass('warning-links') ) { 418 this.onLinkAlert(); 419 } 420 } 421 422 } 423 424 /** 425 * $(window).on('load') 426 * window.load 427 */ 428 429 $(window).on('load', function() { 430 431 keyboard.init(); 432 433 warningLinks.init(); 434 435 }); 436 437})(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.