1 2// Guarded: this is top-level, so a missing wrapper here would throw and abort the 3// whole file - taking the menu behavior below down with it. 4const nav = document.getElementsByClassName('elementor-widget-ubermenu')[0] 5if (nav) nav.onclick = function (element) { 6 const target = element.target 7 if (target.className === 'elementor-widget-container') { 8 const header = document.getElementsByClassName('nav-opened')[0] 9 nav.classList.remove('is-toggled') 10 header.classList.remove('nav-opened') 11 } 12} 13 14jQuery(function($) { 15 16 // Below the responsive breakpoint, require an explicit click to open a submenu 17 // instead of hover. Hovering with a mouse in a narrow/resized browser window is what 18 // caused the whole hover-intent-sensitivity saga below in the first place, and it's 19 // not how anyone actually interacts with a mobile layout anyway. Touch devices 20 // already use a deliberate tap (handleTap), which is untouched - this only changes 21 // mouse behavior at narrow widths. Real desktop-width hover_intent is unaffected. 22 // 23 // This rides on ubermenu's own per-item trigger override rather than patching 24 // handleMouseIntent: the plugin's hover bindings are delegated with 25 // ':not([data-ubermenu-trigger])' in the selector, and delegated selectors are 26 // matched at dispatch time rather than bind time - so setting that attribute at 27 // runtime stops hover firing on the item immediately, no monkey-patching needed. 28 // 29 // Deliberately placed above the prototype patching below, which bails early when it 30 // can't reach a live instance. Click enforcement must not be gated on that. 31 var OURS = 'custom-mobile-click'; // marks only the attributes we added ourselves 32 33 function syncTriggers() { 34 $( '.ubermenu' ).each( function() { 35 var $u = $( this ); 36 var inst = $u.data( 'plugin_ubermenu' ); 37 if ( !inst ) return; 38 39 // isMobile() reads each instance's own configured breakpoint - never hardcode it 40 var mobile = inst.isMobile(); 41 42 $u.find( '.ubermenu-item.ubermenu-has-submenu-drop' ).each( function() { 43 var $li = $( this ); 44 if ( mobile ) { 45 if ( $li.data( OURS ) !== undefined ) return; // already ours 46 // Override author-set triggers too (level-0 items ship with 47 // data-ubermenu-trigger="hover_intent" from wp-admin), but stash 48 // whatever was there so desktop gets it back verbatim. Storing null 49 // for "there was no attribute" keeps that case distinguishable. 50 var existing = $li.attr( 'data-ubermenu-trigger' ); 51 $li.data( OURS, existing === undefined ? null : existing ); 52 $li.attr( 'data-ubermenu-trigger', 'click' ); 53 } 54 else if ( $li.data( OURS ) !== undefined ) { 55 var prev = $li.data( OURS ); 56 if ( prev === null ) $li.removeAttr( 'data-ubermenu-trigger' ); 57 else $li.attr( 'data-ubermenu-trigger', prev ); 58 $li.removeData( OURS ); 59 } 60 }); 61 }); 62 } 63 64 // handleMouseClick already implements exactly the toggle behavior we want here (open 65 // on first click; on a second click, follow the link if there is one and close). 66 // Ubermenu only binds its own click handler to items that carried 67 // data-ubermenu-trigger at init time, so the ones we mark later need this. Delegated 68 // from document so it survives the menu being re-rendered. 69 $( document ).on( 'click.ubermenu-mobile-click-toggle', '.ubermenu-item.ubermenu-has-submenu-drop[data-ubermenu-trigger="click"] > .ubermenu-target', function( e ) { 70 var $li = $( this ).parent(); 71 var stashed = $li.data( OURS ); 72 73 // Not one of ours - we're above the breakpoint, or syncTriggers hasn't reached 74 // this item. Must be an undefined check, not truthiness: null is a meaningful 75 // stashed value meaning "this item had no trigger attribute of its own". 76 if ( stashed === undefined ) return; 77 78 var inst = $( this ).closest( '.ubermenu' ).data( 'plugin_ubermenu' ); 79 if ( !inst ) return; 80 81 // If the item's own configured trigger was already 'click', ubermenu bound a 82 // click handler directly to it at init (the divergents loop) and driving it from 83 // here as well would toggle twice - open then straight back closed. Items 84 // configured for hover/hover_intent never got a click handler, so those are ours 85 // to drive. With trigger overrides disabled that loop doesn't run at all, so 86 // nothing was bound and we handle them either way. 87 if ( stashed === 'click' && inst.allow_trigger_overrides ) return; 88
89 // Pointer-enabled devices already get open/close via handleTap (bound to 90 // pointerup) with preventDefault suppressing the synthetic click that would 91 // follow - skip here too, matching the plugin's own suppress_clicks convention, 92 // so a tap doesn't toggle twice. 93 if ( inst.suppress_clicks ) return; 94 inst.handleMouseClick( e, this, inst ); 95 }); 96 97 var resizeTimer; 98 $( window ).on( 'resize.ubermenu-mobile-click-toggle', function() { 99 clearTimeout( resizeTimer ); 100 resizeTimer = setTimeout( syncTriggers, 150 ); 101 }); 102 103 // Once now, and again at load in case ubermenu initializes after DOM ready. 104 syncTriggers(); 105 $( window ).on( 'load.ubermenu-mobile-click-toggle', syncTriggers ); 106 107 // Ubermenu's Plugin constructor/prototype are closed over inside ubermenu.js's 108 // own IIFE - not exposed globally - so they can't be monkey-patched the normal 109 // way (there's no jQuery.fn.ubermenu.Constructor). The only external hook is the 110 // live instance jQuery stores per-element via $.data(el, 'plugin_ubermenu'). 111 // Object.getPrototypeOf() on that instance gets the real shared prototype 112 // object regardless (ubermenu.js reassigns Plugin.prototype = {...} as a whole 113 // object literal, which drops the default `constructor` back-reference, so 114 // `instance.constructor` can't be trusted - getPrototypeOf() doesn't care). 115 // 116 // Ubermenu constructs its instances inside its OWN jQuery ready callback, and 117 // ubermenu-custom is enqueued depending only on 'jquery' - so whether an instance 118 // exists by the time this runs comes down to script order, which nothing 119 // enforces. This used to `return` when it didn't, silently disabling every patch 120 // below with no console error to show for it. Wait for it instead. The mu-plugin 121 // ubermenu-custom-js-deps.php makes the ordering deterministic; this is the 122 // belt-and-braces half. 123 function withInstance( cb ) { 124 var tries = 0; 125 (function poll() { 126 var $menu = $( '.ubermenu' ).first(); 127 var instance = $menu.length ? $menu.data( 'plugin_ubermenu' ) : null; 128 if ( instance ) return cb( instance ); 129 if ( ++tries > 40 ) return; // ~4s, then give up rather than poll forever 130 setTimeout( poll, 100 ); 131 })(); 132 } 133 134 withInstance( function( instance ) { 135 136 var proto = Object.getPrototypeOf( instance ); 137 138 // The menu is definitely initialized now, so re-sync the triggers set up 139 // above - the first syncTriggers() call may have run before there was an 140 // instance to read isMobile() from. 141 syncTriggers(); 142 143 // Items carrying their own data-ubermenu-trigger get their hover handler bound 144 // DIRECTLY to the element in ubermenu's divergents loop - not delegated, and 145 // with no ':not([data-ubermenu-trigger])' filter to fall foul of. Rewriting the 146 // attribute above therefore can't suppress those, and every level-0 item here 147 // ships with trigger="hover_intent" set in wp-admin. So hover still has to be 148 // gated at the prototype. handleMouseover covers the same case for items whose 149 // configured trigger is plain 'hover' rather than 'hover_intent'. 150 var origHandleMouseIntent = proto.handleMouseIntent; 151 proto.handleMouseIntent = function( e, item, plugin ) { 152 if ( plugin.isMobile() ) return; 153 return origHandleMouseIntent.apply( this, arguments ); 154 }; 155 156 var origHandleMouseover = proto.handleMouseover; 157 proto.handleMouseover = function( e, target, plugin ) { 158 if ( plugin.isMobile() ) return; 159 return origHandleMouseover.apply( this, arguments ); 160 }; 161 162 // How long the mouse must rest on an item before a hover-triggered submenu 163 // switch is confirmed. Ubermenu's own intent_threshold/intent_interval (set in 164 // wp-admin: Hover Intent Settings) only gate how STILL the mouse must be 165 // between polls, not how LONG it must stay put - a mouse that's naturally 166 // resting (normal hand tremor) passes even a strict threshold on the very 167 // first poll, so switching still felt near-instant without this. 168 var CUSTOM_INTENT_MIN_DWELL = 400; // ms 169 170 var origHandleMouseIntentSuccess = proto.handleMouseIntentSuccess; 171 proto.handleMouseIntentSuccess = function( e, $item, plugin ) { 172 var self = this; 173 var args = arguments; 174 175 function attemptOpen() { 176 // Also hold off while another submenu is mid-transition - opening now 177 // would force triggerSubmenu to cut that transition short (see its 178 // closeSubmenuInstantly on ".ubermenu-active, .ubermenu-in-transition" 179 // siblings), which is what makes submenus feel like they're rapidly 180 // flipping between each other. 181 if ( plugin.$ubermenu.find( '.ubermenu-in-transition' ).length ) { 182 setTimeout( attemptOpen, plugin.settings.intent_interval ); 183 return; 184 } 185 origHandleMouseIntentSuccess.apply( self, args ); 186 } 187 188 setTimeout( function() { 189 // If the mouse already left before our minimum dwell elapsed, ubermenu's 190 // own mouseleave.mouse_intent_none handler will have reset this back to 191 // 0 already - don't open in that case. 192 if ( $item.data( 'mouse_intent_state' ) !== 1 ) return; 193 attemptOpen(); 194 }, CUSTOM_INTENT_MIN_DWELL ); 195 }; 196 197 // Touch taps have no gating at all otherwise (unlike mouse hover, which goes 198 // through compare()'s intent_threshold/interval) - a rapid run of taps would 199 // immediately cut short whatever panel is still animating in/out. 200 var origHandleTap = proto.handleTap; 201 proto.handleTap = function( e, target, plugin ) { 202 if ( plugin.$ubermenu.find( '.ubermenu-in-transition' ).length ) { 203 e.preventDefault(); 204 e.stopPropagation(); 205 plugin.log( 'Ignoring tap during active transition (custom.js)' ); 206 var $target = $( target ); 207 $target.data( 'ubermenu-killTouch', false ); 208 plugin.resetHandlers( $target, 'handleTap-transition-hold', plugin ); 209 return; 210 } 211 return origHandleTap.apply( this, arguments ); 212 }; 213 214 }); 215 216});
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.