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