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