[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 /** 2 * @output wp-includes/js/admin-bar.js 3 */ 4 /** 5 * Admin bar with Vanilla JS, no external dependencies. 6 * 7 * @since 5.3.1 8 * 9 * @param {Object} document The document object. 10 * @param {Object} window The window object. 11 * @param {Object} navigator The navigator object. 12 * 13 * @return {void} 14 */ 15 ( function( document, window, navigator ) { 16 document.addEventListener( 'DOMContentLoaded', function() { 17 var adminBar = document.getElementById( 'wpadminbar' ), 18 topMenuItems, 19 allMenuItems, 20 adminBarLogout, 21 adminBarSearchForm, 22 shortlink, 23 skipLink, 24 mobileEvent, 25 adminBarSearchInput, 26 i; 27 28 if ( ! adminBar || ! ( 'querySelectorAll' in adminBar ) ) { 29 return; 30 } 31 32 topMenuItems = adminBar.querySelectorAll( 'li.menupop' ); 33 allMenuItems = adminBar.querySelectorAll( '.ab-item' ); 34 adminBarLogout = document.getElementById( 'wp-admin-bar-logout' ); 35 adminBarSearchForm = document.getElementById( 'adminbarsearch' ); 36 shortlink = document.getElementById( 'wp-admin-bar-get-shortlink' ); 37 skipLink = adminBar.querySelector( '.screen-reader-shortcut' ); 38 mobileEvent = /Mobile\/.+Safari/.test( navigator.userAgent ) ? 'touchstart' : 'click'; 39 40 // Remove nojs class after the DOM is loaded. 41 removeClass( adminBar, 'nojs' ); 42 43 if ( 'ontouchstart' in window ) { 44 // Remove hover class when the user touches outside the menu items. 45 document.body.addEventListener( mobileEvent, function( e ) { 46 if ( ! getClosest( e.target, 'li.menupop' ) ) { 47 removeAllHoverClass( topMenuItems ); 48 } 49 } ); 50 51 // Add listener for menu items to toggle hover class by touches. 52 // Remove the callback later for better performance. 53 adminBar.addEventListener( 'touchstart', function bindMobileEvents() { 54 for ( var i = 0; i < topMenuItems.length; i++ ) { 55 topMenuItems[i].addEventListener( 'click', mobileHover.bind( null, topMenuItems ) ); 56 } 57 58 adminBar.removeEventListener( 'touchstart', bindMobileEvents ); 59 } ); 60 } 61 62 // Scroll page to top when clicking on the admin bar. 63 adminBar.addEventListener( 'click', scrollToTop ); 64 65 for ( i = 0; i < topMenuItems.length; i++ ) { 66 // Adds or removes the hover class based on the hover intent. 67 window.hoverintent( 68 topMenuItems[i], 69 addClass.bind( null, topMenuItems[i], 'hover' ), 70 removeClass.bind( null, topMenuItems[i], 'hover' ) 71 ).options( { 72 timeout: 180 73 } ); 74 75 // Toggle hover class if the enter key is pressed. 76 topMenuItems[i].addEventListener( 'keydown', toggleHoverIfEnter ); 77 } 78 79 // Remove hover class if the escape key is pressed. 80 for ( i = 0; i < allMenuItems.length; i++ ) { 81 allMenuItems[i].addEventListener( 'keydown', removeHoverIfEscape ); 82 } 83 84 if ( adminBarSearchForm ) { 85 adminBarSearchInput = document.getElementById( 'adminbar-search' ); 86 87 // Adds the adminbar-focused class on focus. 88 adminBarSearchInput.addEventListener( 'focus', function() { 89 addClass( adminBarSearchForm, 'adminbar-focused' ); 90 } ); 91 92 // Removes the adminbar-focused class on blur. 93 adminBarSearchInput.addEventListener( 'blur', function() { 94 removeClass( adminBarSearchForm, 'adminbar-focused' ); 95 } ); 96 } 97 98 if ( skipLink ) { 99 // Focus the target of skip link after pressing Enter. 100 skipLink.addEventListener( 'keydown', focusTargetAfterEnter ); 101 } 102 103 if ( shortlink ) { 104 shortlink.addEventListener( 'click', clickShortlink ); 105 } 106 107 // Prevents the toolbar from covering up content when a hash is present in the URL. 108 if ( window.location.hash ) { 109 window.scrollBy( 0, -32 ); 110 } 111 112 // Clear sessionStorage on logging out. 113 if ( adminBarLogout ) { 114 adminBarLogout.addEventListener( 'click', emptySessionStorage ); 115 } 116 } ); 117 118 /** 119 * Remove hover class for top level menu item when escape is pressed. 120 * 121 * @since 5.3.1 122 * 123 * @param {Event} event The keydown event. 124 */ 125 function removeHoverIfEscape( event ) { 126 var wrapper; 127 128 if ( event.which !== 27 ) { 129 return; 130 } 131 132 wrapper = getClosest( event.target, '.menupop' ); 133 134 if ( ! wrapper ) { 135 return; 136 } 137 138 wrapper.querySelector( '.menupop > .ab-item' ).focus(); 139 removeClass( wrapper, 'hover' ); 140 } 141 142 /** 143 * Toggle hover class for top level menu item when enter is pressed. 144 * 145 * @since 5.3.1 146 * 147 * @param {Event} event The keydown event. 148 */ 149 function toggleHoverIfEnter( event ) { 150 var wrapper; 151 152 if ( event.which !== 13 ) { 153 return; 154 } 155 156 if ( !! getClosest( event.target, '.ab-sub-wrapper' ) ) { 157 return; 158 } 159 160 wrapper = getClosest( event.target, '.menupop' ); 161 162 if ( ! wrapper ) { 163 return; 164 } 165 166 event.preventDefault(); 167 168 if ( hasClass( wrapper, 'hover' ) ) { 169 removeClass( wrapper, 'hover' ); 170 } else { 171 addClass( wrapper, 'hover' ); 172 } 173 } 174 175 /** 176 * Focus the target of skip link after pressing Enter. 177 * 178 * @since 5.3.1 179 * 180 * @param {Event} event The keydown event. 181 */ 182 function focusTargetAfterEnter( event ) { 183 var id, userAgent; 184 185 if ( event.which !== 13 ) { 186 return; 187 } 188 189 id = event.target.getAttribute( 'href' ); 190 userAgent = navigator.userAgent.toLowerCase(); 191 192 if ( userAgent.indexOf( 'applewebkit' ) > -1 && id && id.charAt( 0 ) === '#' ) { 193 setTimeout( function() { 194 var target = document.getElementById( id.replace( '#', '' ) ); 195 196 if ( target ) { 197 target.setAttribute( 'tabIndex', '0' ); 198 target.focus(); 199 } 200 }, 100 ); 201 } 202 } 203 204 /** 205 * Toogle hover class for mobile devices. 206 * 207 * @since 5.3.1 208 * 209 * @param {NodeList} topMenuItems All menu items. 210 * @param {Event} event The click event. 211 */ 212 function mobileHover( topMenuItems, event ) { 213 var wrapper; 214 215 if ( !! getClosest( event.target, '.ab-sub-wrapper' ) ) { 216 return; 217 } 218 219 event.preventDefault(); 220 221 wrapper = getClosest( event.target, '.menupop' ); 222 223 if ( ! wrapper ) { 224 return; 225 } 226 227 if ( hasClass( wrapper, 'hover' ) ) { 228 removeClass( wrapper, 'hover' ); 229 } else { 230 removeAllHoverClass( topMenuItems ); 231 addClass( wrapper, 'hover' ); 232 } 233 } 234 235 /** 236 * Handles the click on the Shortlink link in the adminbar. 237 * 238 * @since 3.1.0 239 * @since 5.3.1 Use querySelector to clean up the function. 240 * 241 * @param {Event} event The click event. 242 * @return {boolean} Returns false to prevent default click behavior. 243 */ 244 function clickShortlink( event ) { 245 var wrapper = event.target.parentNode, 246 input; 247 248 if ( wrapper ) { 249 input = wrapper.querySelector( '.shortlink-input' ); 250 } 251 252 if ( ! input ) { 253 return; 254 } 255 256 // (Old) IE doesn't support preventDefault, and does support returnValue. 257 if ( event.preventDefault ) { 258 event.preventDefault(); 259 } 260 261 event.returnValue = false; 262 263 addClass( wrapper, 'selected' ); 264 265 input.focus(); 266 input.select(); 267 input.onblur = function() { 268 removeClass( wrapper, 'selected' ); 269 }; 270 271 return false; 272 } 273 274 /** 275 * Clear sessionStorage on logging out. 276 * 277 * @since 5.3.1 278 */ 279 function emptySessionStorage() { 280 if ( 'sessionStorage' in window ) { 281 try { 282 for ( var key in sessionStorage ) { 283 if ( key.indexOf( 'wp-autosave-' ) > -1 ) { 284 sessionStorage.removeItem( key ); 285 } 286 } 287 } catch ( er ) {} 288 } 289 } 290 291 /** 292 * Check if element has class. 293 * 294 * @since 5.3.1 295 * 296 * @param {HTMLElement} element The HTML element. 297 * @param {string} className The class name. 298 * @return {boolean} Whether the element has the className. 299 */ 300 function hasClass( element, className ) { 301 var classNames; 302 303 if ( ! element ) { 304 return false; 305 } 306 307 if ( element.classList && element.classList.contains ) { 308 return element.classList.contains( className ); 309 } else if ( element.className ) { 310 classNames = element.className.split( ' ' ); 311 return classNames.indexOf( className ) > -1; 312 } 313 314 return false; 315 } 316 317 /** 318 * Add class to an element. 319 * 320 * @since 5.3.1 321 * 322 * @param {HTMLElement} element The HTML element. 323 * @param {string} className The class name. 324 */ 325 function addClass( element, className ) { 326 if ( ! element ) { 327 return; 328 } 329 330 if ( element.classList && element.classList.add ) { 331 element.classList.add( className ); 332 } else if ( ! hasClass( element, className ) ) { 333 if ( element.className ) { 334 element.className += ' '; 335 } 336 337 element.className += className; 338 } 339 } 340 341 /** 342 * Remove class from an element. 343 * 344 * @since 5.3.1 345 * 346 * @param {HTMLElement} element The HTML element. 347 * @param {string} className The class name. 348 */ 349 function removeClass( element, className ) { 350 var testName, 351 classes; 352 353 if ( ! element || ! hasClass( element, className ) ) { 354 return; 355 } 356 357 if ( element.classList && element.classList.remove ) { 358 element.classList.remove( className ); 359 } else { 360 testName = ' ' + className + ' '; 361 classes = ' ' + element.className + ' '; 362 363 while ( classes.indexOf( testName ) > -1 ) { 364 classes = classes.replace( testName, '' ); 365 } 366 367 element.className = classes.replace( /^[\s]+|[\s]+$/g, '' ); 368 } 369 } 370 371 /** 372 * Remove hover class for all menu items. 373 * 374 * @since 5.3.1 375 * 376 * @param {NodeList} topMenuItems All menu items. 377 */ 378 function removeAllHoverClass( topMenuItems ) { 379 if ( topMenuItems && topMenuItems.length ) { 380 for ( var i = 0; i < topMenuItems.length; i++ ) { 381 removeClass( topMenuItems[i], 'hover' ); 382 } 383 } 384 } 385 386 /** 387 * Scrolls to the top of the page. 388 * 389 * @since 3.4.0 390 * 391 * @param {Event} event The Click event. 392 * 393 * @return {void} 394 */ 395 function scrollToTop( event ) { 396 // Only scroll when clicking on the wpadminbar, not on menus or submenus. 397 if ( 398 event.target && 399 event.target.id !== 'wpadminbar' && 400 event.target.id !== 'wp-admin-bar-top-secondary' 401 ) { 402 return; 403 } 404 405 try { 406 window.scrollTo( { 407 top: -32, 408 left: 0, 409 behavior: 'smooth' 410 } ); 411 } catch ( er ) { 412 window.scrollTo( 0, -32 ); 413 } 414 } 415 416 /** 417 * Get closest Element. 418 * 419 * @since 5.3.1 420 * 421 * @param {HTMLElement} el Element to get parent. 422 * @param {string} selector CSS selector to match. 423 */ 424 function getClosest( el, selector ) { 425 if ( ! window.Element.prototype.matches ) { 426 // Polyfill from https://developer.mozilla.org/en-US/docs/Web/API/Element/matches. 427 window.Element.prototype.matches = 428 window.Element.prototype.matchesSelector || 429 window.Element.prototype.mozMatchesSelector || 430 window.Element.prototype.msMatchesSelector || 431 window.Element.prototype.oMatchesSelector || 432 window.Element.prototype.webkitMatchesSelector || 433 function( s ) { 434 var matches = ( this.document || this.ownerDocument ).querySelectorAll( s ), 435 i = matches.length; 436 437 while ( --i >= 0 && matches.item( i ) !== this ) { } 438 439 return i > -1; 440 }; 441 } 442 443 // Get the closest matching elent. 444 for ( ; el && el !== document; el = el.parentNode ) { 445 if ( el.matches( selector ) ) { 446 return el; 447 } 448 } 449 450 return null; 451 } 452 453 } )( document, window, navigator );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:03 2024 | Cross-referenced by PHPXref 0.7.1 |