You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1011 lines
27 KiB

  1. /* global setUserSetting, ajaxurl, commonL10n, alert, confirm, pagenow */
  2. var showNotice, adminMenu, columns, validateForm, screenMeta;
  3. ( function( $, window, undefined ) {
  4. var $document = $( document ),
  5. $window = $( window ),
  6. $body = $( document.body );
  7. // Removed in 3.3.
  8. // (perhaps) needed for back-compat
  9. adminMenu = {
  10. init : function() {},
  11. fold : function() {},
  12. restoreMenuState : function() {},
  13. toggle : function() {},
  14. favorites : function() {}
  15. };
  16. // show/hide/save table columns
  17. columns = {
  18. init : function() {
  19. var that = this;
  20. $('.hide-column-tog', '#adv-settings').click( function() {
  21. var $t = $(this), column = $t.val();
  22. if ( $t.prop('checked') )
  23. that.checked(column);
  24. else
  25. that.unchecked(column);
  26. columns.saveManageColumnsState();
  27. });
  28. },
  29. saveManageColumnsState : function() {
  30. var hidden = this.hidden();
  31. $.post(ajaxurl, {
  32. action: 'hidden-columns',
  33. hidden: hidden,
  34. screenoptionnonce: $('#screenoptionnonce').val(),
  35. page: pagenow
  36. });
  37. },
  38. checked : function(column) {
  39. $('.column-' + column).removeClass( 'hidden' );
  40. this.colSpanChange(+1);
  41. },
  42. unchecked : function(column) {
  43. $('.column-' + column).addClass( 'hidden' );
  44. this.colSpanChange(-1);
  45. },
  46. hidden : function() {
  47. return $( '.manage-column[id]' ).filter( ':hidden' ).map(function() {
  48. return this.id;
  49. }).get().join( ',' );
  50. },
  51. useCheckboxesForHidden : function() {
  52. this.hidden = function(){
  53. return $('.hide-column-tog').not(':checked').map(function() {
  54. var id = this.id;
  55. return id.substring( id, id.length - 5 );
  56. }).get().join(',');
  57. };
  58. },
  59. colSpanChange : function(diff) {
  60. var $t = $('table').find('.colspanchange'), n;
  61. if ( !$t.length )
  62. return;
  63. n = parseInt( $t.attr('colspan'), 10 ) + diff;
  64. $t.attr('colspan', n.toString());
  65. }
  66. };
  67. $document.ready(function(){columns.init();});
  68. validateForm = function( form ) {
  69. return !$( form )
  70. .find( '.form-required' )
  71. .filter( function() { return $( 'input:visible', this ).val() === ''; } )
  72. .addClass( 'form-invalid' )
  73. .find( 'input:visible' )
  74. .change( function() { $( this ).closest( '.form-invalid' ).removeClass( 'form-invalid' ); } )
  75. .length;
  76. };
  77. // stub for doing better warnings
  78. showNotice = {
  79. warn : function() {
  80. var msg = commonL10n.warnDelete || '';
  81. if ( confirm(msg) ) {
  82. return true;
  83. }
  84. return false;
  85. },
  86. note : function(text) {
  87. alert(text);
  88. }
  89. };
  90. screenMeta = {
  91. element: null, // #screen-meta
  92. toggles: null, // .screen-meta-toggle
  93. page: null, // #wpcontent
  94. init: function() {
  95. this.element = $('#screen-meta');
  96. this.toggles = $( '#screen-meta-links' ).find( '.show-settings' );
  97. this.page = $('#wpcontent');
  98. this.toggles.click( this.toggleEvent );
  99. },
  100. toggleEvent: function() {
  101. var panel = $( '#' + $( this ).attr( 'aria-controls' ) );
  102. if ( !panel.length )
  103. return;
  104. if ( panel.is(':visible') )
  105. screenMeta.close( panel, $(this) );
  106. else
  107. screenMeta.open( panel, $(this) );
  108. },
  109. open: function( panel, button ) {
  110. $( '#screen-meta-links' ).find( '.screen-meta-toggle' ).not( button.parent() ).css( 'visibility', 'hidden' );
  111. panel.parent().show();
  112. panel.slideDown( 'fast', function() {
  113. panel.focus();
  114. button.addClass( 'screen-meta-active' ).attr( 'aria-expanded', true );
  115. });
  116. $document.trigger( 'screen:options:open' );
  117. },
  118. close: function( panel, button ) {
  119. panel.slideUp( 'fast', function() {
  120. button.removeClass( 'screen-meta-active' ).attr( 'aria-expanded', false );
  121. $('.screen-meta-toggle').css('visibility', '');
  122. panel.parent().hide();
  123. });
  124. $document.trigger( 'screen:options:close' );
  125. }
  126. };
  127. /**
  128. * Help tabs.
  129. */
  130. $('.contextual-help-tabs').delegate('a', 'click', function(e) {
  131. var link = $(this),
  132. panel;
  133. e.preventDefault();
  134. // Don't do anything if the click is for the tab already showing.
  135. if ( link.is('.active a') )
  136. return false;
  137. // Links
  138. $('.contextual-help-tabs .active').removeClass('active');
  139. link.parent('li').addClass('active');
  140. panel = $( link.attr('href') );
  141. // Panels
  142. $('.help-tab-content').not( panel ).removeClass('active').hide();
  143. panel.addClass('active').show();
  144. });
  145. $document.ready( function() {
  146. var checks, first, last, checked, sliced, mobileEvent, transitionTimeout, focusedRowActions,
  147. lastClicked = false,
  148. pageInput = $('input.current-page'),
  149. currentPage = pageInput.val(),
  150. isIOS = /iPhone|iPad|iPod/.test( navigator.userAgent ),
  151. isAndroid = navigator.userAgent.indexOf( 'Android' ) !== -1,
  152. isIE8 = $( document.documentElement ).hasClass( 'ie8' ),
  153. $adminMenuWrap = $( '#adminmenuwrap' ),
  154. $wpwrap = $( '#wpwrap' ),
  155. $adminmenu = $( '#adminmenu' ),
  156. $overlay = $( '#wp-responsive-overlay' ),
  157. $toolbar = $( '#wp-toolbar' ),
  158. $toolbarPopups = $toolbar.find( 'a[aria-haspopup="true"]' ),
  159. $sortables = $('.meta-box-sortables'),
  160. wpResponsiveActive = false,
  161. $adminbar = $( '#wpadminbar' ),
  162. lastScrollPosition = 0,
  163. pinnedMenuTop = false,
  164. pinnedMenuBottom = false,
  165. menuTop = 0,
  166. menuState,
  167. menuIsPinned = false,
  168. height = {
  169. window: $window.height(),
  170. wpwrap: $wpwrap.height(),
  171. adminbar: $adminbar.height(),
  172. menu: $adminMenuWrap.height()
  173. },
  174. $headerEnd = $( '.wp-header-end' );
  175. // when the menu is folded, make the fly-out submenu header clickable
  176. $adminmenu.on('click.wp-submenu-head', '.wp-submenu-head', function(e){
  177. $(e.target).parent().siblings('a').get(0).click();
  178. });
  179. $( '#collapse-button' ).on( 'click.collapse-menu', function() {
  180. var viewportWidth = getViewportWidth() || 961;
  181. // reset any compensation for submenus near the bottom of the screen
  182. $('#adminmenu div.wp-submenu').css('margin-top', '');
  183. if ( viewportWidth < 960 ) {
  184. if ( $body.hasClass('auto-fold') ) {
  185. $body.removeClass('auto-fold').removeClass('folded');
  186. setUserSetting('unfold', 1);
  187. setUserSetting('mfold', 'o');
  188. menuState = 'open';
  189. } else {
  190. $body.addClass('auto-fold');
  191. setUserSetting('unfold', 0);
  192. menuState = 'folded';
  193. }
  194. } else {
  195. if ( $body.hasClass('folded') ) {
  196. $body.removeClass('folded');
  197. setUserSetting('mfold', 'o');
  198. menuState = 'open';
  199. } else {
  200. $body.addClass('folded');
  201. setUserSetting('mfold', 'f');
  202. menuState = 'folded';
  203. }
  204. }
  205. $document.trigger( 'wp-collapse-menu', { state: menuState } );
  206. });
  207. // Handle the `aria-haspopup` attribute on the current menu item when it has a sub-menu.
  208. function currentMenuItemHasPopup() {
  209. var $current = $( 'a.wp-has-current-submenu' );
  210. if ( 'folded' === menuState ) {
  211. // When folded or auto-folded and not responsive view, the current menu item does have a fly-out sub-menu.
  212. $current.attr( 'aria-haspopup', 'true' );
  213. } else {
  214. // When expanded or in responsive view, reset aria-haspopup.
  215. $current.attr( 'aria-haspopup', 'false' );
  216. }
  217. }
  218. $document.on( 'wp-menu-state-set wp-collapse-menu wp-responsive-activate wp-responsive-deactivate', currentMenuItemHasPopup );
  219. /**
  220. * Ensure an admin submenu is within the visual viewport.
  221. *
  222. * @since 4.1.0
  223. *
  224. * @param {jQuery} $menuItem The parent menu item containing the submenu.
  225. */
  226. function adjustSubmenu( $menuItem ) {
  227. var bottomOffset, pageHeight, adjustment, theFold, menutop, wintop, maxtop,
  228. $submenu = $menuItem.find( '.wp-submenu' );
  229. menutop = $menuItem.offset().top;
  230. wintop = $window.scrollTop();
  231. maxtop = menutop - wintop - 30; // max = make the top of the sub almost touch admin bar
  232. bottomOffset = menutop + $submenu.height() + 1; // Bottom offset of the menu
  233. pageHeight = $wpwrap.height(); // Height of the entire page
  234. adjustment = 60 + bottomOffset - pageHeight;
  235. theFold = $window.height() + wintop - 50; // The fold
  236. if ( theFold < ( bottomOffset - adjustment ) ) {
  237. adjustment = bottomOffset - theFold;
  238. }
  239. if ( adjustment > maxtop ) {
  240. adjustment = maxtop;
  241. }
  242. if ( adjustment > 1 ) {
  243. $submenu.css( 'margin-top', '-' + adjustment + 'px' );
  244. } else {
  245. $submenu.css( 'margin-top', '' );
  246. }
  247. }
  248. if ( 'ontouchstart' in window || /IEMobile\/[1-9]/.test(navigator.userAgent) ) { // touch screen device
  249. // iOS Safari works with touchstart, the rest work with click
  250. mobileEvent = isIOS ? 'touchstart' : 'click';
  251. // close any open submenus when touch/click is not on the menu
  252. $body.on( mobileEvent+'.wp-mobile-hover', function(e) {
  253. if ( $adminmenu.data('wp-responsive') ) {
  254. return;
  255. }
  256. if ( ! $( e.target ).closest( '#adminmenu' ).length ) {
  257. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  258. }
  259. });
  260. $adminmenu.find( 'a.wp-has-submenu' ).on( mobileEvent + '.wp-mobile-hover', function( event ) {
  261. var $menuItem = $(this).parent();
  262. if ( $adminmenu.data( 'wp-responsive' ) ) {
  263. return;
  264. }
  265. // Show the sub instead of following the link if:
  266. // - the submenu is not open
  267. // - the submenu is not shown inline or the menu is not folded
  268. if ( ! $menuItem.hasClass( 'opensub' ) && ( ! $menuItem.hasClass( 'wp-menu-open' ) || $menuItem.width() < 40 ) ) {
  269. event.preventDefault();
  270. adjustSubmenu( $menuItem );
  271. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  272. $menuItem.addClass('opensub');
  273. }
  274. });
  275. }
  276. if ( ! isIOS && ! isAndroid ) {
  277. $adminmenu.find( 'li.wp-has-submenu' ).hoverIntent({
  278. over: function() {
  279. var $menuItem = $( this ),
  280. $submenu = $menuItem.find( '.wp-submenu' ),
  281. top = parseInt( $submenu.css( 'top' ), 10 );
  282. if ( isNaN( top ) || top > -5 ) { // the submenu is visible
  283. return;
  284. }
  285. if ( $adminmenu.data( 'wp-responsive' ) ) {
  286. // The menu is in responsive mode, bail
  287. return;
  288. }
  289. adjustSubmenu( $menuItem );
  290. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  291. $menuItem.addClass( 'opensub' );
  292. },
  293. out: function(){
  294. if ( $adminmenu.data( 'wp-responsive' ) ) {
  295. // The menu is in responsive mode, bail
  296. return;
  297. }
  298. $( this ).removeClass( 'opensub' ).find( '.wp-submenu' ).css( 'margin-top', '' );
  299. },
  300. timeout: 200,
  301. sensitivity: 7,
  302. interval: 90
  303. });
  304. $adminmenu.on( 'focus.adminmenu', '.wp-submenu a', function( event ) {
  305. if ( $adminmenu.data( 'wp-responsive' ) ) {
  306. // The menu is in responsive mode, bail
  307. return;
  308. }
  309. $( event.target ).closest( 'li.menu-top' ).addClass( 'opensub' );
  310. }).on( 'blur.adminmenu', '.wp-submenu a', function( event ) {
  311. if ( $adminmenu.data( 'wp-responsive' ) ) {
  312. return;
  313. }
  314. $( event.target ).closest( 'li.menu-top' ).removeClass( 'opensub' );
  315. }).find( 'li.wp-has-submenu.wp-not-current-submenu' ).on( 'focusin.adminmenu', function() {
  316. adjustSubmenu( $( this ) );
  317. });
  318. }
  319. /*
  320. * The `.below-h2` class is here just for backward compatibility with plugins
  321. * that are (incorrectly) using it. Do not use. Use `.inline` instead. See #34570.
  322. * If '.wp-header-end' is found, append the notices after it otherwise
  323. * after the first h1 or h2 heading found within the main content.
  324. */
  325. if ( ! $headerEnd.length ) {
  326. $headerEnd = $( '.wrap h1, .wrap h2' ).first();
  327. }
  328. $( 'div.updated, div.error, div.notice' ).not( '.inline, .below-h2' ).insertAfter( $headerEnd );
  329. // Make notices dismissible
  330. function makeNoticesDismissible() {
  331. $( '.notice.is-dismissible' ).each( function() {
  332. var $el = $( this ),
  333. $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
  334. btnText = commonL10n.dismiss || '';
  335. // Ensure plain text
  336. $button.find( '.screen-reader-text' ).text( btnText );
  337. $button.on( 'click.wp-dismiss-notice', function( event ) {
  338. event.preventDefault();
  339. $el.fadeTo( 100, 0, function() {
  340. $el.slideUp( 100, function() {
  341. $el.remove();
  342. });
  343. });
  344. });
  345. $el.append( $button );
  346. });
  347. }
  348. $document.on( 'wp-updates-notice-added wp-plugin-install-error wp-plugin-update-error wp-plugin-delete-error wp-theme-install-error wp-theme-delete-error', makeNoticesDismissible );
  349. // Init screen meta
  350. screenMeta.init();
  351. // This event needs to be delegated. Ticket #37973.
  352. $body.on( 'click', 'tbody > .check-column :checkbox', function( event ) {
  353. // Shift click to select a range of checkboxes.
  354. if ( 'undefined' == event.shiftKey ) { return true; }
  355. if ( event.shiftKey ) {
  356. if ( !lastClicked ) { return true; }
  357. checks = $( lastClicked ).closest( 'form' ).find( ':checkbox' ).filter( ':visible:enabled' );
  358. first = checks.index( lastClicked );
  359. last = checks.index( this );
  360. checked = $(this).prop('checked');
  361. if ( 0 < first && 0 < last && first != last ) {
  362. sliced = ( last > first ) ? checks.slice( first, last ) : checks.slice( last, first );
  363. sliced.prop( 'checked', function() {
  364. if ( $(this).closest('tr').is(':visible') )
  365. return checked;
  366. return false;
  367. });
  368. }
  369. }
  370. lastClicked = this;
  371. // Toggle the "Select all" checkboxes depending if the other ones are all checked or not.
  372. var unchecked = $(this).closest('tbody').find(':checkbox').filter(':visible:enabled').not(':checked');
  373. $(this).closest('table').children('thead, tfoot').find(':checkbox').prop('checked', function() {
  374. return ( 0 === unchecked.length );
  375. });
  376. return true;
  377. });
  378. // This event needs to be delegated. Ticket #37973.
  379. $body.on( 'click.wp-toggle-checkboxes', 'thead .check-column :checkbox, tfoot .check-column :checkbox', function( event ) {
  380. var $this = $(this),
  381. $table = $this.closest( 'table' ),
  382. controlChecked = $this.prop('checked'),
  383. toggle = event.shiftKey || $this.data('wp-toggle');
  384. $table.children( 'tbody' ).filter(':visible')
  385. .children().children('.check-column').find(':checkbox')
  386. .prop('checked', function() {
  387. if ( $(this).is(':hidden,:disabled') ) {
  388. return false;
  389. }
  390. if ( toggle ) {
  391. return ! $(this).prop( 'checked' );
  392. } else if ( controlChecked ) {
  393. return true;
  394. }
  395. return false;
  396. });
  397. $table.children('thead, tfoot').filter(':visible')
  398. .children().children('.check-column').find(':checkbox')
  399. .prop('checked', function() {
  400. if ( toggle ) {
  401. return false;
  402. } else if ( controlChecked ) {
  403. return true;
  404. }
  405. return false;
  406. });
  407. });
  408. // Show row actions on keyboard focus of its parent container element or any other elements contained within
  409. $( '#wpbody-content' ).on({
  410. focusin: function() {
  411. clearTimeout( transitionTimeout );
  412. focusedRowActions = $( this ).find( '.row-actions' );
  413. // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
  414. $( '.row-actions' ).not( this ).removeClass( 'visible' );
  415. focusedRowActions.addClass( 'visible' );
  416. },
  417. focusout: function() {
  418. // Tabbing between post title and .row-actions links needs a brief pause, otherwise
  419. // the .row-actions div gets hidden in transit in some browsers (ahem, Firefox).
  420. transitionTimeout = setTimeout( function() {
  421. focusedRowActions.removeClass( 'visible' );
  422. }, 30 );
  423. }
  424. }, '.has-row-actions' );
  425. // Toggle list table rows on small screens
  426. $( 'tbody' ).on( 'click', '.toggle-row', function() {
  427. $( this ).closest( 'tr' ).toggleClass( 'is-expanded' );
  428. });
  429. $('#default-password-nag-no').click( function() {
  430. setUserSetting('default_password_nag', 'hide');
  431. $('div.default-password-nag').hide();
  432. return false;
  433. });
  434. // tab in textareas
  435. $('#newcontent').bind('keydown.wpevent_InsertTab', function(e) {
  436. var el = e.target, selStart, selEnd, val, scroll, sel;
  437. if ( e.keyCode == 27 ) { // escape key
  438. // when pressing Escape: Opera 12 and 27 blur form fields, IE 8 clears them
  439. e.preventDefault();
  440. $(el).data('tab-out', true);
  441. return;
  442. }
  443. if ( e.keyCode != 9 || e.ctrlKey || e.altKey || e.shiftKey ) // tab key
  444. return;
  445. if ( $(el).data('tab-out') ) {
  446. $(el).data('tab-out', false);
  447. return;
  448. }
  449. selStart = el.selectionStart;
  450. selEnd = el.selectionEnd;
  451. val = el.value;
  452. if ( document.selection ) {
  453. el.focus();
  454. sel = document.selection.createRange();
  455. sel.text = '\t';
  456. } else if ( selStart >= 0 ) {
  457. scroll = this.scrollTop;
  458. el.value = val.substring(0, selStart).concat('\t', val.substring(selEnd) );
  459. el.selectionStart = el.selectionEnd = selStart + 1;
  460. this.scrollTop = scroll;
  461. }
  462. if ( e.stopPropagation )
  463. e.stopPropagation();
  464. if ( e.preventDefault )
  465. e.preventDefault();
  466. });
  467. if ( pageInput.length ) {
  468. pageInput.closest('form').submit( function() {
  469. // Reset paging var for new filters/searches but not for bulk actions. See #17685.
  470. if ( $('select[name="action"]').val() == -1 && $('select[name="action2"]').val() == -1 && pageInput.val() == currentPage )
  471. pageInput.val('1');
  472. });
  473. }
  474. $('.search-box input[type="search"], .search-box input[type="submit"]').mousedown(function () {
  475. $('select[name^="action"]').val('-1');
  476. });
  477. // Scroll into view when focused
  478. $('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){
  479. if ( e.target.scrollIntoView )
  480. e.target.scrollIntoView(false);
  481. });
  482. // Disable upload buttons until files are selected
  483. (function(){
  484. var button, input, form = $('form.wp-upload-form');
  485. if ( ! form.length )
  486. return;
  487. button = form.find('input[type="submit"]');
  488. input = form.find('input[type="file"]');
  489. function toggleUploadButton() {
  490. button.prop('disabled', '' === input.map( function() {
  491. return $(this).val();
  492. }).get().join(''));
  493. }
  494. toggleUploadButton();
  495. input.on('change', toggleUploadButton);
  496. })();
  497. function pinMenu( event ) {
  498. var windowPos = $window.scrollTop(),
  499. resizing = ! event || event.type !== 'scroll';
  500. if ( isIOS || isIE8 || $adminmenu.data( 'wp-responsive' ) ) {
  501. return;
  502. }
  503. if ( height.menu + height.adminbar < height.window ||
  504. height.menu + height.adminbar + 20 > height.wpwrap ) {
  505. unpinMenu();
  506. return;
  507. }
  508. menuIsPinned = true;
  509. if ( height.menu + height.adminbar > height.window ) {
  510. // Check for overscrolling
  511. if ( windowPos < 0 ) {
  512. if ( ! pinnedMenuTop ) {
  513. pinnedMenuTop = true;
  514. pinnedMenuBottom = false;
  515. $adminMenuWrap.css({
  516. position: 'fixed',
  517. top: '',
  518. bottom: ''
  519. });
  520. }
  521. return;
  522. } else if ( windowPos + height.window > $document.height() - 1 ) {
  523. if ( ! pinnedMenuBottom ) {
  524. pinnedMenuBottom = true;
  525. pinnedMenuTop = false;
  526. $adminMenuWrap.css({
  527. position: 'fixed',
  528. top: '',
  529. bottom: 0
  530. });
  531. }
  532. return;
  533. }
  534. if ( windowPos > lastScrollPosition ) {
  535. // Scrolling down
  536. if ( pinnedMenuTop ) {
  537. // let it scroll
  538. pinnedMenuTop = false;
  539. menuTop = $adminMenuWrap.offset().top - height.adminbar - ( windowPos - lastScrollPosition );
  540. if ( menuTop + height.menu + height.adminbar < windowPos + height.window ) {
  541. menuTop = windowPos + height.window - height.menu - height.adminbar;
  542. }
  543. $adminMenuWrap.css({
  544. position: 'absolute',
  545. top: menuTop,
  546. bottom: ''
  547. });
  548. } else if ( ! pinnedMenuBottom && $adminMenuWrap.offset().top + height.menu < windowPos + height.window ) {
  549. // pin the bottom
  550. pinnedMenuBottom = true;
  551. $adminMenuWrap.css({
  552. position: 'fixed',
  553. top: '',
  554. bottom: 0
  555. });
  556. }
  557. } else if ( windowPos < lastScrollPosition ) {
  558. // Scrolling up
  559. if ( pinnedMenuBottom ) {
  560. // let it scroll
  561. pinnedMenuBottom = false;
  562. menuTop = $adminMenuWrap.offset().top - height.adminbar + ( lastScrollPosition - windowPos );
  563. if ( menuTop + height.menu > windowPos + height.window ) {
  564. menuTop = windowPos;
  565. }
  566. $adminMenuWrap.css({
  567. position: 'absolute',
  568. top: menuTop,
  569. bottom: ''
  570. });
  571. } else if ( ! pinnedMenuTop && $adminMenuWrap.offset().top >= windowPos + height.adminbar ) {
  572. // pin the top
  573. pinnedMenuTop = true;
  574. $adminMenuWrap.css({
  575. position: 'fixed',
  576. top: '',
  577. bottom: ''
  578. });
  579. }
  580. } else if ( resizing ) {
  581. // Resizing
  582. pinnedMenuTop = pinnedMenuBottom = false;
  583. menuTop = windowPos + height.window - height.menu - height.adminbar - 1;
  584. if ( menuTop > 0 ) {
  585. $adminMenuWrap.css({
  586. position: 'absolute',
  587. top: menuTop,
  588. bottom: ''
  589. });
  590. } else {
  591. unpinMenu();
  592. }
  593. }
  594. }
  595. lastScrollPosition = windowPos;
  596. }
  597. function resetHeights() {
  598. height = {
  599. window: $window.height(),
  600. wpwrap: $wpwrap.height(),
  601. adminbar: $adminbar.height(),
  602. menu: $adminMenuWrap.height()
  603. };
  604. }
  605. function unpinMenu() {
  606. if ( isIOS || ! menuIsPinned ) {
  607. return;
  608. }
  609. pinnedMenuTop = pinnedMenuBottom = menuIsPinned = false;
  610. $adminMenuWrap.css({
  611. position: '',
  612. top: '',
  613. bottom: ''
  614. });
  615. }
  616. function setPinMenu() {
  617. resetHeights();
  618. if ( $adminmenu.data('wp-responsive') ) {
  619. $body.removeClass( 'sticky-menu' );
  620. unpinMenu();
  621. } else if ( height.menu + height.adminbar > height.window ) {
  622. pinMenu();
  623. $body.removeClass( 'sticky-menu' );
  624. } else {
  625. $body.addClass( 'sticky-menu' );
  626. unpinMenu();
  627. }
  628. }
  629. if ( ! isIOS ) {
  630. $window.on( 'scroll.pin-menu', pinMenu );
  631. $document.on( 'tinymce-editor-init.pin-menu', function( event, editor ) {
  632. editor.on( 'wp-autoresize', resetHeights );
  633. });
  634. }
  635. window.wpResponsive = {
  636. init: function() {
  637. var self = this;
  638. // Modify functionality based on custom activate/deactivate event
  639. $document.on( 'wp-responsive-activate.wp-responsive', function() {
  640. self.activate();
  641. }).on( 'wp-responsive-deactivate.wp-responsive', function() {
  642. self.deactivate();
  643. });
  644. $( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' );
  645. // Toggle sidebar when toggle is clicked
  646. $( '#wp-admin-bar-menu-toggle' ).on( 'click.wp-responsive', function( event ) {
  647. event.preventDefault();
  648. // close any open toolbar submenus
  649. $adminbar.find( '.hover' ).removeClass( 'hover' );
  650. $wpwrap.toggleClass( 'wp-responsive-open' );
  651. if ( $wpwrap.hasClass( 'wp-responsive-open' ) ) {
  652. $(this).find('a').attr( 'aria-expanded', 'true' );
  653. $( '#adminmenu a:first' ).focus();
  654. } else {
  655. $(this).find('a').attr( 'aria-expanded', 'false' );
  656. }
  657. } );
  658. // Add menu events
  659. $adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) {
  660. if ( ! $adminmenu.data('wp-responsive') ) {
  661. return;
  662. }
  663. $( this ).parent( 'li' ).toggleClass( 'selected' );
  664. event.preventDefault();
  665. });
  666. self.trigger();
  667. $document.on( 'wp-window-resized.wp-responsive', $.proxy( this.trigger, this ) );
  668. // This needs to run later as UI Sortable may be initialized later on $(document).ready()
  669. $window.on( 'load.wp-responsive', function() {
  670. var width = navigator.userAgent.indexOf('AppleWebKit/') > -1 ? $window.width() : window.innerWidth;
  671. if ( width <= 782 ) {
  672. self.disableSortables();
  673. }
  674. });
  675. },
  676. activate: function() {
  677. setPinMenu();
  678. if ( ! $body.hasClass( 'auto-fold' ) ) {
  679. $body.addClass( 'auto-fold' );
  680. }
  681. $adminmenu.data( 'wp-responsive', 1 );
  682. this.disableSortables();
  683. },
  684. deactivate: function() {
  685. setPinMenu();
  686. $adminmenu.removeData('wp-responsive');
  687. this.enableSortables();
  688. },
  689. trigger: function() {
  690. var viewportWidth = getViewportWidth();
  691. // Exclude IE < 9, it doesn't support @media CSS rules.
  692. if ( ! viewportWidth ) {
  693. return;
  694. }
  695. if ( viewportWidth <= 782 ) {
  696. if ( ! wpResponsiveActive ) {
  697. $document.trigger( 'wp-responsive-activate' );
  698. wpResponsiveActive = true;
  699. }
  700. } else {
  701. if ( wpResponsiveActive ) {
  702. $document.trigger( 'wp-responsive-deactivate' );
  703. wpResponsiveActive = false;
  704. }
  705. }
  706. if ( viewportWidth <= 480 ) {
  707. this.enableOverlay();
  708. } else {
  709. this.disableOverlay();
  710. }
  711. },
  712. enableOverlay: function() {
  713. if ( $overlay.length === 0 ) {
  714. $overlay = $( '<div id="wp-responsive-overlay"></div>' )
  715. .insertAfter( '#wpcontent' )
  716. .hide()
  717. .on( 'click.wp-responsive', function() {
  718. $toolbar.find( '.menupop.hover' ).removeClass( 'hover' );
  719. $( this ).hide();
  720. });
  721. }
  722. $toolbarPopups.on( 'click.wp-responsive', function() {
  723. $overlay.show();
  724. });
  725. },
  726. disableOverlay: function() {
  727. $toolbarPopups.off( 'click.wp-responsive' );
  728. $overlay.hide();
  729. },
  730. disableSortables: function() {
  731. if ( $sortables.length ) {
  732. try {
  733. $sortables.sortable('disable');
  734. } catch(e) {}
  735. }
  736. },
  737. enableSortables: function() {
  738. if ( $sortables.length ) {
  739. try {
  740. $sortables.sortable('enable');
  741. } catch(e) {}
  742. }
  743. }
  744. };
  745. // Add an ARIA role `button` to elements that behave like UI controls when JavaScript is on.
  746. function aria_button_if_js() {
  747. $( '.aria-button-if-js' ).attr( 'role', 'button' );
  748. }
  749. $( document ).ajaxComplete( function() {
  750. aria_button_if_js();
  751. });
  752. /**
  753. * @summary Get the viewport width.
  754. *
  755. * @since 4.7.0
  756. *
  757. * @returns {number|boolean} The current viewport width or false if the
  758. * browser doesn't support innerWidth (IE < 9).
  759. */
  760. function getViewportWidth() {
  761. var viewportWidth = false;
  762. if ( window.innerWidth ) {
  763. // On phones, window.innerWidth is affected by zooming.
  764. viewportWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
  765. }
  766. return viewportWidth;
  767. }
  768. /**
  769. * @summary Set the admin menu collapsed/expanded state.
  770. *
  771. * Sets the global variable `menuState` and triggers a custom event passing
  772. * the current menu state.
  773. *
  774. * @since 4.7.0
  775. *
  776. * @returns {void}
  777. */
  778. function setMenuState() {
  779. var viewportWidth = getViewportWidth() || 961;
  780. if ( viewportWidth <= 782 ) {
  781. menuState = 'responsive';
  782. } else if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && viewportWidth <= 960 && viewportWidth > 782 ) ) {
  783. menuState = 'folded';
  784. } else {
  785. menuState = 'open';
  786. }
  787. $document.trigger( 'wp-menu-state-set', { state: menuState } );
  788. }
  789. // Set the menu state when the window gets resized.
  790. $document.on( 'wp-window-resized.set-menu-state', setMenuState );
  791. /**
  792. * @summary Set ARIA attributes on the collapse/expand menu button.
  793. *
  794. * When the admin menu is open or folded, updates the `aria-expanded` and
  795. * `aria-label` attributes of the button to give feedback to assistive
  796. * technologies. In the responsive view, the button is always hidden.
  797. *
  798. * @since 4.7.0
  799. *
  800. * @returns {void}
  801. */
  802. $document.on( 'wp-menu-state-set wp-collapse-menu', function( event, eventData ) {
  803. var $collapseButton = $( '#collapse-button' ),
  804. ariaExpanded = 'true',
  805. ariaLabelText = commonL10n.collapseMenu;
  806. if ( 'folded' === eventData.state ) {
  807. ariaExpanded = 'false';
  808. ariaLabelText = commonL10n.expandMenu;
  809. }
  810. $collapseButton.attr({
  811. 'aria-expanded': ariaExpanded,
  812. 'aria-label': ariaLabelText
  813. });
  814. });
  815. window.wpResponsive.init();
  816. setPinMenu();
  817. setMenuState();
  818. currentMenuItemHasPopup();
  819. makeNoticesDismissible();
  820. aria_button_if_js();
  821. $document.on( 'wp-pin-menu wp-window-resized.pin-menu postboxes-columnchange.pin-menu postbox-toggled.pin-menu wp-collapse-menu.pin-menu wp-scroll-start.pin-menu', setPinMenu );
  822. // Set initial focus on a specific element.
  823. $( '.wp-initial-focus' ).focus();
  824. });
  825. // Fire a custom jQuery event at the end of window resize
  826. ( function() {
  827. var timeout;
  828. function triggerEvent() {
  829. $document.trigger( 'wp-window-resized' );
  830. }
  831. function fireOnce() {
  832. window.clearTimeout( timeout );
  833. timeout = window.setTimeout( triggerEvent, 200 );
  834. }
  835. $window.on( 'resize.wp-fire-once', fireOnce );
  836. }());
  837. // Make Windows 8 devices play along nicely.
  838. (function(){
  839. if ( '-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/) ) {
  840. var msViewportStyle = document.createElement( 'style' );
  841. msViewportStyle.appendChild(
  842. document.createTextNode( '@-ms-viewport{width:auto!important}' )
  843. );
  844. document.getElementsByTagName( 'head' )[0].appendChild( msViewportStyle );
  845. }
  846. })();
  847. }( jQuery, window ));