common.js 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648
  1. /**
  2. * @output wp-admin/js/common.js
  3. */
  4. /* global setUserSetting, ajaxurl, commonL10n, alert, confirm, pagenow */
  5. /* global columns, screenMeta */
  6. /**
  7. * Adds common WordPress functionality to the window.
  8. *
  9. * @param {jQuery} $ jQuery object.
  10. * @param {Object} window The window object.
  11. * @param {mixed} undefined Unused.
  12. */
  13. ( function( $, window, undefined ) {
  14. var $document = $( document ),
  15. $window = $( window ),
  16. $body = $( document.body );
  17. /**
  18. * Removed in 3.3.0, needed for back-compatibility.
  19. *
  20. * @since 2.7.0
  21. * @deprecated 3.3.0
  22. */
  23. window.adminMenu = {
  24. init : function() {},
  25. fold : function() {},
  26. restoreMenuState : function() {},
  27. toggle : function() {},
  28. favorites : function() {}
  29. };
  30. // Show/hide/save table columns.
  31. window.columns = {
  32. /**
  33. * Initializes the column toggles in the screen options.
  34. *
  35. * Binds an onClick event to the checkboxes to show or hide the table columns
  36. * based on their toggled state. And persists the toggled state.
  37. *
  38. * @since 2.7.0
  39. *
  40. * @returns {void}
  41. */
  42. init : function() {
  43. var that = this;
  44. $('.hide-column-tog', '#adv-settings').click( function() {
  45. var $t = $(this), column = $t.val();
  46. if ( $t.prop('checked') )
  47. that.checked(column);
  48. else
  49. that.unchecked(column);
  50. columns.saveManageColumnsState();
  51. });
  52. },
  53. /**
  54. * Saves the toggled state for the columns.
  55. *
  56. * Saves whether the columns should be shown or hidden on a page.
  57. *
  58. * @since 3.0.0
  59. *
  60. * @returns {void}
  61. */
  62. saveManageColumnsState : function() {
  63. var hidden = this.hidden();
  64. $.post(ajaxurl, {
  65. action: 'hidden-columns',
  66. hidden: hidden,
  67. screenoptionnonce: $('#screenoptionnonce').val(),
  68. page: pagenow
  69. });
  70. },
  71. /**
  72. * Makes a column visible and adjusts the column span for the table.
  73. *
  74. * @since 3.0.0
  75. * @param {string} column The column name.
  76. *
  77. * @returns {void}
  78. */
  79. checked : function(column) {
  80. $('.column-' + column).removeClass( 'hidden' );
  81. this.colSpanChange(+1);
  82. },
  83. /**
  84. * Hides a column and adjusts the column span for the table.
  85. *
  86. * @since 3.0.0
  87. * @param {string} column The column name.
  88. *
  89. * @returns {void}
  90. */
  91. unchecked : function(column) {
  92. $('.column-' + column).addClass( 'hidden' );
  93. this.colSpanChange(-1);
  94. },
  95. /**
  96. * Gets all hidden columns.
  97. *
  98. * @since 3.0.0
  99. *
  100. * @returns {string} The hidden column names separated by a comma.
  101. */
  102. hidden : function() {
  103. return $( '.manage-column[id]' ).filter( '.hidden' ).map(function() {
  104. return this.id;
  105. }).get().join( ',' );
  106. },
  107. /**
  108. * Gets the checked column toggles from the screen options.
  109. *
  110. * @since 3.0.0
  111. *
  112. * @returns {string} String containing the checked column names.
  113. */
  114. useCheckboxesForHidden : function() {
  115. this.hidden = function(){
  116. return $('.hide-column-tog').not(':checked').map(function() {
  117. var id = this.id;
  118. return id.substring( id, id.length - 5 );
  119. }).get().join(',');
  120. };
  121. },
  122. /**
  123. * Adjusts the column span for the table.
  124. *
  125. * @since 3.1.0
  126. *
  127. * @param {int} diff The modifier for the column span.
  128. */
  129. colSpanChange : function(diff) {
  130. var $t = $('table').find('.colspanchange'), n;
  131. if ( !$t.length )
  132. return;
  133. n = parseInt( $t.attr('colspan'), 10 ) + diff;
  134. $t.attr('colspan', n.toString());
  135. }
  136. };
  137. $document.ready(function(){columns.init();});
  138. /**
  139. * Validates that the required form fields are not empty.
  140. *
  141. * @since 2.9.0
  142. *
  143. * @param {jQuery} form The form to validate.
  144. *
  145. * @returns {boolean} Returns true if all required fields are not an empty string.
  146. */
  147. window.validateForm = function( form ) {
  148. return !$( form )
  149. .find( '.form-required' )
  150. .filter( function() { return $( ':input:visible', this ).val() === ''; } )
  151. .addClass( 'form-invalid' )
  152. .find( ':input:visible' )
  153. .change( function() { $( this ).closest( '.form-invalid' ).removeClass( 'form-invalid' ); } )
  154. .length;
  155. };
  156. // stub for doing better warnings
  157. /**
  158. * Shows message pop-up notice or confirmation message.
  159. *
  160. * @since 2.7.0
  161. *
  162. * @type {{warn: showNotice.warn, note: showNotice.note}}
  163. *
  164. * @returns {void}
  165. */
  166. window.showNotice = {
  167. /**
  168. * Shows a delete confirmation pop-up message.
  169. *
  170. * @since 2.7.0
  171. *
  172. * @returns {boolean} Returns true if the message is confirmed.
  173. */
  174. warn : function() {
  175. var msg = commonL10n.warnDelete || '';
  176. if ( confirm(msg) ) {
  177. return true;
  178. }
  179. return false;
  180. },
  181. /**
  182. * Shows an alert message.
  183. *
  184. * @since 2.7.0
  185. *
  186. * @param text The text to display in the message.
  187. */
  188. note : function(text) {
  189. alert(text);
  190. }
  191. };
  192. /**
  193. * Represents the functions for the meta screen options panel.
  194. *
  195. * @since 3.2.0
  196. *
  197. * @type {{element: null, toggles: null, page: null, init: screenMeta.init,
  198. * toggleEvent: screenMeta.toggleEvent, open: screenMeta.open,
  199. * close: screenMeta.close}}
  200. *
  201. * @returns {void}
  202. */
  203. window.screenMeta = {
  204. element: null, // #screen-meta
  205. toggles: null, // .screen-meta-toggle
  206. page: null, // #wpcontent
  207. /**
  208. * Initializes the screen meta options panel.
  209. *
  210. * @since 3.2.0
  211. *
  212. * @returns {void}
  213. */
  214. init: function() {
  215. this.element = $('#screen-meta');
  216. this.toggles = $( '#screen-meta-links' ).find( '.show-settings' );
  217. this.page = $('#wpcontent');
  218. this.toggles.click( this.toggleEvent );
  219. },
  220. /**
  221. * Toggles the screen meta options panel.
  222. *
  223. * @since 3.2.0
  224. *
  225. * @returns {void}
  226. */
  227. toggleEvent: function() {
  228. var panel = $( '#' + $( this ).attr( 'aria-controls' ) );
  229. if ( !panel.length )
  230. return;
  231. if ( panel.is(':visible') )
  232. screenMeta.close( panel, $(this) );
  233. else
  234. screenMeta.open( panel, $(this) );
  235. },
  236. /**
  237. * Opens the screen meta options panel.
  238. *
  239. * @since 3.2.0
  240. *
  241. * @param {jQuery} panel The screen meta options panel div.
  242. * @param {jQuery} button The toggle button.
  243. *
  244. * @returns {void}
  245. */
  246. open: function( panel, button ) {
  247. $( '#screen-meta-links' ).find( '.screen-meta-toggle' ).not( button.parent() ).css( 'visibility', 'hidden' );
  248. panel.parent().show();
  249. /**
  250. * Sets the focus to the meta options panel and adds the necessary CSS classes.
  251. *
  252. * @since 3.2.0
  253. *
  254. * @returns {void}
  255. */
  256. panel.slideDown( 'fast', function() {
  257. panel.focus();
  258. button.addClass( 'screen-meta-active' ).attr( 'aria-expanded', true );
  259. });
  260. $document.trigger( 'screen:options:open' );
  261. },
  262. /**
  263. * Closes the screen meta options panel.
  264. *
  265. * @since 3.2.0
  266. *
  267. * @param {jQuery} panel The screen meta options panel div.
  268. * @param {jQuery} button The toggle button.
  269. *
  270. * @returns {void}
  271. */
  272. close: function( panel, button ) {
  273. /**
  274. * Hides the screen meta options panel.
  275. *
  276. * @since 3.2.0
  277. *
  278. * @returns {void}
  279. */
  280. panel.slideUp( 'fast', function() {
  281. button.removeClass( 'screen-meta-active' ).attr( 'aria-expanded', false );
  282. $('.screen-meta-toggle').css('visibility', '');
  283. panel.parent().hide();
  284. });
  285. $document.trigger( 'screen:options:close' );
  286. }
  287. };
  288. /**
  289. * Initializes the help tabs in the help panel.
  290. *
  291. * @param {Event} e The event object.
  292. *
  293. * @returns {void}
  294. */
  295. $('.contextual-help-tabs').delegate('a', 'click', function(e) {
  296. var link = $(this),
  297. panel;
  298. e.preventDefault();
  299. // Don't do anything if the click is for the tab already showing.
  300. if ( link.is('.active a') )
  301. return false;
  302. // Links
  303. $('.contextual-help-tabs .active').removeClass('active');
  304. link.parent('li').addClass('active');
  305. panel = $( link.attr('href') );
  306. // Panels
  307. $('.help-tab-content').not( panel ).removeClass('active').hide();
  308. panel.addClass('active').show();
  309. });
  310. /**
  311. * Update custom permalink structure via buttons.
  312. */
  313. var permalinkStructureFocused = false,
  314. $permalinkStructure = $( '#permalink_structure' ),
  315. $permalinkStructureInputs = $( '.permalink-structure input:radio' ),
  316. $permalinkCustomSelection = $( '#custom_selection' ),
  317. $availableStructureTags = $( '.form-table.permalink-structure .available-structure-tags button' );
  318. // Change permalink structure input when selecting one of the common structures.
  319. $permalinkStructureInputs.on( 'change', function() {
  320. if ( 'custom' === this.value ) {
  321. return;
  322. }
  323. $permalinkStructure.val( this.value );
  324. // Update button states after selection.
  325. $availableStructureTags.each( function() {
  326. changeStructureTagButtonState( $( this ) );
  327. } );
  328. } );
  329. $permalinkStructure.on( 'click input', function() {
  330. $permalinkCustomSelection.prop( 'checked', true );
  331. } );
  332. // Check if the permalink structure input field has had focus at least once.
  333. $permalinkStructure.on( 'focus', function( event ) {
  334. permalinkStructureFocused = true;
  335. $( this ).off( event );
  336. } );
  337. /**
  338. * Enables or disables a structure tag button depending on its usage.
  339. *
  340. * If the structure is already used in the custom permalink structure,
  341. * it will be disabled.
  342. *
  343. * @param {object} button Button jQuery object.
  344. */
  345. function changeStructureTagButtonState( button ) {
  346. if ( -1 !== $permalinkStructure.val().indexOf( button.text().trim() ) ) {
  347. button.attr( 'data-label', button.attr( 'aria-label' ) );
  348. button.attr( 'aria-label', button.attr( 'data-used' ) );
  349. button.attr( 'aria-pressed', true );
  350. button.addClass( 'active' );
  351. } else if ( button.attr( 'data-label' ) ) {
  352. button.attr( 'aria-label', button.attr( 'data-label' ) );
  353. button.attr( 'aria-pressed', false );
  354. button.removeClass( 'active' );
  355. }
  356. }
  357. // Check initial button state.
  358. $availableStructureTags.each( function() {
  359. changeStructureTagButtonState( $( this ) );
  360. } );
  361. // Observe permalink structure field and disable buttons of tags that are already present.
  362. $permalinkStructure.on( 'change', function() {
  363. $availableStructureTags.each( function() {
  364. changeStructureTagButtonState( $( this ) );
  365. } );
  366. } );
  367. $availableStructureTags.on( 'click', function() {
  368. var permalinkStructureValue = $permalinkStructure.val(),
  369. selectionStart = $permalinkStructure[ 0 ].selectionStart,
  370. selectionEnd = $permalinkStructure[ 0 ].selectionEnd,
  371. textToAppend = $( this ).text().trim(),
  372. textToAnnounce = $( this ).attr( 'data-added' ),
  373. newSelectionStart;
  374. // Remove structure tag if already part of the structure.
  375. if ( -1 !== permalinkStructureValue.indexOf( textToAppend ) ) {
  376. permalinkStructureValue = permalinkStructureValue.replace( textToAppend + '/', '' );
  377. $permalinkStructure.val( '/' === permalinkStructureValue ? '' : permalinkStructureValue );
  378. // Announce change to screen readers.
  379. $( '#custom_selection_updated' ).text( textToAnnounce );
  380. // Disable button.
  381. changeStructureTagButtonState( $( this ) );
  382. return;
  383. }
  384. // Input field never had focus, move selection to end of input.
  385. if ( ! permalinkStructureFocused && 0 === selectionStart && 0 === selectionEnd ) {
  386. selectionStart = selectionEnd = permalinkStructureValue.length;
  387. }
  388. $permalinkCustomSelection.prop( 'checked', true );
  389. // Prepend and append slashes if necessary.
  390. if ( '/' !== permalinkStructureValue.substr( 0, selectionStart ).substr( -1 ) ) {
  391. textToAppend = '/' + textToAppend;
  392. }
  393. if ( '/' !== permalinkStructureValue.substr( selectionEnd, 1 ) ) {
  394. textToAppend = textToAppend + '/';
  395. }
  396. // Insert structure tag at the specified position.
  397. $permalinkStructure.val( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend + permalinkStructureValue.substr( selectionEnd ) );
  398. // Announce change to screen readers.
  399. $( '#custom_selection_updated' ).text( textToAnnounce );
  400. // Disable button.
  401. changeStructureTagButtonState( $( this ) );
  402. // If input had focus give it back with cursor right after appended text.
  403. if ( permalinkStructureFocused && $permalinkStructure[0].setSelectionRange ) {
  404. newSelectionStart = ( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend ).length;
  405. $permalinkStructure[0].setSelectionRange( newSelectionStart, newSelectionStart );
  406. $permalinkStructure.focus();
  407. }
  408. } );
  409. $document.ready( function() {
  410. var checks, first, last, checked, sliced, mobileEvent, transitionTimeout, focusedRowActions,
  411. lastClicked = false,
  412. pageInput = $('input.current-page'),
  413. currentPage = pageInput.val(),
  414. isIOS = /iPhone|iPad|iPod/.test( navigator.userAgent ),
  415. isAndroid = navigator.userAgent.indexOf( 'Android' ) !== -1,
  416. isIE8 = $( document.documentElement ).hasClass( 'ie8' ),
  417. $adminMenuWrap = $( '#adminmenuwrap' ),
  418. $wpwrap = $( '#wpwrap' ),
  419. $adminmenu = $( '#adminmenu' ),
  420. $overlay = $( '#wp-responsive-overlay' ),
  421. $toolbar = $( '#wp-toolbar' ),
  422. $toolbarPopups = $toolbar.find( 'a[aria-haspopup="true"]' ),
  423. $sortables = $('.meta-box-sortables'),
  424. wpResponsiveActive = false,
  425. $adminbar = $( '#wpadminbar' ),
  426. lastScrollPosition = 0,
  427. pinnedMenuTop = false,
  428. pinnedMenuBottom = false,
  429. menuTop = 0,
  430. menuState,
  431. menuIsPinned = false,
  432. height = {
  433. window: $window.height(),
  434. wpwrap: $wpwrap.height(),
  435. adminbar: $adminbar.height(),
  436. menu: $adminMenuWrap.height()
  437. },
  438. $headerEnd = $( '.wp-header-end' );
  439. /**
  440. * Makes the fly-out submenu header clickable, when the menu is folded.
  441. *
  442. * @param {Event} e The event object.
  443. *
  444. * @returns {void}
  445. */
  446. $adminmenu.on('click.wp-submenu-head', '.wp-submenu-head', function(e){
  447. $(e.target).parent().siblings('a').get(0).click();
  448. });
  449. /**
  450. * Collapses the admin menu.
  451. *
  452. * @returns {void}
  453. */
  454. $( '#collapse-button' ).on( 'click.collapse-menu', function() {
  455. var viewportWidth = getViewportWidth() || 961;
  456. // reset any compensation for submenus near the bottom of the screen
  457. $('#adminmenu div.wp-submenu').css('margin-top', '');
  458. if ( viewportWidth < 960 ) {
  459. if ( $body.hasClass('auto-fold') ) {
  460. $body.removeClass('auto-fold').removeClass('folded');
  461. setUserSetting('unfold', 1);
  462. setUserSetting('mfold', 'o');
  463. menuState = 'open';
  464. } else {
  465. $body.addClass('auto-fold');
  466. setUserSetting('unfold', 0);
  467. menuState = 'folded';
  468. }
  469. } else {
  470. if ( $body.hasClass('folded') ) {
  471. $body.removeClass('folded');
  472. setUserSetting('mfold', 'o');
  473. menuState = 'open';
  474. } else {
  475. $body.addClass('folded');
  476. setUserSetting('mfold', 'f');
  477. menuState = 'folded';
  478. }
  479. }
  480. $document.trigger( 'wp-collapse-menu', { state: menuState } );
  481. });
  482. /**
  483. * Handles the `aria-haspopup` attribute on the current menu item when it has a submenu.
  484. *
  485. * @since 4.4.0
  486. *
  487. * @returns {void}
  488. */
  489. function currentMenuItemHasPopup() {
  490. var $current = $( 'a.wp-has-current-submenu' );
  491. if ( 'folded' === menuState ) {
  492. // When folded or auto-folded and not responsive view, the current menu item does have a fly-out sub-menu.
  493. $current.attr( 'aria-haspopup', 'true' );
  494. } else {
  495. // When expanded or in responsive view, reset aria-haspopup.
  496. $current.attr( 'aria-haspopup', 'false' );
  497. }
  498. }
  499. $document.on( 'wp-menu-state-set wp-collapse-menu wp-responsive-activate wp-responsive-deactivate', currentMenuItemHasPopup );
  500. /**
  501. * Ensures an admin submenu is within the visual viewport.
  502. *
  503. * @since 4.1.0
  504. *
  505. * @param {jQuery} $menuItem The parent menu item containing the submenu.
  506. *
  507. * @returns {void}
  508. */
  509. function adjustSubmenu( $menuItem ) {
  510. var bottomOffset, pageHeight, adjustment, theFold, menutop, wintop, maxtop,
  511. $submenu = $menuItem.find( '.wp-submenu' );
  512. menutop = $menuItem.offset().top;
  513. wintop = $window.scrollTop();
  514. maxtop = menutop - wintop - 30; // max = make the top of the sub almost touch admin bar
  515. bottomOffset = menutop + $submenu.height() + 1; // Bottom offset of the menu
  516. pageHeight = $wpwrap.height(); // Height of the entire page
  517. adjustment = 60 + bottomOffset - pageHeight;
  518. theFold = $window.height() + wintop - 50; // The fold
  519. if ( theFold < ( bottomOffset - adjustment ) ) {
  520. adjustment = bottomOffset - theFold;
  521. }
  522. if ( adjustment > maxtop ) {
  523. adjustment = maxtop;
  524. }
  525. if ( adjustment > 1 ) {
  526. $submenu.css( 'margin-top', '-' + adjustment + 'px' );
  527. } else {
  528. $submenu.css( 'margin-top', '' );
  529. }
  530. }
  531. if ( 'ontouchstart' in window || /IEMobile\/[1-9]/.test(navigator.userAgent) ) { // touch screen device
  532. // iOS Safari works with touchstart, the rest work with click
  533. mobileEvent = isIOS ? 'touchstart' : 'click';
  534. /**
  535. * Closes any open submenus when touch/click is not on the menu.
  536. *
  537. * @param {Event} e The event object.
  538. *
  539. * @returns {void}
  540. */
  541. $body.on( mobileEvent+'.wp-mobile-hover', function(e) {
  542. if ( $adminmenu.data('wp-responsive') ) {
  543. return;
  544. }
  545. if ( ! $( e.target ).closest( '#adminmenu' ).length ) {
  546. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  547. }
  548. });
  549. /**
  550. * Handles the opening or closing the submenu based on the mobile click|touch event.
  551. *
  552. * @param {Event} event The event object.
  553. *
  554. * @returns {void}
  555. */
  556. $adminmenu.find( 'a.wp-has-submenu' ).on( mobileEvent + '.wp-mobile-hover', function( event ) {
  557. var $menuItem = $(this).parent();
  558. if ( $adminmenu.data( 'wp-responsive' ) ) {
  559. return;
  560. }
  561. // Show the sub instead of following the link if:
  562. // - the submenu is not open
  563. // - the submenu is not shown inline or the menu is not folded
  564. if ( ! $menuItem.hasClass( 'opensub' ) && ( ! $menuItem.hasClass( 'wp-menu-open' ) || $menuItem.width() < 40 ) ) {
  565. event.preventDefault();
  566. adjustSubmenu( $menuItem );
  567. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  568. $menuItem.addClass('opensub');
  569. }
  570. });
  571. }
  572. if ( ! isIOS && ! isAndroid ) {
  573. $adminmenu.find( 'li.wp-has-submenu' ).hoverIntent({
  574. /**
  575. * Opens the submenu when hovered over the menu item for desktops.
  576. *
  577. * @returns {void}
  578. */
  579. over: function() {
  580. var $menuItem = $( this ),
  581. $submenu = $menuItem.find( '.wp-submenu' ),
  582. top = parseInt( $submenu.css( 'top' ), 10 );
  583. if ( isNaN( top ) || top > -5 ) { // the submenu is visible
  584. return;
  585. }
  586. if ( $adminmenu.data( 'wp-responsive' ) ) {
  587. // The menu is in responsive mode, bail
  588. return;
  589. }
  590. adjustSubmenu( $menuItem );
  591. $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' );
  592. $menuItem.addClass( 'opensub' );
  593. },
  594. /**
  595. * Closes the submenu when no longer hovering the menu item.
  596. *
  597. * @returns {void}
  598. */
  599. out: function(){
  600. if ( $adminmenu.data( 'wp-responsive' ) ) {
  601. // The menu is in responsive mode, bail
  602. return;
  603. }
  604. $( this ).removeClass( 'opensub' ).find( '.wp-submenu' ).css( 'margin-top', '' );
  605. },
  606. timeout: 200,
  607. sensitivity: 7,
  608. interval: 90
  609. });
  610. /**
  611. * Opens the submenu on when focused on the menu item.
  612. *
  613. * @param {Event} event The event object.
  614. *
  615. * @returns {void}
  616. */
  617. $adminmenu.on( 'focus.adminmenu', '.wp-submenu a', function( event ) {
  618. if ( $adminmenu.data( 'wp-responsive' ) ) {
  619. // The menu is in responsive mode, bail
  620. return;
  621. }
  622. $( event.target ).closest( 'li.menu-top' ).addClass( 'opensub' );
  623. /**
  624. * Closes the submenu on blur from the menu item.
  625. *
  626. * @param {Event} event The event object.
  627. *
  628. * @returns {void}
  629. */
  630. }).on( 'blur.adminmenu', '.wp-submenu a', function( event ) {
  631. if ( $adminmenu.data( 'wp-responsive' ) ) {
  632. return;
  633. }
  634. $( event.target ).closest( 'li.menu-top' ).removeClass( 'opensub' );
  635. /**
  636. * Adjusts the size for the submenu.
  637. *
  638. * @returns {void}
  639. */
  640. }).find( 'li.wp-has-submenu.wp-not-current-submenu' ).on( 'focusin.adminmenu', function() {
  641. adjustSubmenu( $( this ) );
  642. });
  643. }
  644. /*
  645. * The `.below-h2` class is here just for backward compatibility with plugins
  646. * that are (incorrectly) using it. Do not use. Use `.inline` instead. See #34570.
  647. * If '.wp-header-end' is found, append the notices after it otherwise
  648. * after the first h1 or h2 heading found within the main content.
  649. */
  650. if ( ! $headerEnd.length ) {
  651. $headerEnd = $( '.wrap h1, .wrap h2' ).first();
  652. }
  653. $( 'div.updated, div.error, div.notice' ).not( '.inline, .below-h2' ).insertAfter( $headerEnd );
  654. /**
  655. * Makes notices dismissible.
  656. *
  657. * @since 4.4.0
  658. *
  659. * @returns {void}
  660. */
  661. function makeNoticesDismissible() {
  662. $( '.notice.is-dismissible' ).each( function() {
  663. var $el = $( this ),
  664. $button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
  665. btnText = commonL10n.dismiss || '';
  666. // Ensure plain text
  667. $button.find( '.screen-reader-text' ).text( btnText );
  668. $button.on( 'click.wp-dismiss-notice', function( event ) {
  669. event.preventDefault();
  670. $el.fadeTo( 100, 0, function() {
  671. $el.slideUp( 100, function() {
  672. $el.remove();
  673. });
  674. });
  675. });
  676. $el.append( $button );
  677. });
  678. }
  679. $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 );
  680. // Init screen meta
  681. screenMeta.init();
  682. /**
  683. * Checks a checkbox.
  684. *
  685. * This event needs to be delegated. Ticket #37973.
  686. *
  687. * @returns {boolean} Returns whether a checkbox is checked or not.
  688. */
  689. $body.on( 'click', 'tbody > tr > .check-column :checkbox', function( event ) {
  690. // Shift click to select a range of checkboxes.
  691. if ( 'undefined' == event.shiftKey ) { return true; }
  692. if ( event.shiftKey ) {
  693. if ( !lastClicked ) { return true; }
  694. checks = $( lastClicked ).closest( 'form' ).find( ':checkbox' ).filter( ':visible:enabled' );
  695. first = checks.index( lastClicked );
  696. last = checks.index( this );
  697. checked = $(this).prop('checked');
  698. if ( 0 < first && 0 < last && first != last ) {
  699. sliced = ( last > first ) ? checks.slice( first, last ) : checks.slice( last, first );
  700. sliced.prop( 'checked', function() {
  701. if ( $(this).closest('tr').is(':visible') )
  702. return checked;
  703. return false;
  704. });
  705. }
  706. }
  707. lastClicked = this;
  708. // Toggle the "Select all" checkboxes depending if the other ones are all checked or not.
  709. var unchecked = $(this).closest('tbody').find(':checkbox').filter(':visible:enabled').not(':checked');
  710. /**
  711. * Determines if all checkboxes are checked.
  712. *
  713. * @returns {boolean} Returns true if there are no unchecked checkboxes.
  714. */
  715. $(this).closest('table').children('thead, tfoot').find(':checkbox').prop('checked', function() {
  716. return ( 0 === unchecked.length );
  717. });
  718. return true;
  719. });
  720. /**
  721. * Controls all the toggles on bulk toggle change.
  722. *
  723. * When the bulk checkbox is changed, all the checkboxes in the tables are changed accordingly.
  724. * When the shift-button is pressed while changing the bulk checkbox the checkboxes in the table are inverted.
  725. *
  726. * This event needs to be delegated. Ticket #37973.
  727. *
  728. * @param {Event} event The event object.
  729. *
  730. * @returns {boolean}
  731. */
  732. $body.on( 'click.wp-toggle-checkboxes', 'thead .check-column :checkbox, tfoot .check-column :checkbox', function( event ) {
  733. var $this = $(this),
  734. $table = $this.closest( 'table' ),
  735. controlChecked = $this.prop('checked'),
  736. toggle = event.shiftKey || $this.data('wp-toggle');
  737. $table.children( 'tbody' ).filter(':visible')
  738. .children().children('.check-column').find(':checkbox')
  739. /**
  740. * Updates the checked state on the checkbox in the table.
  741. *
  742. * @returns {boolean} True checks the checkbox, False unchecks the checkbox.
  743. */
  744. .prop('checked', function() {
  745. if ( $(this).is(':hidden,:disabled') ) {
  746. return false;
  747. }
  748. if ( toggle ) {
  749. return ! $(this).prop( 'checked' );
  750. } else if ( controlChecked ) {
  751. return true;
  752. }
  753. return false;
  754. });
  755. $table.children('thead, tfoot').filter(':visible')
  756. .children().children('.check-column').find(':checkbox')
  757. /**
  758. * Syncs the bulk checkboxes on the top and bottom of the table.
  759. *
  760. * @returns {boolean} True checks the checkbox, False unchecks the checkbox.
  761. */
  762. .prop('checked', function() {
  763. if ( toggle ) {
  764. return false;
  765. } else if ( controlChecked ) {
  766. return true;
  767. }
  768. return false;
  769. });
  770. });
  771. /**
  772. * Shows row actions on focus of its parent container element or any other elements contained within.
  773. *
  774. * @returns {void}
  775. */
  776. $( '#wpbody-content' ).on({
  777. focusin: function() {
  778. clearTimeout( transitionTimeout );
  779. focusedRowActions = $( this ).find( '.row-actions' );
  780. // transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
  781. $( '.row-actions' ).not( this ).removeClass( 'visible' );
  782. focusedRowActions.addClass( 'visible' );
  783. },
  784. focusout: function() {
  785. // Tabbing between post title and .row-actions links needs a brief pause, otherwise
  786. // the .row-actions div gets hidden in transit in some browsers (ahem, Firefox).
  787. transitionTimeout = setTimeout( function() {
  788. focusedRowActions.removeClass( 'visible' );
  789. }, 30 );
  790. }
  791. }, '.has-row-actions' );
  792. // Toggle list table rows on small screens
  793. $( 'tbody' ).on( 'click', '.toggle-row', function() {
  794. $( this ).closest( 'tr' ).toggleClass( 'is-expanded' );
  795. });
  796. $('#default-password-nag-no').click( function() {
  797. setUserSetting('default_password_nag', 'hide');
  798. $('div.default-password-nag').hide();
  799. return false;
  800. });
  801. /**
  802. * Handles tab keypresses in theme and plugin editor textareas.
  803. *
  804. * @param {Event} e The event object.
  805. *
  806. * @returns {void}
  807. */
  808. $('#newcontent').bind('keydown.wpevent_InsertTab', function(e) {
  809. var el = e.target, selStart, selEnd, val, scroll, sel;
  810. // After pressing escape key (keyCode: 27), the tab key should tab out of the textarea.
  811. if ( e.keyCode == 27 ) {
  812. // when pressing Escape: Opera 12 and 27 blur form fields, IE 8 clears them
  813. e.preventDefault();
  814. $(el).data('tab-out', true);
  815. return;
  816. }
  817. // Only listen for plain tab key (keyCode: 9) without any modifiers.
  818. if ( e.keyCode != 9 || e.ctrlKey || e.altKey || e.shiftKey )
  819. return;
  820. // After tabbing out, reset it so next time the tab key can be used again.
  821. if ( $(el).data('tab-out') ) {
  822. $(el).data('tab-out', false);
  823. return;
  824. }
  825. selStart = el.selectionStart;
  826. selEnd = el.selectionEnd;
  827. val = el.value;
  828. // If any text is selected, replace the selection with a tab character.
  829. if ( document.selection ) {
  830. el.focus();
  831. sel = document.selection.createRange();
  832. sel.text = '\t';
  833. } else if ( selStart >= 0 ) {
  834. scroll = this.scrollTop;
  835. el.value = val.substring(0, selStart).concat('\t', val.substring(selEnd) );
  836. el.selectionStart = el.selectionEnd = selStart + 1;
  837. this.scrollTop = scroll;
  838. }
  839. // Cancel the regular tab functionality, to prevent losing focus of the textarea.
  840. if ( e.stopPropagation )
  841. e.stopPropagation();
  842. if ( e.preventDefault )
  843. e.preventDefault();
  844. });
  845. // Reset page number variable for new filters/searches but not for bulk actions. See #17685.
  846. if ( pageInput.length ) {
  847. /**
  848. * Handles pagination variable when filtering the list table.
  849. *
  850. * Set the pagination argument to the first page when the post-filter form is submitted.
  851. * This happens when pressing the 'filter' button on the list table page.
  852. *
  853. * The pagination argument should not be touched when the bulk action dropdowns are set to do anything.
  854. *
  855. * The form closest to the pageInput is the post-filter form.
  856. *
  857. * @returns {void}
  858. */
  859. pageInput.closest('form').submit( function() {
  860. /*
  861. * action = bulk action dropdown at the top of the table
  862. * action2 = bulk action dropdow at the bottom of the table
  863. */
  864. if ( $('select[name="action"]').val() == -1 && $('select[name="action2"]').val() == -1 && pageInput.val() == currentPage )
  865. pageInput.val('1');
  866. });
  867. }
  868. /**
  869. * Resets the bulk actions when the search button is clicked.
  870. *
  871. * @returns {void}
  872. */
  873. $('.search-box input[type="search"], .search-box input[type="submit"]').mousedown(function () {
  874. $('select[name^="action"]').val('-1');
  875. });
  876. /**
  877. * Scrolls into view when focus.scroll-into-view is triggered.
  878. *
  879. * @param {Event} e The event object.
  880. *
  881. * @returns {void}
  882. */
  883. $('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){
  884. if ( e.target.scrollIntoView )
  885. e.target.scrollIntoView(false);
  886. });
  887. /**
  888. * Disables the submit upload buttons when no data is entered.
  889. *
  890. * @returns {void}
  891. */
  892. (function(){
  893. var button, input, form = $('form.wp-upload-form');
  894. // Exit when no upload form is found.
  895. if ( ! form.length )
  896. return;
  897. button = form.find('input[type="submit"]');
  898. input = form.find('input[type="file"]');
  899. /**
  900. * Determines if any data is entered in any file upload input.
  901. *
  902. * @since 3.5.0
  903. *
  904. * @returns {void}
  905. */
  906. function toggleUploadButton() {
  907. // When no inputs have a value, disable the upload buttons.
  908. button.prop('disabled', '' === input.map( function() {
  909. return $(this).val();
  910. }).get().join(''));
  911. }
  912. // Update the status initially.
  913. toggleUploadButton();
  914. // Update the status when any file input changes.
  915. input.on('change', toggleUploadButton);
  916. })();
  917. /**
  918. * Pins the menu while distraction-free writing is enabled.
  919. *
  920. * @param {Event} event Event data.
  921. *
  922. * @since 4.1.0
  923. *
  924. * @returns {void}
  925. */
  926. function pinMenu( event ) {
  927. var windowPos = $window.scrollTop(),
  928. resizing = ! event || event.type !== 'scroll';
  929. if ( isIOS || isIE8 || $adminmenu.data( 'wp-responsive' ) ) {
  930. return;
  931. }
  932. /*
  933. * When the menu is higher than the window and smaller than the entire page.
  934. * It should be adjusted to be able to see the entire menu.
  935. *
  936. * Otherwise it can be accessed normally.
  937. */
  938. if ( height.menu + height.adminbar < height.window ||
  939. height.menu + height.adminbar + 20 > height.wpwrap ) {
  940. unpinMenu();
  941. return;
  942. }
  943. menuIsPinned = true;
  944. // If the menu is higher than the window, compensate on scroll.
  945. if ( height.menu + height.adminbar > height.window ) {
  946. // Check for overscrolling, this happens when swiping up at the top of the document in modern browsers.
  947. if ( windowPos < 0 ) {
  948. // Stick the menu to the top.
  949. if ( ! pinnedMenuTop ) {
  950. pinnedMenuTop = true;
  951. pinnedMenuBottom = false;
  952. $adminMenuWrap.css({
  953. position: 'fixed',
  954. top: '',
  955. bottom: ''
  956. });
  957. }
  958. return;
  959. } else if ( windowPos + height.window > $document.height() - 1 ) {
  960. // When overscrolling at the bottom, stick the menu to the bottom.
  961. if ( ! pinnedMenuBottom ) {
  962. pinnedMenuBottom = true;
  963. pinnedMenuTop = false;
  964. $adminMenuWrap.css({
  965. position: 'fixed',
  966. top: '',
  967. bottom: 0
  968. });
  969. }
  970. return;
  971. }
  972. if ( windowPos > lastScrollPosition ) {
  973. // When a down scroll has been detected.
  974. // If it was pinned to the top, unpin and calculate relative scroll.
  975. if ( pinnedMenuTop ) {
  976. pinnedMenuTop = false;
  977. // Calculate new offset position.
  978. menuTop = $adminMenuWrap.offset().top - height.adminbar - ( windowPos - lastScrollPosition );
  979. if ( menuTop + height.menu + height.adminbar < windowPos + height.window ) {
  980. menuTop = windowPos + height.window - height.menu - height.adminbar;
  981. }
  982. $adminMenuWrap.css({
  983. position: 'absolute',
  984. top: menuTop,
  985. bottom: ''
  986. });
  987. } else if ( ! pinnedMenuBottom && $adminMenuWrap.offset().top + height.menu < windowPos + height.window ) {
  988. // Pin it to the bottom.
  989. pinnedMenuBottom = true;
  990. $adminMenuWrap.css({
  991. position: 'fixed',
  992. top: '',
  993. bottom: 0
  994. });
  995. }
  996. } else if ( windowPos < lastScrollPosition ) {
  997. // When a scroll up is detected.
  998. // If it was pinned to the bottom, unpin and calculate relative scroll.
  999. if ( pinnedMenuBottom ) {
  1000. pinnedMenuBottom = false;
  1001. // Calculate new offset position.
  1002. menuTop = $adminMenuWrap.offset().top - height.adminbar + ( lastScrollPosition - windowPos );
  1003. if ( menuTop + height.menu > windowPos + height.window ) {
  1004. menuTop = windowPos;
  1005. }
  1006. $adminMenuWrap.css({
  1007. position: 'absolute',
  1008. top: menuTop,
  1009. bottom: ''
  1010. });
  1011. } else if ( ! pinnedMenuTop && $adminMenuWrap.offset().top >= windowPos + height.adminbar ) {
  1012. // Pin it to the top.
  1013. pinnedMenuTop = true;
  1014. $adminMenuWrap.css({
  1015. position: 'fixed',
  1016. top: '',
  1017. bottom: ''
  1018. });
  1019. }
  1020. } else if ( resizing ) {
  1021. // Window is being resized.
  1022. pinnedMenuTop = pinnedMenuBottom = false;
  1023. // Calculate the new offset.
  1024. menuTop = windowPos + height.window - height.menu - height.adminbar - 1;
  1025. if ( menuTop > 0 ) {
  1026. $adminMenuWrap.css({
  1027. position: 'absolute',
  1028. top: menuTop,
  1029. bottom: ''
  1030. });
  1031. } else {
  1032. unpinMenu();
  1033. }
  1034. }
  1035. }
  1036. lastScrollPosition = windowPos;
  1037. }
  1038. /**
  1039. * Determines the height of certain elements.
  1040. *
  1041. * @since 4.1.0
  1042. *
  1043. * @returns {void}
  1044. */
  1045. function resetHeights() {
  1046. height = {
  1047. window: $window.height(),
  1048. wpwrap: $wpwrap.height(),
  1049. adminbar: $adminbar.height(),
  1050. menu: $adminMenuWrap.height()
  1051. };
  1052. }
  1053. /**
  1054. * Unpins the menu.
  1055. *
  1056. * @since 4.1.0
  1057. *
  1058. * @returns {void}
  1059. */
  1060. function unpinMenu() {
  1061. if ( isIOS || ! menuIsPinned ) {
  1062. return;
  1063. }
  1064. pinnedMenuTop = pinnedMenuBottom = menuIsPinned = false;
  1065. $adminMenuWrap.css({
  1066. position: '',
  1067. top: '',
  1068. bottom: ''
  1069. });
  1070. }
  1071. /**
  1072. * Pins and unpins the menu when applicable.
  1073. *
  1074. * @since 4.1.0
  1075. *
  1076. * @returns {void}
  1077. */
  1078. function setPinMenu() {
  1079. resetHeights();
  1080. if ( $adminmenu.data('wp-responsive') ) {
  1081. $body.removeClass( 'sticky-menu' );
  1082. unpinMenu();
  1083. } else if ( height.menu + height.adminbar > height.window ) {
  1084. pinMenu();
  1085. $body.removeClass( 'sticky-menu' );
  1086. } else {
  1087. $body.addClass( 'sticky-menu' );
  1088. unpinMenu();
  1089. }
  1090. }
  1091. if ( ! isIOS ) {
  1092. $window.on( 'scroll.pin-menu', pinMenu );
  1093. $document.on( 'tinymce-editor-init.pin-menu', function( event, editor ) {
  1094. editor.on( 'wp-autoresize', resetHeights );
  1095. });
  1096. }
  1097. /**
  1098. * Changes the sortables and responsiveness of metaboxes.
  1099. *
  1100. * @since 3.8.0
  1101. *
  1102. *@returns {void}
  1103. */
  1104. window.wpResponsive = {
  1105. /**
  1106. * Initializes the wpResponsive object.
  1107. *
  1108. * @since 3.8.0
  1109. *
  1110. * @returns {void}
  1111. */
  1112. init: function() {
  1113. var self = this;
  1114. this.maybeDisableSortables = this.maybeDisableSortables.bind( this );
  1115. // Modify functionality based on custom activate/deactivate event
  1116. $document.on( 'wp-responsive-activate.wp-responsive', function() {
  1117. self.activate();
  1118. }).on( 'wp-responsive-deactivate.wp-responsive', function() {
  1119. self.deactivate();
  1120. });
  1121. $( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' );
  1122. // Toggle sidebar when toggle is clicked.
  1123. $( '#wp-admin-bar-menu-toggle' ).on( 'click.wp-responsive', function( event ) {
  1124. event.preventDefault();
  1125. // close any open toolbar submenus.
  1126. $adminbar.find( '.hover' ).removeClass( 'hover' );
  1127. $wpwrap.toggleClass( 'wp-responsive-open' );
  1128. if ( $wpwrap.hasClass( 'wp-responsive-open' ) ) {
  1129. $(this).find('a').attr( 'aria-expanded', 'true' );
  1130. $( '#adminmenu a:first' ).focus();
  1131. } else {
  1132. $(this).find('a').attr( 'aria-expanded', 'false' );
  1133. }
  1134. } );
  1135. // Add menu events.
  1136. $adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) {
  1137. if ( ! $adminmenu.data('wp-responsive') ) {
  1138. return;
  1139. }
  1140. $( this ).parent( 'li' ).toggleClass( 'selected' );
  1141. event.preventDefault();
  1142. });
  1143. self.trigger();
  1144. $document.on( 'wp-window-resized.wp-responsive', $.proxy( this.trigger, this ) );
  1145. // This needs to run later as UI Sortable may be initialized later on $(document).ready().
  1146. $window.on( 'load.wp-responsive', this.maybeDisableSortables );
  1147. $document.on( 'postbox-toggled', this.maybeDisableSortables );
  1148. // When the screen columns are changed, potentially disable sortables.
  1149. $( '#screen-options-wrap input' ).on( 'click', this.maybeDisableSortables );
  1150. },
  1151. /**
  1152. * Disable sortables if there is only one metabox, or the screen is in one column mode. Otherwise, enable sortables.
  1153. *
  1154. * @since 5.3.0
  1155. *
  1156. * @returns {void}
  1157. */
  1158. maybeDisableSortables: function() {
  1159. var width = navigator.userAgent.indexOf('AppleWebKit/') > -1 ? $window.width() : window.innerWidth;
  1160. if (
  1161. ( width <= 782 ) ||
  1162. ( 1 >= $sortables.find( '.ui-sortable-handle:visible' ).length && jQuery( '.columns-prefs-1 input' ).prop( 'checked' ) )
  1163. ) {
  1164. this.disableSortables();
  1165. } else {
  1166. this.enableSortables();
  1167. }
  1168. },
  1169. /**
  1170. * Changes properties of body and admin menu.
  1171. *
  1172. * Pins and unpins the menu and adds the auto-fold class to the body.
  1173. * Makes the admin menu responsive and disables the metabox sortables.
  1174. *
  1175. * @since 3.8.0
  1176. *
  1177. * @returns {void}
  1178. */
  1179. activate: function() {
  1180. setPinMenu();
  1181. if ( ! $body.hasClass( 'auto-fold' ) ) {
  1182. $body.addClass( 'auto-fold' );
  1183. }
  1184. $adminmenu.data( 'wp-responsive', 1 );
  1185. this.disableSortables();
  1186. },
  1187. /**
  1188. * Changes properties of admin menu and enables metabox sortables.
  1189. *
  1190. * Pin and unpin the menu.
  1191. * Removes the responsiveness of the admin menu and enables the metabox sortables.
  1192. *
  1193. * @since 3.8.0
  1194. *
  1195. * @returns {void}
  1196. */
  1197. deactivate: function() {
  1198. setPinMenu();
  1199. $adminmenu.removeData('wp-responsive');
  1200. this.maybeDisableSortables();
  1201. },
  1202. /**
  1203. * Sets the responsiveness and enables the overlay based on the viewport width.
  1204. *
  1205. * @since 3.8.0
  1206. *
  1207. * @returns {void}
  1208. */
  1209. trigger: function() {
  1210. var viewportWidth = getViewportWidth();
  1211. // Exclude IE < 9, it doesn't support @media CSS rules.
  1212. if ( ! viewportWidth ) {
  1213. return;
  1214. }
  1215. if ( viewportWidth <= 782 ) {
  1216. if ( ! wpResponsiveActive ) {
  1217. $document.trigger( 'wp-responsive-activate' );
  1218. wpResponsiveActive = true;
  1219. }
  1220. } else {
  1221. if ( wpResponsiveActive ) {
  1222. $document.trigger( 'wp-responsive-deactivate' );
  1223. wpResponsiveActive = false;
  1224. }
  1225. }
  1226. if ( viewportWidth <= 480 ) {
  1227. this.enableOverlay();
  1228. } else {
  1229. this.disableOverlay();
  1230. }
  1231. this.maybeDisableSortables();
  1232. },
  1233. /**
  1234. * Inserts a responsive overlay and toggles the window.
  1235. *
  1236. * @since 3.8.0
  1237. *
  1238. * @returns {void}
  1239. */
  1240. enableOverlay: function() {
  1241. if ( $overlay.length === 0 ) {
  1242. $overlay = $( '<div id="wp-responsive-overlay"></div>' )
  1243. .insertAfter( '#wpcontent' )
  1244. .hide()
  1245. .on( 'click.wp-responsive', function() {
  1246. $toolbar.find( '.menupop.hover' ).removeClass( 'hover' );
  1247. $( this ).hide();
  1248. });
  1249. }
  1250. $toolbarPopups.on( 'click.wp-responsive', function() {
  1251. $overlay.show();
  1252. });
  1253. },
  1254. /**
  1255. * Disables the responsive overlay and removes the overlay.
  1256. *
  1257. * @since 3.8.0
  1258. *
  1259. * @returns {void}
  1260. */
  1261. disableOverlay: function() {
  1262. $toolbarPopups.off( 'click.wp-responsive' );
  1263. $overlay.hide();
  1264. },
  1265. /**
  1266. * Disables sortables.
  1267. *
  1268. * @since 3.8.0
  1269. *
  1270. * @returns {void}
  1271. */
  1272. disableSortables: function() {
  1273. if ( $sortables.length ) {
  1274. try {
  1275. $sortables.sortable( 'disable' );
  1276. $sortables.find( '.ui-sortable-handle' ).addClass( 'is-non-sortable' );
  1277. } catch ( e ) {}
  1278. }
  1279. },
  1280. /**
  1281. * Enables sortables.
  1282. *
  1283. * @since 3.8.0
  1284. *
  1285. * @returns {void}
  1286. */
  1287. enableSortables: function() {
  1288. if ( $sortables.length ) {
  1289. try {
  1290. $sortables.sortable( 'enable' );
  1291. $sortables.find( '.ui-sortable-handle' ).removeClass( 'is-non-sortable' );
  1292. } catch ( e ) {}
  1293. }
  1294. }
  1295. };
  1296. /**
  1297. * Add an ARIA role `button` to elements that behave like UI controls when JavaScript is on.
  1298. *
  1299. * @since 4.5.0
  1300. *
  1301. * @returns {void}
  1302. */
  1303. function aria_button_if_js() {
  1304. $( '.aria-button-if-js' ).attr( 'role', 'button' );
  1305. }
  1306. $( document ).ajaxComplete( function() {
  1307. aria_button_if_js();
  1308. });
  1309. /**
  1310. * Get the viewport width.
  1311. *
  1312. * @since 4.7.0
  1313. *
  1314. * @returns {number|boolean} The current viewport width or false if the
  1315. * browser doesn't support innerWidth (IE < 9).
  1316. */
  1317. function getViewportWidth() {
  1318. var viewportWidth = false;
  1319. if ( window.innerWidth ) {
  1320. // On phones, window.innerWidth is affected by zooming.
  1321. viewportWidth = Math.max( window.innerWidth, document.documentElement.clientWidth );
  1322. }
  1323. return viewportWidth;
  1324. }
  1325. /**
  1326. * Sets the admin menu collapsed/expanded state.
  1327. *
  1328. * Sets the global variable `menuState` and triggers a custom event passing
  1329. * the current menu state.
  1330. *
  1331. * @since 4.7.0
  1332. *
  1333. * @returns {void}
  1334. */
  1335. function setMenuState() {
  1336. var viewportWidth = getViewportWidth() || 961;
  1337. if ( viewportWidth <= 782 ) {
  1338. menuState = 'responsive';
  1339. } else if ( $body.hasClass( 'folded' ) || ( $body.hasClass( 'auto-fold' ) && viewportWidth <= 960 && viewportWidth > 782 ) ) {
  1340. menuState = 'folded';
  1341. } else {
  1342. menuState = 'open';
  1343. }
  1344. $document.trigger( 'wp-menu-state-set', { state: menuState } );
  1345. }
  1346. // Set the menu state when the window gets resized.
  1347. $document.on( 'wp-window-resized.set-menu-state', setMenuState );
  1348. /**
  1349. * Sets ARIA attributes on the collapse/expand menu button.
  1350. *
  1351. * When the admin menu is open or folded, updates the `aria-expanded` and
  1352. * `aria-label` attributes of the button to give feedback to assistive
  1353. * technologies. In the responsive view, the button is always hidden.
  1354. *
  1355. * @since 4.7.0
  1356. *
  1357. * @returns {void}
  1358. */
  1359. $document.on( 'wp-menu-state-set wp-collapse-menu', function( event, eventData ) {
  1360. var $collapseButton = $( '#collapse-button' ),
  1361. ariaExpanded = 'true',
  1362. ariaLabelText = commonL10n.collapseMenu;
  1363. if ( 'folded' === eventData.state ) {
  1364. ariaExpanded = 'false';
  1365. ariaLabelText = commonL10n.expandMenu;
  1366. }
  1367. $collapseButton.attr({
  1368. 'aria-expanded': ariaExpanded,
  1369. 'aria-label': ariaLabelText
  1370. });
  1371. });
  1372. window.wpResponsive.init();
  1373. setPinMenu();
  1374. setMenuState();
  1375. currentMenuItemHasPopup();
  1376. makeNoticesDismissible();
  1377. aria_button_if_js();
  1378. $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 );
  1379. // Set initial focus on a specific element.
  1380. $( '.wp-initial-focus' ).focus();
  1381. // Toggle update details on update-core.php.
  1382. $body.on( 'click', '.js-update-details-toggle', function() {
  1383. var $updateNotice = $( this ).closest( '.js-update-details' ),
  1384. $progressDiv = $( '#' + $updateNotice.data( 'update-details' ) );
  1385. /*
  1386. * When clicking on "Show details" move the progress div below the update
  1387. * notice. Make sure it gets moved just the first time.
  1388. */
  1389. if ( ! $progressDiv.hasClass( 'update-details-moved' ) ) {
  1390. $progressDiv.insertAfter( $updateNotice ).addClass( 'update-details-moved' );
  1391. }
  1392. // Toggle the progress div visibility.
  1393. $progressDiv.toggle();
  1394. // Toggle the Show Details button expanded state.
  1395. $( this ).attr( 'aria-expanded', $progressDiv.is( ':visible' ) );
  1396. });
  1397. });
  1398. // Fire a custom jQuery event at the end of window resize.
  1399. ( function() {
  1400. var timeout;
  1401. /**
  1402. * Triggers the WP window-resize event.
  1403. *
  1404. * @since 3.8.0
  1405. *
  1406. * @returns {void}
  1407. */
  1408. function triggerEvent() {
  1409. $document.trigger( 'wp-window-resized' );
  1410. }
  1411. /**
  1412. * Fires the trigger event again after 200 ms.
  1413. *
  1414. * @since 3.8.0
  1415. *
  1416. * @returns {void}
  1417. */
  1418. function fireOnce() {
  1419. window.clearTimeout( timeout );
  1420. timeout = window.setTimeout( triggerEvent, 200 );
  1421. }
  1422. $window.on( 'resize.wp-fire-once', fireOnce );
  1423. }());
  1424. // Make Windows 8 devices play along nicely.
  1425. (function(){
  1426. if ( '-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/) ) {
  1427. var msViewportStyle = document.createElement( 'style' );
  1428. msViewportStyle.appendChild(
  1429. document.createTextNode( '@-ms-viewport{width:auto!important}' )
  1430. );
  1431. document.getElementsByTagName( 'head' )[0].appendChild( msViewportStyle );
  1432. }
  1433. })();
  1434. }( jQuery, window ));