[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[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.querySelector( '#wp-admin-bar-logout a' ); 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 // Follow link if pressing Ctrl and/or Shift with Enter (opening in a new tab or window). 153 if ( event.which !== 13 || event.ctrlKey || event.shiftKey ) { 154 return; 155 } 156 157 if ( !! getClosest( event.target, '.ab-sub-wrapper' ) ) { 158 return; 159 } 160 161 wrapper = getClosest( event.target, '.menupop' ); 162 163 if ( ! wrapper ) { 164 return; 165 } 166 167 event.preventDefault(); 168 169 if ( hasClass( wrapper, 'hover' ) ) { 170 removeClass( wrapper, 'hover' ); 171 } else { 172 addClass( wrapper, 'hover' ); 173 } 174 } 175 176 /** 177 * Focus the target of skip link after pressing Enter. 178 * 179 * @since 5.3.1 180 * 181 * @param {Event} event The keydown event. 182 */ 183 function focusTargetAfterEnter( event ) { 184 var id, userAgent; 185 186 if ( event.which !== 13 ) { 187 return; 188 } 189 190 id = event.target.getAttribute( 'href' ); 191 userAgent = navigator.userAgent.toLowerCase(); 192 193 if ( userAgent.indexOf( 'applewebkit' ) > -1 && id && id.charAt( 0 ) === '#' ) { 194 setTimeout( function() { 195 var target = document.getElementById( id.replace( '#', '' ) ); 196 197 if ( target ) { 198 target.setAttribute( 'tabIndex', '0' ); 199 target.focus(); 200 } 201 }, 100 ); 202 } 203 } 204 205 /** 206 * Toogle hover class for mobile devices. 207 * 208 * @since 5.3.1 209 * 210 * @param {NodeList} topMenuItems All menu items. 211 * @param {Event} event The click event. 212 */ 213 function mobileHover( topMenuItems, event ) { 214 var wrapper; 215 216 if ( !! getClosest( event.target, '.ab-sub-wrapper' ) ) { 217 return; 218 } 219 220 event.preventDefault(); 221 222 wrapper = getClosest( event.target, '.menupop' ); 223 224 if ( ! wrapper ) { 225 return; 226 } 227 228 if ( hasClass( wrapper, 'hover' ) ) { 229 removeClass( wrapper, 'hover' ); 230 } else { 231 removeAllHoverClass( topMenuItems ); 232 addClass( wrapper, 'hover' ); 233 } 234 } 235 236 /** 237 * Handles the click on the Shortlink link in the adminbar. 238 * 239 * @since 3.1.0 240 * @since 5.3.1 Use querySelector to clean up the function. 241 * 242 * @param {Event} event The click event. 243 * @return {boolean} Returns false to prevent default click behavior. 244 */ 245 function clickShortlink( event ) { 246 var wrapper = event.target.parentNode, 247 input; 248 249 if ( wrapper ) { 250 input = wrapper.querySelector( '.shortlink-input' ); 251 } 252 253 if ( ! input ) { 254 return; 255 } 256 257 // (Old) IE doesn't support preventDefault, and does support returnValue. 258 if ( event.preventDefault ) { 259 event.preventDefault(); 260 } 261 262 event.returnValue = false; 263 264 addClass( wrapper, 'selected' ); 265 266 input.focus(); 267 input.select(); 268 input.onblur = function() { 269 removeClass( wrapper, 'selected' ); 270 }; 271 272 return false; 273 } 274 275 /** 276 * Clear sessionStorage on logging out. 277 * 278 * @since 5.3.1 279 */ 280 function emptySessionStorage() { 281 if ( 'sessionStorage' in window ) { 282 try { 283 for ( var key in sessionStorage ) { 284 if ( key.indexOf( 'wp-autosave-' ) > -1 ) { 285 sessionStorage.removeItem( key ); 286 } 287 } 288 } catch ( er ) {} 289 } 290 } 291 292 /** 293 * Check if element has class. 294 * 295 * @since 5.3.1 296 * 297 * @param {HTMLElement} element The HTML element. 298 * @param {string} className The class name. 299 * @return {boolean} Whether the element has the className. 300 */ 301 function hasClass( element, className ) { 302 var classNames; 303 304 if ( ! element ) { 305 return false; 306 } 307 308 if ( element.classList && element.classList.contains ) { 309 return element.classList.contains( className ); 310 } else if ( element.className ) { 311 classNames = element.className.split( ' ' ); 312 return classNames.indexOf( className ) > -1; 313 } 314 315 return false; 316 } 317 318 /** 319 * Add class to an element. 320 * 321 * @since 5.3.1 322 * 323 * @param {HTMLElement} element The HTML element. 324 * @param {string} className The class name. 325 */ 326 function addClass( element, className ) { 327 if ( ! element ) { 328 return; 329 } 330 331 if ( element.classList && element.classList.add ) { 332 element.classList.add( className ); 333 } else if ( ! hasClass( element, className ) ) { 334 if ( element.className ) { 335 element.className += ' '; 336 } 337 338 element.className += className; 339 } 340 341 var menuItemToggle = element.querySelector( 'a' ); 342 if ( className === 'hover' && menuItemToggle && menuItemToggle.hasAttribute( 'aria-expanded' ) ) { 343 menuItemToggle.setAttribute( 'aria-expanded', 'true' ); 344 } 345 } 346 347 /** 348 * Remove class from an element. 349 * 350 * @since 5.3.1 351 * 352 * @param {HTMLElement} element The HTML element. 353 * @param {string} className The class name. 354 */ 355 function removeClass( element, className ) { 356 var testName, 357 classes; 358 359 if ( ! element || ! hasClass( element, className ) ) { 360 return; 361 } 362 363 if ( element.classList && element.classList.remove ) { 364 element.classList.remove( className ); 365 } else { 366 testName = ' ' + className + ' '; 367 classes = ' ' + element.className + ' '; 368 369 while ( classes.indexOf( testName ) > -1 ) { 370 classes = classes.replace( testName, '' ); 371 } 372 373 element.className = classes.replace( /^[\s]+|[\s]+$/g, '' ); 374 } 375 376 var menuItemToggle = element.querySelector( 'a' ); 377 if ( className === 'hover' && menuItemToggle && menuItemToggle.hasAttribute( 'aria-expanded' ) ) { 378 menuItemToggle.setAttribute( 'aria-expanded', 'false' ); 379 } 380 } 381 382 /** 383 * Remove hover class for all menu items. 384 * 385 * @since 5.3.1 386 * 387 * @param {NodeList} topMenuItems All menu items. 388 */ 389 function removeAllHoverClass( topMenuItems ) { 390 if ( topMenuItems && topMenuItems.length ) { 391 for ( var i = 0; i < topMenuItems.length; i++ ) { 392 removeClass( topMenuItems[i], 'hover' ); 393 } 394 } 395 } 396 397 /** 398 * Scrolls to the top of the page. 399 * 400 * @since 3.4.0 401 * 402 * @param {Event} event The Click event. 403 * 404 * @return {void} 405 */ 406 function scrollToTop( event ) { 407 // Only scroll when clicking on the wpadminbar, not on menus or submenus. 408 if ( 409 event.target && 410 event.target.id !== 'wpadminbar' && 411 event.target.id !== 'wp-admin-bar-top-secondary' 412 ) { 413 return; 414 } 415 416 try { 417 window.scrollTo( { 418 top: -32, 419 left: 0, 420 behavior: 'smooth' 421 } ); 422 } catch ( er ) { 423 window.scrollTo( 0, -32 ); 424 } 425 } 426 427 /** 428 * Get closest Element. 429 * 430 * @since 5.3.1 431 * 432 * @param {HTMLElement} el Element to get parent. 433 * @param {string} selector CSS selector to match. 434 */ 435 function getClosest( el, selector ) { 436 if ( ! window.Element.prototype.matches ) { 437 // Polyfill from https://developer.mozilla.org/en-US/docs/Web/API/Element/matches. 438 window.Element.prototype.matches = 439 window.Element.prototype.matchesSelector || 440 window.Element.prototype.mozMatchesSelector || 441 window.Element.prototype.msMatchesSelector || 442 window.Element.prototype.oMatchesSelector || 443 window.Element.prototype.webkitMatchesSelector || 444 function( s ) { 445 var matches = ( this.document || this.ownerDocument ).querySelectorAll( s ), 446 i = matches.length; 447 448 while ( --i >= 0 && matches.item( i ) !== this ) { } 449 450 return i > -1; 451 }; 452 } 453 454 // Get the closest matching elent. 455 for ( ; el && el !== document; el = el.parentNode ) { 456 if ( el.matches( selector ) ) { 457 return el; 458 } 459 } 460 461 return null; 462 } 463 464 } )( document, window, navigator );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Tue Mar 19 08:20:01 2024 | Cross-referenced by PHPXref |