| 4 | /** |
| 5 | * Admin bar with Vanilla JS, no external dependencies. |
| 6 | * |
| 7 | * @param {Object} document The document object. |
| 8 | * @param {Object} window The window object. |
| 9 | * @param {Object} navigator The navigator object. |
| 10 | * |
| 11 | * @return {void} |
| 12 | */ |
| 13 | ( function( document, window, navigator ) { |
| 14 | document.addEventListener( 'DOMContentLoaded', function() { |
| 15 | var adminBar = document.getElementById( 'wpadminbar' ), |
| 16 | topMenuItems = adminBar.querySelectorAll( 'li.menupop' ), |
| 17 | allMenuItems = adminBar.querySelectorAll( '.ab-item' ), |
| 18 | adminBarLogout = document.getElementById( 'wp-admin-bar-logout' ), |
| 19 | adminBarSearchForm = document.getElementById( 'adminbarsearch' ), |
| 20 | shortlink = document.getElementById( 'wp-admin-bar-get-shortlink' ), |
| 21 | skipLink = adminBar.querySelector( '.screen-reader-shortcut' ), |
| 22 | mobileEvent = /Mobile\/.+Safari/.test( navigator.userAgent ) ? 'touchstart' : 'click', |
| 23 | adminBarSearchInput, |
| 24 | i; |
5 | | /* jshint loopfunc: true */ |
6 | | // use jQuery and hoverIntent if loaded |
7 | | if ( typeof(jQuery) != 'undefined' ) { |
8 | | if ( typeof(jQuery.fn.hoverIntent) == 'undefined' ) { |
9 | | /* jshint ignore:start */ |
10 | | // hoverIntent v1.8.1 - Copy of wp-includes/js/hoverIntent.min.js |
11 | | !function(a){a.fn.hoverIntent=function(b,c,d){var e={interval:100,sensitivity:6,timeout:0};e="object"==typeof b?a.extend(e,b):a.isFunction(c)?a.extend(e,{over:b,out:c,selector:d}):a.extend(e,{over:b,out:b,selector:c});var f,g,h,i,j=function(a){f=a.pageX,g=a.pageY},k=function(b,c){return c.hoverIntent_t=clearTimeout(c.hoverIntent_t),Math.sqrt((h-f)*(h-f)+(i-g)*(i-g))<e.sensitivity?(a(c).off("mousemove.hoverIntent",j),c.hoverIntent_s=!0,e.over.apply(c,[b])):(h=f,i=g,c.hoverIntent_t=setTimeout(function(){k(b,c)},e.interval),void 0)},l=function(a,b){return b.hoverIntent_t=clearTimeout(b.hoverIntent_t),b.hoverIntent_s=!1,e.out.apply(b,[a])},m=function(b){var c=a.extend({},b),d=this;d.hoverIntent_t&&(d.hoverIntent_t=clearTimeout(d.hoverIntent_t)),"mouseenter"===b.type?(h=c.pageX,i=c.pageY,a(d).on("mousemove.hoverIntent",j),d.hoverIntent_s||(d.hoverIntent_t=setTimeout(function(){k(c,d)},e.interval))):(a(d).off("mousemove.hoverIntent",j),d.hoverIntent_s&&(d.hoverIntent_t=setTimeout(function(){l(c,d)},e.timeout)))};return this.on({"mouseenter.hoverIntent":m,"mouseleave.hoverIntent":m},e.selector)}}(jQuery); |
12 | | /* jshint ignore:end */ |
13 | | } |
14 | | jQuery(document).ready(function($){ |
15 | | var adminbar = $('#wpadminbar'), refresh, touchOpen, touchClose, disableHoverIntent = false; |
| 26 | /** |
| 27 | * Remove nojs class after the DOM is loaded. |
| 28 | */ |
| 29 | adminBar.classList.remove( 'nojs' ); |
| 30 | |
| 31 | if ( 'ontouchstart' in window ) { |
| 32 | /** |
| 33 | * Remove hover class when the user touches outside the menu items. |
| 34 | */ |
| 35 | document.body.addEventListener( mobileEvent, function( e ) { |
| 36 | if ( ! getClosest( e.target, 'li.menupop' ) ) { |
| 37 | removeAllHoverClass( topMenuItems ); |
| 38 | } |
| 39 | } ); |
| 40 | |
| 41 | /** |
| 42 | * Add listener for menu items to toggle hover class by touches. |
| 43 | * Remove the callback later for better performance. |
| 44 | */ |
| 45 | adminBar.addEventListener( 'touchstart', function bindMobileEvents() { |
| 46 | for ( var i = 0; i < topMenuItems.length; i++ ) { |
| 47 | topMenuItems[i].addEventListener( 'click', mobileHover.bind( null, topMenuItems ) ); |
| 48 | } |
| 49 | |
| 50 | adminBar.removeEventListener( 'touchstart', bindMobileEvents ); |
| 51 | } ); |
| 52 | } |
29 | | refresh = function(i, el){ |
30 | | var node = $(el), tab = node.attr('tabindex'); |
31 | | if ( tab ) |
32 | | node.attr('tabindex', '0').attr('tabindex', tab); |
33 | | }; |
| 57 | adminBar.addEventListener( 'click', scrollToTop ); |
| 58 | |
| 59 | for ( i = 0; i < topMenuItems.length; i++ ) { |
| 60 | /** |
| 61 | * Adds or removes the hover class based on the hover intent. |
| 62 | */ |
| 63 | hoverIntent( |
| 64 | topMenuItems[i], |
| 65 | addHoverClass.bind( null, topMenuItems[i] ), |
| 66 | removeHoverClass.bind( null, topMenuItems[i] ) |
| 67 | ).options( { |
| 68 | timeout: 180, |
| 69 | } ); |
| 70 | |
| 71 | /** |
| 72 | * Toggle hover class if the enter key is pressed. |
| 73 | */ |
| 74 | topMenuItems[i].addEventListener( 'keydown', toggleHoverIfEnter ); |
| 75 | } |
44 | | touchOpen = function(unbind) { |
45 | | adminbar.find('li.menupop').on('click.wp-mobile-hover', function(e) { |
46 | | var el = $(this); |
47 | | |
48 | | if ( el.parent().is('#wp-admin-bar-root-default') && !el.hasClass('hover') ) { |
49 | | e.preventDefault(); |
50 | | adminbar.find('li.menupop.hover').removeClass('hover'); |
51 | | el.addClass('hover'); |
52 | | } else if ( !el.hasClass('hover') ) { |
53 | | e.stopPropagation(); |
54 | | e.preventDefault(); |
55 | | el.addClass('hover'); |
56 | | } else if ( ! $( e.target ).closest( 'div' ).hasClass( 'ab-sub-wrapper' ) ) { |
57 | | // We're dealing with an already-touch-opened menu genericon (we know el.hasClass('hover')), |
58 | | // so close it on a second tap and prevent propag and defaults. See #29906 |
59 | | e.stopPropagation(); |
60 | | e.preventDefault(); |
61 | | el.removeClass('hover'); |
62 | | } |
| 80 | for ( i = 0; i < allMenuItems.length; i++ ) { |
| 81 | allMenuItems[i].addEventListener( 'keydown', removeHoverIfEscape ); |
| 82 | } |
64 | | if ( unbind ) { |
65 | | $('li.menupop').off('click.wp-mobile-hover'); |
66 | | disableHoverIntent = false; |
67 | | } |
68 | | }); |
69 | | }; |
| 84 | if ( adminBarSearchForm ) { |
| 85 | adminBarSearchInput = document.getElementById( 'adminbar-search' ); |
| 86 | |
| 87 | /** |
| 88 | * Adds the adminbar-focused class on focus. |
| 89 | */ |
| 90 | adminBarSearchInput.addEventListener( 'focus', function() { |
| 91 | adminBarSearchForm.classList.add( 'adminbar-focused' ); |
| 92 | } ); |
| 93 | |
| 94 | /** |
| 95 | * Removes the adminbar-focused class on blur. |
| 96 | */ |
| 97 | adminBarSearchInput.addEventListener( 'blur', function() { |
| 98 | adminBarSearchForm.classList.remove( 'adminbar-focused' ); |
| 99 | } ); |
| 100 | } |
89 | | // If clicked on the adminbar add the hoverclass, if clicked outside it remove |
90 | | // it. |
91 | | if ( 'ontouchstart' in window ) { |
92 | | adminbar.on('touchstart', function(){ |
93 | | touchOpen(true); |
94 | | disableHoverIntent = true; |
95 | | }); |
96 | | touchClose(); |
97 | | } else if ( /IEMobile\/[1-9]/.test(navigator.userAgent) ) { |
98 | | touchOpen(); |
99 | | touchClose(); |
100 | | } |
101 | | |
102 | | // Adds or removes the hover class based on the hover intent. |
103 | | adminbar.find('li.menupop').hoverIntent({ |
104 | | over: function() { |
105 | | if ( disableHoverIntent ) |
106 | | return; |
107 | | |
108 | | $(this).addClass('hover'); |
109 | | }, |
110 | | out: function() { |
111 | | if ( disableHoverIntent ) |
112 | | return; |
113 | | |
114 | | $(this).removeClass('hover'); |
115 | | }, |
116 | | timeout: 180, |
117 | | sensitivity: 7, |
118 | | interval: 100 |
119 | | }); |
120 | | |
121 | | // Prevents the toolbar from covering up content when a hash is present in the |
122 | | // URL. |
123 | | if ( window.location.hash ) |
| 111 | /** |
| 112 | * Prevents the toolbar from covering up content when a hash is present |
| 113 | * in the URL. |
| 114 | */ |
| 115 | if ( window.location.hash ) { |
127 | | * Handles the selected state of the Shortlink link. |
128 | | * |
129 | | * When the input is visible the link should be selected, when the input is |
130 | | * unfocused the link should be unselected. |
131 | | * |
132 | | * @param {Object} e The click event. |
133 | | * |
134 | | * @return {void} |
135 | | **/ |
136 | | $('#wp-admin-bar-get-shortlink').click(function(e){ |
137 | | e.preventDefault(); |
138 | | $(this).addClass('selected').children('.shortlink-input').blur(function(){ |
139 | | $(this).parents('#wp-admin-bar-get-shortlink').removeClass('selected'); |
140 | | }).focus().select(); |
141 | | }); |
| 120 | * Add no-font-face class to body if needed. |
| 121 | */ |
| 122 | if ( navigator.userAgent && document.body.className.indexOf( 'no-font-face' ) === -1 && |
| 123 | /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 ) ) { |
| 124 | document.body.className += ' no-font-face'; |
| 125 | } |
| 233 | wrapper = getClosest( e.target, '.menupop' ); |
| 234 | |
| 235 | if ( ! wrapper ) { |
| 236 | return; |
| 237 | } |
| 238 | |
| 239 | if ( hasHoverClass( wrapper ) ) { |
| 240 | removeHoverClass( wrapper ); |
| 241 | } else { |
| 242 | removeAllHoverClass( topMenuItems ); |
| 243 | addHoverClass( wrapper ); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | /** |
| 248 | * Handles the click on the Shortlink link in the adminbar. |
| 249 | * |
| 250 | * @since 3.1.0 |
| 251 | * @since 5.3.0 Use querySelector to clean up the function. |
| 252 | * |
| 253 | * @param {Event} e The click event. |
| 254 | * |
| 255 | * @return {boolean} Returns false to prevent default click behavior. |
| 256 | */ |
| 257 | function clickShortlink( e ) { |
| 258 | var wrapper = e.target.parentNode, |
| 259 | input = wrapper.querySelector( '.shortlink-input' ); |
| 260 | |
| 261 | // IE doesn't support preventDefault, and does support returnValue |
| 262 | if ( e.preventDefault ) { |
| 263 | e.preventDefault(); |
| 264 | } |
| 265 | e.returnValue = false; |
| 266 | |
| 267 | wrapper.classList.add( 'selected' ); |
| 268 | input.focus(); |
| 269 | input.select(); |
| 270 | input.onblur = function() { |
| 271 | wrapper.classList.remove( 'selected' ); |
| 272 | }; |
| 273 | |
| 274 | return false; |
| 275 | } |
| 276 | |
| 277 | /** |
| 278 | * Clear sessionStorage on logging out. |
| 279 | * |
| 280 | * @since 5.3.0 |
| 281 | */ |
| 282 | function emptySessionStorage() { |
281 | | document.body.className += ' no-font-face'; |
| 305 | /** |
| 306 | * Add hover class for menu item. |
| 307 | * |
| 308 | * @since 5.3.0 |
| 309 | * |
| 310 | * @param {HTMLElement} item Menu item Element. |
| 311 | */ |
| 312 | function addHoverClass( item ) { |
| 313 | item.classList.add( 'hover' ); |
| 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Remove hover class for menu item. |
| 318 | * |
| 319 | * @since 5.3.0 |
| 320 | * |
| 321 | * @param {HTMLElement} item Menu item Element. |
| 322 | */ |
| 323 | function removeHoverClass( item ) { |
| 324 | item.classList.remove( 'hover' ); |
| 325 | } |
| 326 | |
| 327 | /** |
| 328 | * Remove hover class for all menu items. |
| 329 | * |
| 330 | * @since 5.3.0 |
| 331 | * |
| 332 | * @param {NodeList} topMenuItems All menu items. |
| 333 | */ |
| 334 | function removeAllHoverClass( topMenuItems ) { |
| 335 | for ( var i = 0; i < topMenuItems.length; i++ ) { |
| 336 | if ( hasHoverClass( topMenuItems[i] ) ) { |
| 337 | removeHoverClass( topMenuItems[i] ); |
| 338 | } |
305 | | var addEvent = function( obj, type, fn ) { |
306 | | if ( obj && typeof obj.addEventListener === 'function' ) { |
307 | | obj.addEventListener( type, fn, false ); |
308 | | } else if ( obj && typeof obj.attachEvent === 'function' ) { |
309 | | obj.attachEvent( 'on' + type, function() { |
310 | | return fn.call( obj, window.event ); |
311 | | } ); |
312 | | } |
313 | | }, |
| 357 | if ( e.target.id != 'wpadminbar' && e.target.id != 'wp-admin-bar-top-secondary' ) { |
| 358 | return; |
| 359 | } |
336 | | /** |
337 | | * Adds the hoverclass to menu items. |
338 | | * |
339 | | * @since 3.1.0 |
340 | | * |
341 | | * @param {HTMLElement} t The HTML element. |
342 | | * |
343 | | * @return {void} |
344 | | */ |
345 | | addHoverClass = function(t) { |
346 | | var i, id, inA, hovering, ul, li, |
347 | | ancestors = [], |
348 | | ancestorLength = 0; |
349 | | |
350 | | // aB is adminbar. d is document. |
351 | | while ( t && t != aB && t != d ) { |
352 | | if ( 'LI' == t.nodeName.toUpperCase() ) { |
353 | | ancestors[ ancestors.length ] = t; |
354 | | id = getTOID(t); |
355 | | if ( id ) |
356 | | clearTimeout( id ); |
357 | | t.className = t.className ? ( t.className.replace(hc, '') + ' hover' ) : 'hover'; |
358 | | hovering = t; |
359 | | } |
360 | | t = t.parentNode; |
| 367 | speedStep = distance > 800 ? 130 : 100; |
| 368 | speed = Math.min( 12, Math.round( distance / speedStep ) ); |
| 369 | step = distance > 800 ? Math.round( distance / 30 ) : Math.round( distance / 20 ); |
| 370 | steps = []; |
| 371 | timer = 0; |
| 372 | |
| 373 | // Animate scrolling to the top of the page by generating steps to |
| 374 | // the top of the page and shifting to each step at a set interval. |
| 375 | while ( distance ) { |
| 376 | distance -= step; |
| 377 | if ( distance < 0 ) { |
| 378 | distance = 0; |
| 379 | } |
| 380 | steps.push( distance ); |
| 381 | |
| 382 | setTimeout( ( function( window, steps ) { |
| 383 | window.scrollTo( 0, steps.shift() ); |
| 384 | } ).bind( null, window, steps ), timer * speed ); |
| 385 | |
| 386 | timer++; |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | /** |
| 391 | * Get closest Element. |
| 392 | * |
| 393 | * @since 5.3.0 |
| 394 | * |
| 395 | * @param {HTMLElement} el Element to get parent. |
| 396 | * @param {string} selector CSS selector to match. |
| 397 | */ |
| 398 | function getClosest( el, selector ) { |
| 399 | if ( ! Element.prototype.matches ) { |
| 400 | Element.prototype.matches = |
| 401 | Element.prototype.matchesSelector || |
| 402 | Element.prototype.mozMatchesSelector || |
| 403 | Element.prototype.msMatchesSelector || |
| 404 | Element.prototype.oMatchesSelector || |
| 405 | Element.prototype.webkitMatchesSelector || |
| 406 | function( s ) { |
| 407 | var matches = ( this.document || this.ownerDocument ).querySelectorAll( s ), |
| 408 | i = matches.length; |
| 409 | while ( --i >= 0 && matches.item( i ) !== this ) { } |
| 410 | return i > -1; |
| 411 | }; |
| 412 | } |
| 413 | |
| 414 | // Get the closest matching elent |
| 415 | for ( ; el && el !== document; el = el.parentNode ) { |
| 416 | if ( el.matches( selector ) ) { |
| 417 | return el; |
363 | | // Removes any selected classes. |
364 | | if ( hovering && hovering.parentNode ) { |
365 | | ul = hovering.parentNode; |
366 | | if ( ul && 'UL' == ul.nodeName.toUpperCase() ) { |
367 | | i = ul.childNodes.length; |
368 | | while ( i-- ) { |
369 | | li = ul.childNodes[i]; |
370 | | if ( li != hovering ) |
371 | | li.className = li.className ? li.className.replace( rselected, '' ) : ''; |
372 | | } |
| 423 | /** |
| 424 | * Object.assign polyfill. |
| 425 | * |
| 426 | * @since 5.3.0 |
| 427 | * |
| 428 | * @param {Object} target Target object |
| 429 | */ |
| 430 | function _extends( target ) { |
| 431 | for ( var i = 1; i < arguments.length; i++ ) { |
| 432 | var source = arguments[i]; |
| 433 | |
| 434 | for ( var key in source ) { |
| 435 | if ( Object.prototype.hasOwnProperty.call( source, key ) ) { |
| 436 | target[key] = source[key]; |
386 | | if ( ! inA ) |
387 | | q[i][1].className = q[i][1].className ? q[i][1].className.replace(hc, '') : ''; |
| 444 | /** |
| 445 | * Vanilla JS HoverIntent. |
| 446 | * |
| 447 | * @since 5.3.0 |
| 448 | * |
| 449 | * @param {HTMLElement} el Link elent. |
| 450 | * @param {callback} onOver Callback when mouse is over the elent. |
| 451 | * @param {callback} onOut Callback when mouse leaves the elent. |
| 452 | */ |
| 453 | function hoverIntent( el, onOver, onOut ) { |
| 454 | var x, y, pX, pY, |
| 455 | mouseOver = false, |
| 456 | focused = false, |
| 457 | h = {}, |
| 458 | state = 0, |
| 459 | timer = 0, |
| 460 | |
| 461 | options = { |
| 462 | sensitivity: 7, |
| 463 | interval: 100, |
| 464 | timeout: 0, |
| 465 | handleFocus: false, |
| 466 | }; |
| 467 | |
| 468 | function delay( el, e ) { |
| 469 | if ( timer ) { |
| 470 | timer = clearTimeout( timer ); |
414 | | /** |
415 | | * Handles the click on the Shortlink link in the adminbar. |
416 | | * |
417 | | * @since 3.1.0 |
418 | | * |
419 | | * @param {Object} e The click event. |
420 | | * |
421 | | * @return {boolean} Returns false to prevent default click behavior. |
422 | | */ |
423 | | clickShortlink = function(e) { |
424 | | var i, l, node, |
425 | | t = e.target || e.srcElement; |
426 | | |
427 | | // Make t the shortlink menu item, or return. |
428 | | while ( true ) { |
429 | | // Check if we've gone past the shortlink node, |
430 | | // or if the user is clicking on the input. |
431 | | if ( ! t || t == d || t == aB ) |
432 | | return; |
433 | | // Check if we've found the shortlink node. |
434 | | if ( t.id && t.id == 'wp-admin-bar-get-shortlink' ) |
435 | | break; |
436 | | t = t.parentNode; |
437 | | } |
438 | | |
439 | | // IE doesn't support preventDefault, and does support returnValue |
440 | | if ( e.preventDefault ) |
441 | | e.preventDefault(); |
442 | | e.returnValue = false; |
443 | | |
444 | | if ( -1 == t.className.indexOf('selected') ) |
445 | | t.className += ' selected'; |
446 | | |
447 | | for ( i = 0, l = t.childNodes.length; i < l; i++ ) { |
448 | | node = t.childNodes[i]; |
449 | | if ( node.className && -1 != node.className.indexOf('shortlink-input') ) { |
450 | | node.focus(); |
451 | | node.select(); |
452 | | node.onblur = function() { |
453 | | t.className = t.className ? t.className.replace( rselected, '' ) : ''; |
454 | | }; |
455 | | break; |
456 | | } |
| 481 | function compare( el, e ) { |
| 482 | if ( timer ) { |
| 483 | timer = clearTimeout( timer ); |
| 484 | } |
| 485 | if ( Math.abs( pX - x ) + Math.abs( pY - y ) < options.sensitivity ) { |
| 486 | state = 1; |
| 487 | return focused ? undefined : onOver.call( el, e ); |
| 488 | } |
| 489 | pX = x; |
| 490 | pY = y; |
| 491 | timer = setTimeout( function() { |
| 492 | compare( el, e ); |
| 493 | }, options.interval ); |
| 494 | } |
| 495 | |
| 496 | // Public methods |
| 497 | h.options = function( opt ) { |
| 498 | var focusOptionChanged = opt.handleFocus !== options.handleFocus; |
| 499 | options = _extends( {}, options, opt ); |
| 500 | if ( focusOptionChanged ) { |
| 501 | options.handleFocus ? addFocus() : removeFocus(); |
461 | | /** |
462 | | * Scrolls to the top of the page. |
463 | | * |
464 | | * @since 3.4.0 |
465 | | * |
466 | | * @param {HTMLElement} t The HTML element. |
467 | | * |
468 | | * @return {void} |
469 | | */ |
470 | | scrollToTop = function(t) { |
471 | | var distance, speed, step, steps, timer, speed_step; |
| 506 | function dispatchOver( e ) { |
| 507 | mouseOver = true; |
| 508 | if ( timer ) { |
| 509 | timer = clearTimeout( timer ); |
| 510 | } |
| 511 | el.removeEventListener( 'mousemove', tracker, false ); |
513 | | addEvent(aB, 'mouseover', function(e) { |
514 | | addHoverClass( e.target || e.srcElement ); |
515 | | }); |
516 | | |
517 | | addEvent(aB, 'mouseout', function(e) { |
518 | | removeHoverClass( e.target || e.srcElement ); |
519 | | }); |
520 | | |
521 | | addEvent(aB, 'click', clickShortlink ); |
522 | | |
523 | | addEvent(aB, 'click', function(e) { |
524 | | scrollToTop( e.target || e.srcElement ); |
525 | | }); |
526 | | |
527 | | addEvent( document.getElementById('wp-admin-bar-logout'), 'click', function() { |
528 | | if ( 'sessionStorage' in window ) { |
529 | | try { |
530 | | for ( var key in sessionStorage ) { |
531 | | if ( key.indexOf('wp-autosave-') != -1 ) |
532 | | sessionStorage.removeItem(key); |
533 | | } |
534 | | } catch(e) {} |
535 | | } |
536 | | }); |
| 550 | function dispatchBlur( e ) { |
| 551 | if ( ! mouseOver && focused ) { |
| 552 | focused = false; |
| 553 | onOut.call( el, e ); |