[ 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 ( shortlink ) { 99 shortlink.addEventListener( 'click', clickShortlink ); 100 } 101 102 // Prevents the toolbar from covering up content when a hash is present in the URL. 103 if ( window.location.hash ) { 104 window.scrollBy( 0, -32 ); 105 } 106 107 // Clear sessionStorage on logging out. 108 if ( adminBarLogout ) { 109 adminBarLogout.addEventListener( 'click', emptySessionStorage ); 110 } 111 } ); 112 113 /** 114 * Remove hover class for top level menu item when escape is pressed. 115 * 116 * @since 5.3.1 117 * 118 * @param {Event} event The keydown event. 119 */ 120 function removeHoverIfEscape( event ) { 121 var wrapper; 122 123 if ( event.which !== 27 ) { 124 return; 125 } 126 127 wrapper = getClosest( event.target, '.menupop' ); 128 129 if ( ! wrapper ) { 130 return; 131 } 132 133 wrapper.querySelector( '.menupop > .ab-item' ).focus(); 134 removeClass( wrapper, 'hover' ); 135 } 136 137 /** 138 * Toggle hover class for top level menu item when enter is pressed. 139 * 140 * @since 5.3.1 141 * 142 * @param {Event} event The keydown event. 143 */ 144 function toggleHoverIfEnter( event ) { 145 var wrapper; 146 147 // Follow link if pressing Ctrl and/or Shift with Enter (opening in a new tab or window). 148 if ( event.which !== 13 || event.ctrlKey || event.shiftKey ) { 149 return; 150 } 151 152 if ( !! getClosest( event.target, '.ab-sub-wrapper' ) ) { 153 return; 154 } 155 156 wrapper = getClosest( event.target, '.menupop' ); 157 158 if ( ! wrapper ) { 159 return; 160 } 161 162 event.preventDefault(); 163 164 if ( hasClass( wrapper, 'hover' ) ) { 165 removeClass( wrapper, 'hover' ); 166 } else { 167 addClass( wrapper, 'hover' ); 168 } 169 } 170 171 /** 172 * Toggle hover class for mobile devices. 173 * 174 * @since 5.3.1 175 * 176 * @param {NodeList} topMenuItems All menu items. 177 * @param {Event} event The click event. 178 */ 179 function mobileHover( topMenuItems, event ) { 180 var wrapper; 181 182 if ( !! getClosest( event.target, '.ab-sub-wrapper' ) ) { 183 return; 184 } 185 186 event.preventDefault(); 187 188 wrapper = getClosest( event.target, '.menupop' ); 189 190 if ( ! wrapper ) { 191 return; 192 } 193 194 if ( hasClass( wrapper, 'hover' ) ) { 195 removeClass( wrapper, 'hover' ); 196 } else { 197 removeAllHoverClass( topMenuItems ); 198 addClass( wrapper, 'hover' ); 199 } 200 } 201 202 /** 203 * Handles the click on the Shortlink link in the adminbar. 204 * 205 * @since 3.1.0 206 * @since 5.3.1 Use querySelector to clean up the function. 207 * 208 * @param {Event} event The click event. 209 * @return {boolean} Returns false to prevent default click behavior. 210 */ 211 function clickShortlink( event ) { 212 var wrapper = event.target.parentNode, 213 input; 214 215 if ( wrapper ) { 216 input = wrapper.querySelector( '.shortlink-input' ); 217 } 218 219 if ( ! input ) { 220 return; 221 } 222 223 // (Old) IE doesn't support preventDefault, and does support returnValue. 224 if ( event.preventDefault ) { 225 event.preventDefault(); 226 } 227 228 event.returnValue = false; 229 230 addClass( wrapper, 'selected' ); 231 232 input.focus(); 233 input.select(); 234 input.onblur = function() { 235 removeClass( wrapper, 'selected' ); 236 }; 237 238 return false; 239 } 240 241 /** 242 * Clear sessionStorage on logging out. 243 * 244 * @since 5.3.1 245 */ 246 function emptySessionStorage() { 247 if ( 'sessionStorage' in window ) { 248 try { 249 for ( var key in sessionStorage ) { 250 if ( key.indexOf( 'wp-autosave-' ) > -1 ) { 251 sessionStorage.removeItem( key ); 252 } 253 } 254 } catch ( er ) {} 255 } 256 } 257 258 /** 259 * Check if element has class. 260 * 261 * @since 5.3.1 262 * 263 * @param {HTMLElement} element The HTML element. 264 * @param {string} className The class name. 265 * @return {boolean} Whether the element has the className. 266 */ 267 function hasClass( element, className ) { 268 var classNames; 269 270 if ( ! element ) { 271 return false; 272 } 273 274 if ( element.classList && element.classList.contains ) { 275 return element.classList.contains( className ); 276 } else if ( element.className ) { 277 classNames = element.className.split( ' ' ); 278 return classNames.indexOf( className ) > -1; 279 } 280 281 return false; 282 } 283 284 /** 285 * Add class to an element. 286 * 287 * @since 5.3.1 288 * 289 * @param {HTMLElement} element The HTML element. 290 * @param {string} className The class name. 291 */ 292 function addClass( element, className ) { 293 if ( ! element ) { 294 return; 295 } 296 297 if ( element.classList && element.classList.add ) { 298 element.classList.add( className ); 299 } else if ( ! hasClass( element, className ) ) { 300 if ( element.className ) { 301 element.className += ' '; 302 } 303 304 element.className += className; 305 } 306 307 var menuItemToggle = element.querySelector( 'a' ); 308 if ( className === 'hover' && menuItemToggle && menuItemToggle.hasAttribute( 'aria-expanded' ) ) { 309 menuItemToggle.setAttribute( 'aria-expanded', 'true' ); 310 } 311 } 312 313 /** 314 * Remove class from an element. 315 * 316 * @since 5.3.1 317 * 318 * @param {HTMLElement} element The HTML element. 319 * @param {string} className The class name. 320 */ 321 function removeClass( element, className ) { 322 var testName, 323 classes; 324 325 if ( ! element || ! hasClass( element, className ) ) { 326 return; 327 } 328 329 if ( element.classList && element.classList.remove ) { 330 element.classList.remove( className ); 331 } else { 332 testName = ' ' + className + ' '; 333 classes = ' ' + element.className + ' '; 334 335 while ( classes.indexOf( testName ) > -1 ) { 336 classes = classes.replace( testName, '' ); 337 } 338 339 element.className = classes.replace( /^[\s]+|[\s]+$/g, '' ); 340 } 341 342 var menuItemToggle = element.querySelector( 'a' ); 343 if ( className === 'hover' && menuItemToggle && menuItemToggle.hasAttribute( 'aria-expanded' ) ) { 344 menuItemToggle.setAttribute( 'aria-expanded', 'false' ); 345 } 346 } 347 348 /** 349 * Remove hover class for all menu items. 350 * 351 * @since 5.3.1 352 * 353 * @param {NodeList} topMenuItems All menu items. 354 */ 355 function removeAllHoverClass( topMenuItems ) { 356 if ( topMenuItems && topMenuItems.length ) { 357 for ( var i = 0; i < topMenuItems.length; i++ ) { 358 removeClass( topMenuItems[i], 'hover' ); 359 } 360 } 361 } 362 363 /** 364 * Scrolls to the top of the page. 365 * 366 * @since 3.4.0 367 * 368 * @param {Event} event The Click event. 369 * 370 * @return {void} 371 */ 372 function scrollToTop( event ) { 373 // Only scroll when clicking on the wpadminbar, not on menus or submenus. 374 if ( 375 event.target && 376 event.target.id !== 'wpadminbar' && 377 event.target.id !== 'wp-admin-bar-top-secondary' 378 ) { 379 return; 380 } 381 382 try { 383 window.scrollTo( { 384 top: -32, 385 left: 0, 386 behavior: 'smooth' 387 } ); 388 } catch ( er ) { 389 window.scrollTo( 0, -32 ); 390 } 391 } 392 393 /** 394 * Get closest Element. 395 * 396 * @since 5.3.1 397 * 398 * @param {HTMLElement} el Element to get parent. 399 * @param {string} selector CSS selector to match. 400 */ 401 function getClosest( el, selector ) { 402 if ( ! window.Element.prototype.matches ) { 403 // Polyfill from https://developer.mozilla.org/en-US/docs/Web/API/Element/matches. 404 window.Element.prototype.matches = 405 window.Element.prototype.matchesSelector || 406 window.Element.prototype.mozMatchesSelector || 407 window.Element.prototype.msMatchesSelector || 408 window.Element.prototype.oMatchesSelector || 409 window.Element.prototype.webkitMatchesSelector || 410 function( s ) { 411 var matches = ( this.document || this.ownerDocument ).querySelectorAll( s ), 412 i = matches.length; 413 414 while ( --i >= 0 && matches.item( i ) !== this ) { } 415 416 return i > -1; 417 }; 418 } 419 420 // Get the closest matching elent. 421 for ( ; el && el !== document; el = el.parentNode ) { 422 if ( el.matches( selector ) ) { 423 return el; 424 } 425 } 426 427 return null; 428 } 429 430 } )( document, window, navigator );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Dec 26 08:20:01 2024 | Cross-referenced by PHPXref |