common.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
  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. /**
  146. * Update custom permalink structure via buttons.
  147. */
  148. var permalinkStructureFocused = false,
  149. $permalinkStructure = $( '#permalink_structure' ),
  150. $permalinkStructureInputs = $( '.permalink-structure input:radio' ),
  151. $permalinkCustomSelection = $( '#custom_selection' ),
  152. $availableStructureTags = $( '.form-table.permalink-structure .available-structure-tags button' );
  153. // Change permalink structure input when selecting one of the common structures.
  154. $permalinkStructureInputs.on( 'change', function() {
  155. if ( 'custom' === this.value ) {
  156. return;
  157. }
  158. $permalinkStructure.val( this.value );
  159. // Update button states after selection.
  160. $availableStructureTags.each( function() {
  161. changeStructureTagButtonState( $( this ) );
  162. } );
  163. } );
  164. $permalinkStructure.on( 'click input', function() {
  165. $permalinkCustomSelection.prop( 'checked', true );
  166. } );
  167. // Check if the permalink structure input field has had focus at least once.
  168. $permalinkStructure.on( 'focus', function( event ) {
  169. permalinkStructureFocused = true;
  170. $( this ).off( event );
  171. } );
  172. /**
  173. * Enables or disables a structure tag button depending on its usage.
  174. *
  175. * If the structure is already used in the custom permalink structure,
  176. * it will be disabled.
  177. *
  178. * @param {object} button Button jQuery object.
  179. */
  180. function changeStructureTagButtonState( button ) {
  181. if ( -1 !== $permalinkStructure.val().indexOf( button.text().trim() ) ) {
  182. button.attr( 'data-label', button.attr( 'aria-label' ) );
  183. button.attr( 'aria-label', button.attr( 'data-used' ) );
  184. button.attr( 'aria-pressed', true );
  185. button.addClass( 'active' );
  186. } else if ( button.attr( 'data-label' ) ) {
  187. button.attr( 'aria-label', button.attr( 'data-label' ) );
  188. button.attr( 'aria-pressed', false );
  189. button.removeClass( 'active' );
  190. }
  191. }
  192. // Check initial button state.
  193. $availableStructureTags.each( function() {
  194. changeStructureTagButtonState( $( this ) );
  195. } );
  196. // Observe permalink structure field and disable buttons of tags that are already present.
  197. $permalinkStructure.on( 'change', function() {
  198. $availableStructureTags.each( function() {
  199. changeStructureTagButtonState( $( this ) );
  200. } );
  201. } );
  202. $availableStructureTags.on( 'click', function() {
  203. var permalinkStructureValue = $permalinkStructure.val(),
  204. selectionStart = $permalinkStructure[ 0 ].selectionStart,
  205. selectionEnd = $permalinkStructure[ 0 ].selectionEnd,
  206. textToAppend = $( this ).text().trim(),
  207. textToAnnounce = $( this ).attr( 'data-added' ),
  208. newSelectionStart;
  209. // Remove structure tag if already part of the structure.
  210. if ( -1 !== permalinkStructureValue.indexOf( textToAppend ) ) {
  211. permalinkStructureValue = permalinkStructureValue.replace( textToAppend + '/', '' );
  212. $permalinkStructure.val( '/' === permalinkStructureValue ? '' : permalinkStructureValue );
  213. // Announce change to screen readers.
  214. $( '#custom_selection_updated' ).text( textToAnnounce );
  215. // Disable button.
  216. changeStructureTagButtonState( $( this ) );
  217. return;
  218. }
  219. // Input field never had focus, move selection to end of input.
  220. if ( ! permalinkStructureFocused && 0 === selectionStart && 0 === selectionEnd ) {
  221. selectionStart = selectionEnd = permalinkStructureValue.length;
  222. }
  223. $permalinkCustomSelection.prop( 'checked', true );
  224. // Prepend and append slashes if necessary.
  225. if ( '/' !== permalinkStructureValue.substr( 0, selectionStart ).substr( -1 ) ) {
  226. textToAppend = '/' + textToAppend;
  227. }
  228. if ( '/' !== permalinkStructureValue.substr( selectionEnd, 1 ) ) {
  229. textToAppend = textToAppend + '/';
  230. }
  231. // Insert structure tag at the specified position.
  232. $permalinkStructure.val( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend + permalinkStructureValue.substr( selectionEnd ) );
  233. // Announce change to screen readers.
  234. $( '#custom_selection_updated' ).text( textToAnnounce );
  235. // Disable button.
  236. changeStructureTagButtonState( $( this ) );
  237. // If input had focus give it back with cursor right after appended text.
  238. if ( permalinkStructureFocused && $permalinkStructure[0].setSelectionRange ) {
  239. newSelectionStart = ( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend ).length;
  240. $permalinkStructure[0].setSelectionRange( newSelectionStart, newSelectionStart );
  241. $permalinkStructure.focus();
  242. }
  243. } );
  244. $document.ready( function() {
  245. var checks, first, last, checked, sliced, mobileEvent, transitionTimeout, focusedRowActions,
  246. lastClicked = false,
  247. pageInput = $('input.current-page'),
  248. currentPage = pageInput.val(),
  249. isIOS = /iPhone|iPad|iPod/.test( navigator.userAgent ),
  250. isAndroid = navigator.userAgent.indexOf( 'Android' ) !== -1,
  251. isIE8 = $( document.documentElement ).hasClass( 'ie8' ),
  252. $adminMenuWrap = $( '#adminmenuwrap' ),
  253. $wpwrap = $( '#wpwrap' ),
  254. $adminmenu = $( '#adminmenu' ),
  255. $overlay = $( '#wp-responsive-overlay' ),
  256. $toolbar = $( '#wp-toolbar' ),
  257. $toolbarPopups = $toolbar.find( 'a[aria-haspopup="true"]' ),
  258. $sortables = $('.meta-box-sortables'),
  259. wpResponsiveActive = false,
  260. $adminbar = $( '#wpadminbar' ),
  261. lastScrollPosition = 0,
  262. pinnedMenuTop = false,
  263. pinnedMenuBottom = false,
  264. menuTop = 0,
  265. menuState,
  266. menuIsPinned = false,
  267. height = {
  268. window: $window.height(),
  269. wpwrap: $wpwrap.height(),
  270. adminbar: $adminbar.height(),
  271. menu: $adminMenuWrap.height()
  272. },
  273. $headerEnd = $( '.wp-header-end' );
  274. // when the menu is folded, make the fly-out submenu header clickable
  275. $adminmenu.on('click.wp-submenu-head', '.wp-submenu-head', function(e){
  276. $(e.target).parent().siblings('a').get(0).click();
  277. });
  278. $( '#collapse-button' ).on( 'click.collapse-menu', function() {
  279. var viewportWidth = getViewportWidth() || 961;
  280. // reset any compensation for submenus near the bottom of the screen
  281. $('#adminmenu div.wp-submenu').css('margin-top', '');
  282. if ( viewportWidth < 960 ) {
  283. if ( $body.hasClass('auto-fold') ) {
  284. $body.removeClass('auto-fold').removeClass('folded');
  285. setUserSetting('unfold', 1);
  286. setUserSetting('mfold', 'o');
  287. menuState = 'open';
  288. } else {
  289. $body.addClass('auto-fold');
  290. setUserSetting('unfold', 0);
  291. menuState = 'folded';
  292. }
  293. } else {
  294. if ( $body.hasClass('folded') ) {
  295. $body.removeClass('folded');
  296. setUserSetting('mfold', 'o');
  297. menuState = 'open';
  298. } else {
  299. $body.addClass('folded');
  300. setUserSetting('mfold', 'f');
  301. menuState = 'folded';
  302. }
  303. }
  304. $document.trigger( 'wp-collapse-menu', { state: menuState } );
  305. });
  306. // Handle the `aria-haspopup` attribute on the current menu item when it has a sub-menu.
  307. function currentMenuItemHasPopup() {
  308. var $current = $( 'a.wp-has-current-submenu' );
  309. if ( 'folded' === menuState ) {
  310. // When folded or auto-folded and not responsive view, the current menu item does have a fly-out sub-menu.
  311. $current.attr( 'aria-haspopup', 'true' );
  312. } else {
  313. // When expanded or in responsive view, reset aria-haspopup.
  314. $current.attr( 'aria-haspopup', 'false' );
  315. }
  316. }
  317. $document.on( 'wp-menu-state-set wp-collapse-menu wp-responsive-activate wp-responsive-deactivate', currentMenuItemHasPopup );
  318. /**
  319. * Ensure an admin submenu is within the visual viewport.
  320. *
  321. * @since 4.1.0
  322. *
  323. * @param {jQuery} $menuItem The parent menu item containing the submenu.
  324. */
  325. function adjustSubmenu( $menuItem ) {
  326. var bottomOffset, pageHeight, adjustment, theFold, menutop, wintop, maxtop,
  327. $submenu = $menuItem.find( '.wp-submenu' );
  328. menutop = $menuItem.offset().top;
  329. wintop = $window.scrollTop();
  330. maxtop = menutop - wintop - 30; // max = make the top of the sub almost touch admin bar
  331. bottomOffset = menutop + $submenu.height() + 1; // Bottom offset of the menu
  332. pageHeight = $wpwrap.height(); // Height of the entire page
  333. adjustment = 60 + bottomOffset - pageHeight;
  334. theFold = $window.height() + wintop - 50; // The fold
  335. if ( theFold < ( bottomOffset - adjustment ) ) {
  336. adjustment = bottomOffset - theFold;
  337. }
  338. if ( adjustment > maxtop ) {
  339. adjustment = maxtop;
  340. }
  341. if ( adjustment > 1 ) {
  342. $submenu.css( 'margin-top', '-' + adjustment + 'px' );
  343. } else {
  344. $submenu.css( 'margin-top', '' );
  345. }
  346. }
  347. if ( 'ontouchstart' in window || /IEMobile\/[1-9]/.test(navigator.userAgent) ) { // touch screen device
  348. // iOS Safari works with touchstart, the rest work with click
  349. mobileEvent = isIOS ? 'touchstart' : 'click';
  350. // close any open submenus when touch/click is not on the menu
  351. $body.on( mobileEvent+'.wp-mobile-hover', function(e) {
  352. if ( $adminmenu.data('wp-responsive') ) {
  353. return;
  354. }
  355. if ( ! $( e.target ).closest( '#adminmenu' ).length ) {
  356. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  357. }
  358. });
  359. $adminmenu.find( 'a.wp-has-submenu' ).on( mobileEvent + '.wp-mobile-hover', function( event ) {
  360. var $menuItem = $(this).parent();
  361. if ( $adminmenu.data( 'wp-responsive' ) ) {
  362. return;
  363. }
  364. // Show the sub instead of following the link if:
  365. // - the submenu is not open
  366. // - the submenu is not shown inline or the menu is not folded
  367. if ( ! $menuItem.hasClass( 'opensub' ) && ( ! $menuItem.hasClass( 'wp-menu-open' ) || $menuItem.width() < 40 ) ) {
  368. event.preventDefault();
  369. adjustSubmenu( $menuItem );
  370. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  371. $menuItem.addClass('opensub');
  372. }
  373. });
  374. }
  375. if ( ! isIOS && ! isAndroid ) {
  376. $adminmenu.find( 'li.wp-has-submenu' ).hoverIntent({
  377. over: function() {
  378. var $menuItem = $( this ),
  379. $submenu = $menuItem.find( '.wp-submenu' ),
  380. top = parseInt( $submenu.css( 'top' ), 10 );
  381. if ( isNaN( top ) || top > -5 ) { // the submenu is visible
  382. return;
  383. }
  384. if ( $adminmenu.data( 'wp-responsive' ) ) {
  385. // The menu is in responsive mode, bail
  386. return;
  387. }
  388. adjustSubmenu( $menuItem );
  389. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  390. $menuItem.addClass( 'opensub' );
  391. },
  392. out: function(){
  393. if ( $adminmenu.data( 'wp-responsive' ) ) {
  394. // The menu is in responsive mode, bail
  395. return;
  396. }
  397. $( this ).removeClass( 'opensub' ).find( '.wp-submenu' ).css( 'margin-top', '' );
  398. },
  399. timeout: 200,
  400. sensitivity: 7,
  401. interval: 90
  402. });
  403. $adminmenu.on( 'focus.adminmenu', '.wp-submenu a', function( event ) {
  404. if ( $adminmenu.data( 'wp-responsive' ) ) {
  405. // The menu is in responsive mode, bail
  406. return;
  407. }
  408. $( event.target ).closest( 'li.menu-top' ).addClass( 'opensub' );
  409. }).on( 'blur.adminmenu', '.wp-submenu a', function( event ) {
  410. if ( $adminmenu.data( 'wp-responsive' ) ) {
  411. return;
  412. }
  413. $( event.target ).closest( 'li.menu-top' ).removeClass( 'opensub' );
  414. }).find( 'li.wp-has-submenu.wp-not-current-submenu' ).on( 'focusin.adminmenu', function() {
  415. adjustSubmenu( $( this ) );
  416. });
  417. }
  418. /*
  419. * The `.below-h2` class is here just for backward compatibility with plugins
  420. * that are (incorrectly) using it. Do not use. Use `.inline` instead. See #34570.
  421. * If '.wp-header-end' is found, append the notices after it otherwise
  422. * after the first h1 or h2 heading found within the main content.
  423. */
  424. if ( ! $headerEnd.length ) {
  425. $headerEnd = $( '.wrap h1, .wrap h2' ).first();
  426. }
  427. $( 'div.updated, div.error, div.notice' ).not( '.inline, .below-h2' ).insertAfter( $headerEnd );
  428. // Make notices dismissible
  429. function makeNoticesDismissible() {
  430. $( '.notice.is-dismissible' ).each( function() {
  431. var $el = $( this ),
  432. $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
  433. btnText = commonL10n.dismiss || '';
  434. // Ensure plain text
  435. $button.find( '.screen-reader-text' ).text( btnText );
  436. $button.on( 'click.wp-dismiss-notice', function( event ) {
  437. event.preventDefault();
  438. $el.fadeTo( 100, 0, function() {
  439. $el.slideUp( 100, function() {
  440. $el.remove();
  441. });
  442. });
  443. });
  444. $el.append( $button );
  445. });
  446. }
  447. $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 );
  448. // Init screen meta
  449. screenMeta.init();
  450. // This event needs to be delegated. Ticket #37973.
  451. $body.on( 'click', 'tbody > tr > .check-column :checkbox', function( event ) {
  452. // Shift click to select a range of checkboxes.
  453. if ( 'undefined' == event.shiftKey ) { return true; }
  454. if ( event.shiftKey ) {
  455. if ( !lastClicked ) { return true; }
  456. checks = $( lastClicked ).closest( 'form' ).find( ':checkbox' ).filter( ':visible:enabled' );
  457. first = checks.index( lastClicked );
  458. last = checks.index( this );
  459. checked = $(this).prop('checked');
  460. if ( 0 < first && 0 < last && first != last ) {
  461. sliced = ( last > first ) ? checks.slice( first, last ) : checks.slice( last, first );
  462. sliced.prop( 'checked', function() {
  463. if ( $(this).closest('tr').is(':visible') )
  464. return checked;
  465. return false;
  466. });
  467. }
  468. }
  469. lastClicked = this;
  470. // Toggle the "Select all" checkboxes depending if the other ones are all checked or not.
  471. var unchecked = $(this).closest('tbody').find(':checkbox').filter(':visible:enabled').not(':checked');
  472. $(this).closest('table').children('thead, tfoot').find(':checkbox').prop('checked', function() {
  473. return ( 0 === unchecked.length );
  474. });
  475. return true;
  476. });
  477. // This event needs to be delegated. Ticket #37973.
  478. $body.on( 'click.wp-toggle-checkboxes', 'thead .check-column :checkbox, tfoot .check-column :checkbox', function( event ) {
  479. var $this = $(this),
  480. $table = $this.closest( 'table' ),
  481. controlChecked = $this.prop('checked'),
  482. toggle = event.shiftKey || $this.data('wp-toggle');
  483. $table.children( 'tbody' ).filter(':visible')
  484. .children().children('.check-column').find(':checkbox')
  485. .prop('checked', function() {
  486. if ( $(this).is(':hidden,:disabled') ) {
  487. return false;
  488. }
  489. if ( toggle ) {
  490. return ! $(this).prop( 'checked' );
  491. } else if ( controlChecked ) {
  492. return true;
  493. }
  494. return false;
  495. });
  496. $table.children('thead, tfoot').filter(':visible')
  497. .children().children('.check-column').find(':checkbox')
  498. .prop('checked', function() {
  499. if ( toggle ) {
  500. return false;
  501. } else if ( controlChecked ) {
  502. return true;
  503. }
  504. return false;
  505. });
  506. });
  507. // Show row actions on keyboard focus of its parent container element or any other elements contained within
  508. $( '#wpbody-content' ).on({
  509. focusin: function() {
  510. clearTimeout( transitionTimeout );
  511. focusedRowActions = $( this ).find( '.row-actions' );
  512. // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
  513. $( '.row-actions' ).not( this ).removeClass( 'visible' );
  514. focusedRowActions.addClass( 'visible' );
  515. },
  516. focusout: function() {
  517. // Tabbing between post title and .row-actions links needs a brief pause, otherwise
  518. // the .row-actions div gets hidden in transit in some browsers (ahem, Firefox).
  519. transitionTimeout = setTimeout( function() {
  520. focusedRowActions.removeClass( 'visible' );
  521. }, 30 );
  522. }
  523. }, '.has-row-actions' );
  524. // Toggle list table rows on small screens
  525. $( 'tbody' ).on( 'click', '.toggle-row', function() {
  526. $( this ).closest( 'tr' ).toggleClass( 'is-expanded' );
  527. });
  528. $('#default-password-nag-no').click( function() {
  529. setUserSetting('default_password_nag', 'hide');
  530. $('div.default-password-nag').hide();
  531. return false;
  532. });
  533. // tab in textareas
  534. $('#newcontent').bind('keydown.wpevent_InsertTab', function(e) {
  535. var el = e.target, selStart, selEnd, val, scroll, sel;
  536. if ( e.keyCode == 27 ) { // escape key
  537. // when pressing Escape: Opera 12 and 27 blur form fields, IE 8 clears them
  538. e.preventDefault();
  539. $(el).data('tab-out', true);
  540. return;
  541. }
  542. if ( e.keyCode != 9 || e.ctrlKey || e.altKey || e.shiftKey ) // tab key
  543. return;
  544. if ( $(el).data('tab-out') ) {
  545. $(el).data('tab-out', false);
  546. return;
  547. }
  548. selStart = el.selectionStart;
  549. selEnd = el.selectionEnd;
  550. val = el.value;
  551. if ( document.selection ) {
  552. el.focus();
  553. sel = document.selection.createRange();
  554. sel.text = '\t';
  555. } else if ( selStart >= 0 ) {
  556. scroll = this.scrollTop;
  557. el.value = val.substring(0, selStart).concat('\t', val.substring(selEnd) );
  558. el.selectionStart = el.selectionEnd = selStart + 1;
  559. this.scrollTop = scroll;
  560. }
  561. if ( e.stopPropagation )
  562. e.stopPropagation();
  563. if ( e.preventDefault )
  564. e.preventDefault();
  565. });
  566. if ( pageInput.length ) {
  567. pageInput.closest('form').submit( function() {
  568. // Reset paging var for new filters/searches but not for bulk actions. See #17685.
  569. if ( $('select[name="action"]').val() == -1 && $('select[name="action2"]').val() == -1 && pageInput.val() == currentPage )
  570. pageInput.val('1');
  571. });
  572. }
  573. $('.search-box input[type="search"], .search-box input[type="submit"]').mousedown(function () {
  574. $('select[name^="action"]').val('-1');
  575. });
  576. // Scroll into view when focused
  577. $('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){
  578. if ( e.target.scrollIntoView )
  579. e.target.scrollIntoView(false);
  580. });
  581. // Disable upload buttons until files are selected
  582. (function(){
  583. var button, input, form = $('form.wp-upload-form');
  584. if ( ! form.length )
  585. return;
  586. button = form.find('input[type="submit"]');
  587. input = form.find('input[type="file"]');
  588. function toggleUploadButton() {
  589. button.prop('disabled', '' === input.map( function() {
  590. return $(this).val();
  591. }).get().join(''));
  592. }
  593. toggleUploadButton();
  594. input.on('change', toggleUploadButton);
  595. })();
  596. function pinMenu( event ) {
  597. var windowPos = $window.scrollTop(),
  598. resizing = ! event || event.type !== 'scroll';
  599. if ( isIOS || isIE8 || $adminmenu.data( 'wp-responsive' ) ) {
  600. return;
  601. }
  602. if ( height.menu + height.adminbar < height.window ||
  603. height.menu + height.adminbar + 20 > height.wpwrap ) {
  604. unpinMenu();
  605. return;
  606. }
  607. menuIsPinned = true;
  608. if ( height.menu + height.adminbar > height.window ) {
  609. // Check for overscrolling
  610. if ( windowPos < 0 ) {
  611. if ( ! pinnedMenuTop ) {
  612. pinnedMenuTop = true;
  613. pinnedMenuBottom = false;
  614. $adminMenuWrap.css({
  615. position: 'fixed',
  616. top: '',
  617. bottom: ''
  618. });
  619. }
  620. return;
  621. } else if ( windowPos + height.window > $document.height() - 1 ) {
  622. if ( ! pinnedMenuBottom ) {
  623. pinnedMenuBottom = true;
  624. pinnedMenuTop = false;
  625. $adminMenuWrap.css({
  626. position: 'fixed',
  627. top: '',
  628. bottom: 0
  629. });
  630. }
  631. return;
  632. }
  633. if ( windowPos > lastScrollPosition ) {
  634. // Scrolling down
  635. if ( pinnedMenuTop ) {
  636. // let it scroll
  637. pinnedMenuTop = false;
  638. menuTop = $adminMenuWrap.offset().top - height.adminbar - ( windowPos - lastScrollPosition );
  639. if ( menuTop + height.menu + height.adminbar < windowPos + height.window ) {
  640. menuTop = windowPos + height.window - height.menu - height.adminbar;
  641. }
  642. $adminMenuWrap.css({
  643. position: 'absolute',
  644. top: menuTop,
  645. bottom: ''
  646. });
  647. } else if ( ! pinnedMenuBottom && $adminMenuWrap.offset().top + height.menu < windowPos + height.window ) {
  648. // pin the bottom
  649. pinnedMenuBottom = true;
  650. $adminMenuWrap.css({
  651. position: 'fixed',
  652. top: '',
  653. bottom: 0
  654. });
  655. }
  656. } else if ( windowPos < lastScrollPosition ) {
  657. // Scrolling up
  658. if ( pinnedMenuBottom ) {
  659. // let it scroll
  660. pinnedMenuBottom = false;
  661. menuTop = $adminMenuWrap.offset().top - height.adminbar + ( lastScrollPosition - windowPos );
  662. if ( menuTop + height.menu > windowPos + height.window ) {
  663. menuTop = windowPos;
  664. }
  665. $adminMenuWrap.css({
  666. position: 'absolute',
  667. top: menuTop,
  668. bottom: ''
  669. });
  670. } else if ( ! pinnedMenuTop && $adminMenuWrap.offset().top >= windowPos + height.adminbar ) {
  671. // pin the top
  672. pinnedMenuTop = true;
  673. $adminMenuWrap.css({
  674. position: 'fixed',
  675. top: '',
  676. bottom: ''
  677. });
  678. }
  679. } else if ( resizing ) {
  680. // Resizing
  681. pinnedMenuTop = pinnedMenuBottom = false;
  682. menuTop = windowPos + height.window - height.menu - height.adminbar - 1;
  683. if ( menuTop > 0 ) {
  684. $adminMenuWrap.css({
  685. position: 'absolute',
  686. top: menuTop,
  687. bottom: ''
  688. });
  689. } else {
  690. unpinMenu();
  691. }
  692. }
  693. }
  694. lastScrollPosition = windowPos;
  695. }
  696. function resetHeights() {
  697. height = {
  698. window: $window.height(),
  699. wpwrap: $wpwrap.height(),
  700. adminbar: $adminbar.height(),
  701. menu: $adminMenuWrap.height()
  702. };
  703. }
  704. function unpinMenu() {
  705. if ( isIOS || ! menuIsPinned ) {
  706. return;
  707. }
  708. pinnedMenuTop = pinnedMenuBottom = menuIsPinned = false;
  709. $adminMenuWrap.css({
  710. position: '',
  711. top: '',
  712. bottom: ''
  713. });
  714. }
  715. function setPinMenu() {
  716. resetHeights();
  717. if ( $adminmenu.data('wp-responsive') ) {
  718. $body.removeClass( 'sticky-menu' );
  719. unpinMenu();
  720. } else if ( height.menu + height.adminbar > height.window ) {
  721. pinMenu();
  722. $body.removeClass( 'sticky-menu' );
  723. } else {
  724. $body.addClass( 'sticky-menu' );
  725. unpinMenu();
  726. }
  727. }
  728. if ( ! isIOS ) {
  729. $window.on( 'scroll.pin-menu', pinMenu );
  730. $document.on( 'tinymce-editor-init.pin-menu', function( event, editor ) {
  731. editor.on( 'wp-autoresize', resetHeights );
  732. });
  733. }
  734. window.wpResponsive = {
  735. init: function() {
  736. var self = this;
  737. // Modify functionality based on custom activate/deactivate event
  738. $document.on( 'wp-responsive-activate.wp-responsive', function() {
  739. self.activate();
  740. }).on( 'wp-responsive-deactivate.wp-responsive', function() {
  741. self.deactivate();
  742. });
  743. $( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' );
  744. // Toggle sidebar when toggle is clicked
  745. $( '#wp-admin-bar-menu-toggle' ).on( 'click.wp-responsive', function( event ) {
  746. event.preventDefault();
  747. // close any open toolbar submenus
  748. $adminbar.find( '.hover' ).removeClass( 'hover' );
  749. $wpwrap.toggleClass( 'wp-responsive-open' );
  750. if ( $wpwrap.hasClass( 'wp-responsive-open' ) ) {
  751. $(this).find('a').attr( 'aria-expanded', 'true' );
  752. $( '#adminmenu a:first' ).focus();
  753. } else {
  754. $(this).find('a').attr( 'aria-expanded', 'false' );
  755. }
  756. } );
  757. // Add menu events
  758. $adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) {
  759. if ( ! $adminmenu.data('wp-responsive') ) {
  760. return;
  761. }
  762. $( this ).parent( 'li' ).toggleClass( 'selected' );
  763. event.preventDefault();
  764. });
  765. self.trigger();
  766. $document.on( 'wp-window-resized.wp-responsive', $.proxy( this.trigger, this ) );
  767. // This needs to run later as UI Sortable may be initialized later on $(document).ready()
  768. $window.on( 'load.wp-responsive', function() {
  769. var width = navigator.userAgent.indexOf('AppleWebKit/') > -1 ? $window.width() : window.innerWidth;
  770. if ( width <= 782 ) {
  771. self.disableSortables();
  772. }
  773. });
  774. },
  775. activate: function() {
  776. setPinMenu();
  777. if ( ! $body.hasClass( 'auto-fold' ) ) {
  778. $body.addClass( 'auto-fold' );
  779. }
  780. $adminmenu.data( 'wp-responsive', 1 );
  781. this.disableSortables();
  782. },
  783. deactivate: function() {
  784. setPinMenu();
  785. $adminmenu.removeData('wp-responsive');
  786. this.enableSortables();
  787. },
  788. trigger: function() {
  789. var viewportWidth = getViewportWidth();
  790. // Exclude IE < 9, it doesn't support @media CSS rules.
  791. if ( ! viewportWidth ) {
  792. return;
  793. }
  794. if ( viewportWidth <= 782 ) {
  795. if ( ! wpResponsiveActive ) {
  796. $document.trigger( 'wp-responsive-activate' );
  797. wpResponsiveActive = true;
  798. }
  799. } else {
  800. if ( wpResponsiveActive ) {
  801. $document.trigger( 'wp-responsive-deactivate' );
  802. wpResponsiveActive = false;
  803. }
  804. }
  805. if ( viewportWidth <= 480 ) {
  806. this.enableOverlay();
  807. } else {
  808. this.disableOverlay();
  809. }
  810. },
  811. enableOverlay: function() {
  812. if ( $overlay.length === 0 ) {
  813. $overlay = $( '<div id="wp-responsive-overlay"></div>' )
  814. .insertAfter( '#wpcontent' )
  815. .hide()
  816. .on( 'click.wp-responsive', function() {
  817. $toolbar.find( '.menupop.hover' ).removeClass( 'hover' );
  818. $( this ).hide();
  819. });
  820. }
  821. $toolbarPopups.on( 'click.wp-responsive', function() {
  822. $overlay.show();
  823. });
  824. },
  825. disableOverlay: function() {
  826. $toolbarPopups.off( 'click.wp-responsive' );
  827. $overlay.hide();
  828. },
  829. disableSortables: function() {
  830. if ( $sortables.length ) {
  831. try {
  832. $sortables.sortable('disable');
  833. } catch(e) {}
  834. }
  835. },
  836. enableSortables: function() {
  837. if ( $sortables.length ) {
  838. try {
  839. $sortables.sortable('enable');
  840. } catch(e) {}
  841. }
  842. }
  843. };
  844. // Add an ARIA role `button` to elements that behave like UI controls when JavaScript is on.
  845. function aria_button_if_js() {
  846. $( '.aria-button-if-js' ).attr( 'role', 'button' );
  847. }
  848. $( document ).ajaxComplete( function() {
  849. aria_button_if_js();
  850. });
  851. /**
  852. * @summary Get the viewport width.
  853. *
  854. * @since 4.7.0
  855. *
  856. * @returns {number|boolean} The current viewport width or false if the
  857. * browser doesn't support innerWidth (IE < 9).
  858. */
  859. function getViewportWidth() {
  860. var viewportWidth = false;
  861. if ( window.innerWidth ) {
  862. // On phones, window.innerWidth is affected by zooming.
  863. viewportWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
  864. }
  865. return viewportWidth;
  866. }
  867. /**
  868. * @summary Set the admin menu collapsed/expanded state.
  869. *
  870. * Sets the global variable `menuState` and triggers a custom event passing
  871. * the current menu state.
  872. *
  873. * @since 4.7.0
  874. *
  875. * @returns {void}
  876. */
  877. function setMenuState() {
  878. var viewportWidth = getViewportWidth() || 961;
  879. if ( viewportWidth <= 782 ) {
  880. menuState = 'responsive';
  881. } else if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && viewportWidth <= 960 && viewportWidth > 782 ) ) {
  882. menuState = 'folded';
  883. } else {
  884. menuState = 'open';
  885. }
  886. $document.trigger( 'wp-menu-state-set', { state: menuState } );
  887. }
  888. // Set the menu state when the window gets resized.
  889. $document.on( 'wp-window-resized.set-menu-state', setMenuState );
  890. /**
  891. * @summary Set ARIA attributes on the collapse/expand menu button.
  892. *
  893. * When the admin menu is open or folded, updates the `aria-expanded` and
  894. * `aria-label` attributes of the button to give feedback to assistive
  895. * technologies. In the responsive view, the button is always hidden.
  896. *
  897. * @since 4.7.0
  898. *
  899. * @returns {void}
  900. */
  901. $document.on( 'wp-menu-state-set wp-collapse-menu', function( event, eventData ) {
  902. var $collapseButton = $( '#collapse-button' ),
  903. ariaExpanded = 'true',
  904. ariaLabelText = commonL10n.collapseMenu;
  905. if ( 'folded' === eventData.state ) {
  906. ariaExpanded = 'false';
  907. ariaLabelText = commonL10n.expandMenu;
  908. }
  909. $collapseButton.attr({
  910. 'aria-expanded': ariaExpanded,
  911. 'aria-label': ariaLabelText
  912. });
  913. });
  914. window.wpResponsive.init();
  915. setPinMenu();
  916. setMenuState();
  917. currentMenuItemHasPopup();
  918. makeNoticesDismissible();
  919. aria_button_if_js();
  920. $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 );
  921. // Set initial focus on a specific element.
  922. $( '.wp-initial-focus' ).focus();
  923. // Toggle update details on update-core.php.
  924. $body.on( 'click', '.js-update-details-toggle', function() {
  925. var $updateNotice = $( this ).closest( '.js-update-details' ),
  926. $progressDiv = $( '#' + $updateNotice.data( 'update-details' ) );
  927. /*
  928. * When clicking on "Show details" move the progress div below the update
  929. * notice. Make sure it gets moved just the first time.
  930. */
  931. if ( ! $progressDiv.hasClass( 'update-details-moved' ) ) {
  932. $progressDiv.insertAfter( $updateNotice ).addClass( 'update-details-moved' );
  933. }
  934. // Toggle the progress div visibility.
  935. $progressDiv.toggle();
  936. // Toggle the Show Details button expanded state.
  937. $( this ).attr( 'aria-expanded', $progressDiv.is( ':visible' ) );
  938. });
  939. });
  940. // Fire a custom jQuery event at the end of window resize
  941. ( function() {
  942. var timeout;
  943. function triggerEvent() {
  944. $document.trigger( 'wp-window-resized' );
  945. }
  946. function fireOnce() {
  947. window.clearTimeout( timeout );
  948. timeout = window.setTimeout( triggerEvent, 200 );
  949. }
  950. $window.on( 'resize.wp-fire-once', fireOnce );
  951. }());
  952. // Make Windows 8 devices play along nicely.
  953. (function(){
  954. if ( '-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/) ) {
  955. var msViewportStyle = document.createElement( 'style' );
  956. msViewportStyle.appendChild(
  957. document.createTextNode( '@-ms-viewport{width:auto!important}' )
  958. );
  959. document.getElementsByTagName( 'head' )[0].appendChild( msViewportStyle );
  960. }
  961. })();
  962. }( jQuery, window ));